feat: implementado fallback automatico do Groq para classificacao e chat de texto

This commit is contained in:
2026-05-26 17:17:55 +00:00
parent 0f981a0b83
commit 8cc773a75f
2 changed files with 101 additions and 38 deletions
+2
View File
@@ -5,3 +5,5 @@ OPENROUTER_MODEL=openai/gpt-4.1-nano
SESSION_SECRET=camila-secret-key-321123
MINIMAX_API_KEY=sk-cp-iWzSsdzjVy0tT5dnvv_sGrAlv3Nw7kkrZ8hPK0KjbNeSRQxIY1O6AhkQAq_yrYV1XPJY1TH6ZQeOlJZd-Zi_elkJhef9PcTSzn2m9bcKIxezarM19BREcVM
MINIMAX_API_BASE=https://api.minimax.io/v1
GROQ_API_KEY=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
GROQ_MODEL=llama-3.3-70b-versatile
+71 -10
View File
@@ -274,7 +274,11 @@ app.post('/api/chat', requireAuth, async (req, res) => {
try {
const systemDetector = "Você é um classificador de intenções de prompt. Classifique a última mensagem do usuário em uma destas quatro categorias exatas: 'TEXT' (para conversas normais, perguntas, redação de relatórios, planejamentos, textos pedagógicos, respostas gerais), 'IMAGE' (se o usuário estiver pedindo explicitamente para gerar, desenhar, criar ou ilustrar uma imagem, figura, ilustração ou foto), 'AUDIO' (se o usuário estiver pedindo explicitamente para gerar uma música, melodia, trilha sonora, cantiga ou som), ou 'VIDEO' (se o usuário estiver pedindo explicitamente para gerar ou criar um vídeo ou animação). Responda APENAS com a palavra da categoria em letras maiúsculas: TEXT, IMAGE, AUDIO, VIDEO.";
const detectResponse = await fetch('https://openrouter.ai/api/v1/chat/completions', {
let detectResponse;
let usingGroqForDetection = false;
try {
detectResponse = await fetch('https://openrouter.ai/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
@@ -290,8 +294,37 @@ app.post('/api/chat', requireAuth, async (req, res) => {
temperature: 0.0
})
});
} catch (err) {
console.error('[Smart Router] Falha de conexão com OpenRouter para detecção, tentando Groq...', err.message);
}
if (detectResponse.ok) {
// Se OpenRouter falhou ou retornou erro (ex: status != 200), tenta o Groq como fallback
if ((!detectResponse || !detectResponse.ok) && process.env.GROQ_API_KEY) {
console.log('[Smart Router] Usando Groq (llama-3.1-8b-instant) para classificar intenção...');
try {
detectResponse = 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.1-8b-instant',
messages: [
{ role: 'system', content: systemDetector },
{ role: 'user', content: promptText }
],
max_tokens: 20,
temperature: 0.0
})
});
usingGroqForDetection = detectResponse.ok;
} catch (groqErr) {
console.error('[Smart Router] Falha de detecção na API da Groq:', groqErr.message);
}
}
if (detectResponse && detectResponse.ok) {
const detectData = await detectResponse.json();
const detectedText = detectData.choices?.[0]?.message?.content?.toUpperCase() || 'TEXT';
console.log(`[Smart Router] Resposta crua do detector: "${detectedText}"`);
@@ -615,13 +648,16 @@ app.post('/api/chat', requireAuth, async (req, res) => {
}
// ==========================================================================
// FLUXO PADRÃO DE TEXTO (OpenRouter com SSE streaming original)
// FLUXO PADRÃO DE TEXTO (OpenRouter com SSE streaming original, com fallback Groq)
// ==========================================================================
const enrichedMessages = [KEMILY_SYSTEM_PROMPT, ...messages];
try {
const openrouterUrl = 'https://openrouter.ai/api/v1/chat/completions';
const response = await fetch(openrouterUrl, {
let chatResponse;
let providerUsed = 'OpenRouter';
try {
chatResponse = await fetch('https://openrouter.ai/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
@@ -635,11 +671,36 @@ app.post('/api/chat', requireAuth, async (req, res) => {
stream: true
})
});
} catch (e) {
console.error('Erro na conexão com OpenRouter, tentando Groq...', e.message);
}
if (!response.ok) {
const errorText = await response.text();
console.error('Erro na API do OpenRouter:', errorText);
return res.status(response.status).json({ error: 'Erro ao processar requisição na API do OpenRouter' });
// Se o OpenRouter falhar ou retornar erro (ex: status != 200), caímos no fallback do Groq
if ((!chatResponse || !chatResponse.ok) && process.env.GROQ_API_KEY) {
console.log('[Smart Router] Usando Groq (llama-3.3-70b-versatile) como fallback de streaming de texto...');
providerUsed = 'Groq';
try {
chatResponse = 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: process.env.GROQ_MODEL || 'llama-3.3-70b-versatile',
messages: enrichedMessages,
stream: true
})
});
} catch (groqErr) {
console.error('[Smart Router] Erro crítico: Falha no fallback do Groq também:', groqErr.message);
}
}
if (!chatResponse || !chatResponse.ok) {
const errorText = chatResponse ? await chatResponse.text() : 'Sem conexão com os servidores de IA';
console.error(`Erro na API de IA (${providerUsed}):`, errorText);
return res.status(500).json({ error: `Erro ao processar requisição na API de IA (${providerUsed})` });
}
// Configurar headers para Server-Sent Events (SSE)
@@ -647,7 +708,7 @@ app.post('/api/chat', requireAuth, async (req, res) => {
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const reader = response.body.getReader();
const reader = chatResponse.body.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';