// /respuesta/respuestaBrave.js import fs from 'fs/promises'; import { log } from '../logger.js'; import { sendText, fetchChatMessages, setTypingStatus } from '../whatsapp.js'; import { processMessage } from '../utils/processMessage.js'; import { saveMedia } from '../utils/saveMedia.js'; import { GoogleGenAI } from '@google/genai'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; function sanitizeSchema(obj) { if (Array.isArray(obj)) return obj.map(sanitizeSchema); if (obj && typeof obj === 'object') { const out = {}; for (const [k, v] of Object.entries(obj)) { if (k === 'additionalProperties' || k === '$schema') continue; out[k] = sanitizeSchema(v); } return out; } return obj; } const cleanForGemini = ({ media, reactions, preview, ...rest }) => rest; export async function respuestaBrave(msg) { const text = msg.text || ''; setTypingStatus(msg.chatId, true); log('info', '🧠 Generando respuesta para @nucleo (Brave)…'); try { const allraw = await fetchChatMessages(msg.chatId); const allMsgs = allraw.map(processMessage); const context = allMsgs.map(cleanForGemini); let json = JSON.stringify(context); while (json.length > 100_000 && context.length) { context.shift(); json = JSON.stringify(context); } const chatName = context.length > 0 ? context[0]?.chatName : 'chat'; const prompt = [ `Eres un asistente con acceso a Brave Search mediante herramientas externas.`, `Pregunta del usuario: ${text}`, ...context ]; const medias = allraw.filter(m => m.type !== 'chat'); const settled = await Promise.allSettled(medias.map(saveMedia)); const MAX = 20 * 1024 * 1024; let total = 0; const files = {}; for (const r of settled) { if (r.status !== 'fulfilled' || !r.value) continue; const entries = Object.entries(r.value); if (entries.length === 0) continue; const [msgId, obj] = entries[0]; let size = Number(obj.sizeBytes || 0); if (!size && obj.filePath) { try { size = (await fs.stat(obj.filePath)).size; } catch { size = 0; } } if (total + size > MAX && total > 0) break; if (size > MAX) continue; total += size; files[msgId] = { uri: obj.uri || obj.filePath, mimeType: obj.mimeType }; } // === MCP Brave Search === const client = new Client({ name: 'brave-agent', version: '1.0.0' }); const serverParams = new StdioClientTransport({ command: 'uvx', args: ['mcp-server-fetch'] }); await client.connect(serverParams); const mcp = await client.listTools(); const tools = mcp.tools.map(t => ({ name: t.name, description: t.description, parameters: sanitizeSchema({ type: 'object', ...t.inputSchema }) })); const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY }); const response = await ai.models.generateContent({ model: 'gemini-2.5-flash-preview-04-17', contents: prompt, config: { tools: [{ functionDeclarations: tools }], temperature: 0 } }); let respuesta; if (response.functionCalls && response.functionCalls.length > 0) { const call = response.functionCalls[0]; const result = await client.callTool({ name: call.name, arguments: call.args }); await client.close(); respuesta = result.content?.[0]?.text ?? JSON.stringify(result); } else { await client.close(); respuesta = response.text; } await sendText(msg.chatId, respuesta); log('info', '🧠 Respuesta enviada'); } catch (error) { log('error', 'Error en respuestaBrave:', error); await sendText(msg.chatId, 'Hubo un error al procesar tu solicitud.'); } finally { setTypingStatus(msg.chatId, false); } }