Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion lib/llm/gemini.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,35 @@ export class GeminiProvider extends LLMProvider {
body: JSON.stringify({
systemInstruction: { parts: [{ text: systemPrompt }] },
contents: [{ parts: [{ text: userMessage }] }],
// --- TRAVA DE SEGURANÇA DESATIVADA PARA OSINT ---
safetySettings: [
{ category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_NONE" },
{ category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_NONE" },
{ category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_NONE" },
{ category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_NONE" }
],
// ------------------------------------------------
generationConfig: {
maxOutputTokens: opts.maxTokens || 4096,
// Trava estrutural para garantir o JSON nativo
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new inline comment is in Portuguese while the rest of the LLM provider files use English comments, which makes the file inconsistent to maintain for the broader team. Please translate it to English (and keep terminology consistent with other providers).

Suggested change
// Trava estrutural para garantir o JSON nativo
// Structural safeguard to enforce native JSON output

Copilot uses AI. Check for mistakes.
responseMimeType: "application/json",
responseSchema: {
type: "ARRAY",
Comment on lines 32 to +37
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

responseMimeType/responseSchema are being applied unconditionally for every complete() call. This makes the Gemini provider incompatible with other call sites that expect a different JSON shape (e.g. alert evaluation in lib/alerts/telegram.mjs expects a JSON object, not an array), and will likely cause parsing failures there. Consider making structured-output settings opt-in via opts (e.g. opts.responseSchema / opts.responseMimeType) or providing a separate method/helper specifically for the trade-ideas schema, leaving the default complete() behavior schema-free.

Copilot uses AI. Check for mistakes.
items: {
type: "OBJECT",
properties: {
title: { type: "STRING", description: "Short title (max 10 words)" },
type: { type: "STRING", enum: ["LONG", "SHORT", "HEDGE", "WATCH", "AVOID"] },
ticker: { type: "STRING", description: "Primary instrument" },
confidence: { type: "STRING", enum: ["HIGH", "MEDIUM", "LOW"] },
rationale: { type: "STRING", description: "2-3 sentence explanation citing specific data" },
risk: { type: "STRING", description: "Key risk factor" },
horizon: { type: "STRING", description: "Intraday, Days, Weeks, or Months" },
signals: { type: "ARRAY", items: { type: "STRING" } }
},
required: ["title", "type", "ticker", "confidence", "rationale", "risk", "horizon", "signals"]
}
}
},
Comment on lines 32 to 53
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are unit tests for most other providers (e.g. test/llm-mistral.test.mjs) that assert the request body shape. Given this change alters the request payload and behavior significantly, please add a GeminiProvider unit test that verifies (a) the request includes responseMimeType/responseSchema when enabled and (b) the default call path doesn’t force an ideas-specific schema (once made opt-in), to prevent regressions.

Copilot uses AI. Check for mistakes.
}),
signal: AbortSignal.timeout(opts.timeout || 60000),
Expand All @@ -34,8 +61,21 @@ export class GeminiProvider extends LLMProvider {
}

const data = await res.json();

// Fallback de segurança caso a API bloqueie em um nível superior (independente do BLOCK_NONE)
if (data.candidates?.[0]?.finishReason === 'SAFETY') {
console.warn('[LLM] Alerta de Censura: O modelo recusou a análise do feed por violação de segurança.');
}

const text = data.candidates?.[0]?.content?.parts?.[0]?.text || '';

// --- INJEÇÃO DE DEBUG ---
if (!text || text.length < 10) {
console.log("\n[DEBUG CRÍTICO] A IA não retornou texto válido. Resposta bruta da API:");
console.log(JSON.stringify(data, null, 2));
}
// ------------------------

return {
text,
usage: {
Expand All @@ -45,4 +85,4 @@ export class GeminiProvider extends LLMProvider {
model: this.model,
};
}
}
}