fix: hoisting do closeRecordModal no frontend e fallback Groq/Gemini no Whisper/Relatório

This commit is contained in:
2026-05-27 17:26:57 +00:00
parent d6260747d1
commit 056d211494
2 changed files with 136 additions and 38 deletions
+133 -35
View File
@@ -230,28 +230,56 @@ app.post('/api/observacao/transcrever', requireAuth, multerUpload.single('audio'
// Salva arquivo temporário a partir do buffer
fs.writeFileSync(tempPath, req.file.buffer);
// Transcreve via OpenAI Whisper
const whisperFormData = new FormData();
whisperFormData.append('file', fs.createReadStream(tempPath));
whisperFormData.append('model', 'whisper-1');
whisperFormData.append('language', 'pt');
whisperFormData.append('response_format', 'text');
let transcribedText = '';
const whisperRes = await fetch('https://api.openai.com/v1/audio/transcriptions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
},
body: whisperFormData
});
if (process.env.OPENAI_API_KEY) {
console.log('[Smart Transcription] Usando OpenAI Whisper...');
const whisperFormData = new FormData();
whisperFormData.append('file', fs.createReadStream(tempPath));
whisperFormData.append('model', 'whisper-1');
whisperFormData.append('language', 'pt');
whisperFormData.append('response_format', 'text');
if (!whisperRes.ok) {
const errText = await whisperRes.text();
console.error('Whisper error:', errText);
throw new Error('Erro na transcrição');
const whisperRes = await fetch('https://api.openai.com/v1/audio/transcriptions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
},
body: whisperFormData
});
if (!whisperRes.ok) {
const errText = await whisperRes.text();
console.error('OpenAI Whisper error:', errText);
throw new Error('Erro na transcrição via OpenAI');
}
transcribedText = await whisperRes.text();
} else if (process.env.GROQ_API_KEY) {
console.log('[Smart Transcription] Usando Groq Whisper como fallback...');
const whisperFormData = new FormData();
whisperFormData.append('file', fs.createReadStream(tempPath));
whisperFormData.append('model', 'whisper-large-v3');
whisperFormData.append('language', 'pt');
whisperFormData.append('response_format', 'text');
const whisperRes = await fetch('https://api.groq.com/openai/v1/audio/transcriptions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GROQ_API_KEY}`
},
body: whisperFormData
});
if (!whisperRes.ok) {
const errText = await whisperRes.text();
console.error('Groq Whisper error:', errText);
throw new Error('Erro na transcrição via Groq');
}
transcribedText = await whisperRes.text();
} else {
throw new Error('Nenhuma chave de API configurada para transcrição de áudio (OPENAI_API_KEY ou GROQ_API_KEY).');
}
const transcribedText = await whisperRes.text();
fs.unlinkSync(tempPath); // Limpa tmp
if (!transcribedText || transcribedText.trim().length < 5) {
@@ -286,26 +314,96 @@ ${transcribedText}
Retorne APENAS o JSON válido:`;
const reportRes = await fetch('https://openrouter.ai/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
messages: [{ role: 'user', content: reportPrompt }],
temperature: 0.5,
max_tokens: 1500
})
});
let rawContent = '';
if (!reportRes.ok) {
throw new Error('Erro ao gerar relatório');
// Tentar OpenRouter
try {
console.log('[Smart Report] Tentando gerar relatório via OpenRouter...');
const reportRes = await fetch('https://openrouter.ai/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
messages: [{ role: 'user', content: reportPrompt }],
temperature: 0.5,
max_tokens: 1500
})
});
if (reportRes.ok) {
const reportData = await reportRes.json();
rawContent = reportData.choices?.[0]?.message?.content || '';
} else {
console.warn('[Smart Report] Falha no OpenRouter:', await reportRes.text());
}
} catch (err) {
console.error('[Smart Report] Erro de conexão com OpenRouter:', err.message);
}
const reportData = await reportRes.json();
const rawContent = reportData.choices?.[0]?.message?.content || '';
// Fallback para Groq se OpenRouter falhar
if (!rawContent && process.env.GROQ_API_KEY) {
try {
console.log('[Smart Report] Usando Groq como fallback para gerar relatório...');
const reportRes = 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: [{ role: 'user', content: reportPrompt }],
temperature: 0.5,
max_tokens: 1500
})
});
if (reportRes.ok) {
const reportData = await reportRes.json();
rawContent = reportData.choices?.[0]?.message?.content || '';
} else {
console.warn('[Smart Report] Falha no Groq:', await reportRes.text());
}
} catch (err) {
console.error('[Smart Report] Erro de conexão com Groq:', err.message);
}
}
// Fallback para Google Gemini se OpenRouter e Groq falharem
if (!rawContent && process.env.GOOGLE_AI_API_KEY) {
try {
console.log('[Smart Report] Usando Google Gemini como fallback para gerar relatório...');
const reportRes = await fetch('https://generativelanguage.googleapis.com/v1beta/openai/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GOOGLE_AI_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: process.env.GOOGLE_AI_MODEL || 'gemini-2.5-flash',
messages: [{ role: 'user', content: reportPrompt }],
temperature: 0.5,
max_tokens: 1500
})
});
if (reportRes.ok) {
const reportData = await reportRes.json();
rawContent = reportData.choices?.[0]?.message?.content || '';
} else {
console.warn('[Smart Report] Falha no Google Gemini:', await reportRes.text());
}
} catch (err) {
console.error('[Smart Report] Erro de conexão com Google Gemini:', err.message);
}
}
if (!rawContent) {
throw new Error('Falha ao processar o relatório pedagógico em todos os provedores de IA (OpenRouter, Groq e Google Gemini).');
}
// Parse JSON da resposta
let parsedData = { criancas: null, turma: 'não informada', tags: [], relatorio: rawContent };