// ai_client.js const OLLAMA_URL = process.env.OLLAMA_URL || "http://localhost:11434/api/generate"; const MODEL = process.env.MODEL || "openchat"; const TIMEOUT = parseInt(process.env.TIMEOUT || "120000"); // 2 minutos por defecto // Rate limiting let lastCallTime = 0; const MIN_CALL_INTERVAL = 2000; // 2 segundos // Caché básica const cache = new Map(); async function callLLM(prompt, maxTokens = 1000) { // Rate limiting const now = Date.now(); const timeSinceLastCall = now - lastCallTime; if (timeSinceLastCall < MIN_CALL_INTERVAL) { const waitTime = MIN_CALL_INTERVAL - timeSinceLastCall; console.log(`⏳ Esperando ${waitTime}ms por rate limit...`); await new Promise(resolve => setTimeout(resolve, waitTime)); } // Verificar caché const cacheKey = `${prompt}_${maxTokens}`; if (cache.has(cacheKey)) { console.log("✅ Respuesta obtenida de caché"); return cache.get(cacheKey); } try { // Crear un AbortController manual para timeout const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), TIMEOUT); const response = await fetch(OLLAMA_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ model: MODEL, prompt: prompt, stream: false, options: { num_predict: maxTokens, temperature: 0.7 } }), signal: controller.signal }); clearTimeout(timeoutId); lastCallTime = Date.now(); if (response.status === 429) { console.error("⚠️ Error 429: Demasiadas peticiones. Esperando 5 segundos..."); await new Promise(resolve => setTimeout(resolve, 5000)); return callLLM(prompt, maxTokens); // Reintentar } if (!response.ok) { const errorText = await response.text(); throw new Error(`Error Ollama: ${response.status} - ${errorText}`); } const data = await response.json(); if (!data.response) { throw new Error("La respuesta de Ollama no contiene el campo 'response'"); } // Guardar en caché cache.set(cacheKey, data.response); return data.response; } catch (error) { if (error.name === 'AbortError') { throw new Error(`⏱️ Timeout: La petición tardó más de ${TIMEOUT/1000} segundos. Intenta con un prompt más corto o aumenta TIMEOUT en .env`); } if (error.code === 'ECONNREFUSED') { throw new Error("🔌 No se puede conectar a Ollama. Asegúrate de que está ejecutándose con 'ollama serve'"); } throw error; } } module.exports = { callLLM };