121 lines
5.2 KiB
JavaScript
121 lines
5.2 KiB
JavaScript
// gemini.js – integración Grounding con Google Search + logs de verificación
|
||
import genai from '@google/genai';
|
||
import { config } from './config.js';
|
||
import { log } from './logger.js';
|
||
|
||
const { GoogleGenAI, createUserContent, createPartFromUri } = genai;
|
||
|
||
/*───────────────────────────────────────────────────────────────*/
|
||
/* 📜 System prompt */
|
||
/*───────────────────────────────────────────────────────────────*/
|
||
const SYSTEM_PROMPT = `
|
||
#########
|
||
Tu nombre es nucleo
|
||
##########
|
||
RESPUESTAS CORTAS y CONCISAS
|
||
sos la inteligencia artificial de Servidores de G.O.D (nuestro J.A.R.V.I.S.).
|
||
tenés derecho a sostener opiniones firmes con lógica, sin corrección política excesiva.
|
||
Respuestas casuales breves; si te extendés, hacelo en un solo mensaje.
|
||
Podés continuar conversaciones, usar chistes, analogías, etc.
|
||
`.trim();
|
||
|
||
/*───────────────────────────────────────────────────────────────*/
|
||
/* 🧠 Inicializar Gemini */
|
||
/*───────────────────────────────────────────────────────────────*/
|
||
let ai = null;
|
||
function initGemini() {
|
||
if (!config.GEMINI_KEY) throw new Error('🔑 GEMINI_API_KEY no configurada');
|
||
if (!ai) {
|
||
ai = new GoogleGenAI({ apiKey: config.GEMINI_KEY });
|
||
log('info', `🧠 Gemini SDK inicializado (${config.GEMINI_MODEL_ID})`);
|
||
}
|
||
return ai;
|
||
}
|
||
|
||
/*───────────────────────────────────────────────────────────────*/
|
||
/* 🔍 Construir tools de búsqueda */
|
||
/*───────────────────────────────────────────────────────────────*/
|
||
function buildSearchTools() {
|
||
const model = config.GEMINI_MODEL_ID;
|
||
// Los objetos literales cumplen con el esquema Tool del SDK.
|
||
if (/^gemini-2\./.test(model) || /^gemini-2\.5/.test(model)) {
|
||
return [{ google_search: {} }]; // Search‑as‑a‑tool
|
||
}
|
||
if (/^gemini-1\.5/.test(model)) {
|
||
return [{
|
||
google_search_retrieval: {
|
||
dynamic_retrieval_config: {
|
||
mode: 'MODE_DYNAMIC',
|
||
dynamic_threshold: 0.3,
|
||
},
|
||
},
|
||
}];
|
||
}
|
||
return [];
|
||
}
|
||
|
||
/*───────────────────────────────────────────────────────────────*/
|
||
/* 🚀 askGemini */
|
||
/*───────────────────────────────────────────────────────────────*/
|
||
export async function askGemini(historial, files = {}) {
|
||
try {
|
||
const client = initGemini();
|
||
|
||
// 1️⃣ Construir "contents"
|
||
let contents;
|
||
if (typeof historial === 'string') {
|
||
contents = historial;
|
||
} else if (Array.isArray(historial)) {
|
||
const parts = [];
|
||
for (const m of historial) {
|
||
if (m.type === 'document') continue;
|
||
if (m.type === 'chat') {
|
||
parts.push(`${m.senderName}: ${m.text} -- ${m.date}`);
|
||
continue;
|
||
}
|
||
const up = files[m.msgId?.toLowerCase?.()];
|
||
if (up?.uri) {
|
||
parts.push(
|
||
createPartFromUri(up.uri, up.mimeType),
|
||
`archivo ${m.type} de ${m.senderName}: ${m.caption || m.text} -- ${m.date}`
|
||
);
|
||
}
|
||
}
|
||
contents = createUserContent(parts);
|
||
} else {
|
||
throw new Error('Formato de historial no soportado');
|
||
}
|
||
|
||
// 2️⃣ Herramientas
|
||
const tools = buildSearchTools();
|
||
|
||
// 3️⃣ Llamar al modelo
|
||
const response = await client.models.generateContent({
|
||
model: config.GEMINI_MODEL_ID,
|
||
contents,
|
||
config: {
|
||
systemInstruction: SYSTEM_PROMPT,
|
||
maxOutputTokens: 4096,
|
||
temperature: 0.3, // menor → mayor factualidad
|
||
tools,
|
||
response_modalities: ['TEXT'],
|
||
},
|
||
});
|
||
|
||
// 4️⃣ Log de grounding
|
||
const candidate = response?.candidates?.[0];
|
||
if (candidate?.groundingMetadata) {
|
||
log('info', '🔗 GroundingMetadata presente:', JSON.stringify(candidate.groundingMetadata.webSearchQueries));
|
||
} else {
|
||
log('warn', 'ℹ️ Sin groundingMetadata en la respuesta');
|
||
}
|
||
|
||
if (!candidate) return '⚠️ Sin candidato.';
|
||
return candidate.content.parts.map(p => p.text).join('').trim() || '⚠️ Respuesta vacía.';
|
||
} catch (e) {
|
||
log('error', 'Gemini falló:', e.message);
|
||
if (e.response?.status === 429) return '🚦 Límite alcanzado. Probá más tarde.';
|
||
return '⚠️ No se pudo obtener respuesta de Gemini.';
|
||
}
|
||
}
|