Blindagem global de IA de texto com fallback triplo (MiniMax -> Groq -> Gemini) para todo o aplicativo
This commit is contained in:
@@ -28,6 +28,11 @@ async function callMinimax({ messages, temperature = 0.5, max_tokens = 1500, sys
|
|||||||
? [{ role: 'system', content: system }, ...messages]
|
? [{ role: 'system', content: system }, ...messages]
|
||||||
: messages;
|
: messages;
|
||||||
|
|
||||||
|
// Tentativa 1: MiniMax M3 com timeout de 45s
|
||||||
|
try {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => controller.abort(), 45000);
|
||||||
|
|
||||||
const res = await fetch(`${baseUrl}/anthropic/v1/messages`, {
|
const res = await fetch(`${baseUrl}/anthropic/v1/messages`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -40,22 +45,96 @@ async function callMinimax({ messages, temperature = 0.5, max_tokens = 1500, sys
|
|||||||
max_tokens,
|
max_tokens,
|
||||||
temperature,
|
temperature,
|
||||||
messages: finalMessages
|
messages: finalMessages
|
||||||
})
|
}),
|
||||||
|
signal: controller.signal
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
clearTimeout(timeoutId);
|
||||||
const txt = await res.text();
|
|
||||||
throw new Error(`MiniMax respondeu ${res.status}: ${txt}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
// Extrai só os blocos de texto (ignora thinking blocks)
|
|
||||||
const text = (data.content || [])
|
const text = (data.content || [])
|
||||||
.filter(b => b.type === 'text')
|
.filter(b => b.type === 'text')
|
||||||
.map(b => b.text)
|
.map(b => b.text)
|
||||||
.join('')
|
.join('')
|
||||||
.trim();
|
.trim();
|
||||||
return { text, raw: data };
|
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...');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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' }));
|
app.use(express.json({ limit: '50mb' }));
|
||||||
|
|||||||
Reference in New Issue
Block a user