diff --git a/server.js b/server.js index 8d73fde..e9913d4 100644 --- a/server.js +++ b/server.js @@ -28,34 +28,113 @@ async function callMinimax({ messages, temperature = 0.5, max_tokens = 1500, sys ? [{ role: 'system', content: system }, ...messages] : messages; - const res = await fetch(`${baseUrl}/anthropic/v1/messages`, { - method: 'POST', - headers: { - 'X-Api-Key': process.env.MINIMAX_API_KEY, - 'Content-Type': 'application/json', - 'anthropic-version': '2023-06-01' - }, - body: JSON.stringify({ - model: chosenModel, - max_tokens, - temperature, - messages: finalMessages - }) - }); + // Tentativa 1: MiniMax M3 com timeout de 45s + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 45000); - if (!res.ok) { - const txt = await res.text(); - throw new Error(`MiniMax respondeu ${res.status}: ${txt}`); + const res = await fetch(`${baseUrl}/anthropic/v1/messages`, { + method: 'POST', + headers: { + 'X-Api-Key': process.env.MINIMAX_API_KEY, + 'Content-Type': 'application/json', + 'anthropic-version': '2023-06-01' + }, + body: JSON.stringify({ + model: chosenModel, + max_tokens, + temperature, + messages: finalMessages + }), + signal: controller.signal + }); + + clearTimeout(timeoutId); + + if (res.ok) { + const data = await res.json(); + const text = (data.content || []) + .filter(b => b.type === 'text') + .map(b => b.text) + .join('') + .trim(); + if (text) return { text, raw: data, provider: 'minimax' }; + } else { + const txt = await res.text(); + console.warn(`[AI Text] MiniMax respondeu ${res.status}: ${txt}. Acionando fallback...`); + } + } catch (err) { + console.warn('[AI Text] Falha/Timeout no MiniMax (' + err.message + '). Acionando fallback...'); } - const data = await res.json(); - // Extrai só os blocos de texto (ignora thinking blocks) - const text = (data.content || []) - .filter(b => b.type === 'text') - .map(b => b.text) - .join('') - .trim(); - return { text, raw: data }; + // Fallback 1: Groq (Llama 3.3 70b / Llama 3.1 8b) + if (process.env.GROQ_API_KEY) { + try { + console.log('[AI Text Fallback] Acionando Groq...'); + const groqMessages = system ? [{ role: 'system', content: system }, ...messages] : messages; + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 30000); + + const res = await fetch('https://api.groq.com/openai/v1/chat/completions', { + method: 'POST', + headers: { + 'Authorization': `Bearer ${process.env.GROQ_API_KEY}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: 'llama-3.3-70b-versatile', + messages: groqMessages, + temperature, + max_tokens + }), + signal: controller.signal + }); + + clearTimeout(timeoutId); + + if (res.ok) { + const data = await res.json(); + const text = data.choices?.[0]?.message?.content?.trim(); + if (text) return { text, raw: data, provider: 'groq' }; + } + } catch (groqErr) { + console.warn('[AI Text Fallback] Falha no Groq:', groqErr.message); + } + } + + // Fallback 2: Google AI Gemini + if (process.env.GOOGLE_AI_API_KEY) { + try { + console.log('[AI Text Fallback] Acionando Google AI Gemini...'); + const geminiModel = process.env.GOOGLE_AI_MODEL || 'gemini-2.5-flash'; + const promptText = (system ? `System: ${system}\n\n` : '') + messages.map(m => `${m.role}: ${m.content}`).join('\n'); + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 30000); + + const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${geminiModel}:generateContent?key=${process.env.GOOGLE_AI_API_KEY}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + contents: [{ parts: [{ text: promptText }] }], + generationConfig: { temperature, maxOutputTokens: max_tokens } + }), + signal: controller.signal + }); + + clearTimeout(timeoutId); + + if (res.ok) { + const data = await res.json(); + const text = data.candidates?.[0]?.content?.parts?.[0]?.text?.trim(); + if (text) return { text, raw: data, provider: 'gemini' }; + } + } catch (gemErr) { + console.warn('[AI Text Fallback] Falha no Gemini:', gemErr.message); + } + } + + throw new Error('Todas as IAs de geração de texto estão indisponíveis no momento. Por favor, tente novamente.'); } app.use(express.json({ limit: '50mb' }));