⚡ fix: hoisting do closeRecordModal no frontend e fallback Groq/Gemini no Whisper/Relatório
This commit is contained in:
+3
-3
@@ -2042,7 +2042,7 @@ const initApp = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Atualizar timer
|
// Atualizar timer
|
||||||
const updateTimer = () => {
|
function updateTimer() {
|
||||||
if (!recordingStartTime) return;
|
if (!recordingStartTime) return;
|
||||||
const elapsed = Math.floor((Date.now() - recordingStartTime) / 1000);
|
const elapsed = Math.floor((Date.now() - recordingStartTime) / 1000);
|
||||||
const mins = Math.floor(elapsed / 60).toString().padStart(2, '0');
|
const mins = Math.floor(elapsed / 60).toString().padStart(2, '0');
|
||||||
@@ -2088,7 +2088,7 @@ const initApp = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mostrar prévia do relatório
|
// Mostrar prévia do relatório
|
||||||
const showReportPreview = (data) => {
|
function showReportPreview(data) {
|
||||||
recordStateProcessing.style.display = 'none';
|
recordStateProcessing.style.display = 'none';
|
||||||
recordStatePreview.style.display = 'flex';
|
recordStatePreview.style.display = 'flex';
|
||||||
|
|
||||||
@@ -2166,7 +2166,7 @@ const initApp = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fechar modal
|
// Fechar modal
|
||||||
const closeRecordModal = () => {
|
function closeRecordModal() {
|
||||||
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
|
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
|
||||||
mediaRecorder.stop();
|
mediaRecorder.stop();
|
||||||
mediaRecorder.stream.getTracks().forEach(t => t.stop());
|
mediaRecorder.stream.getTracks().forEach(t => t.stop());
|
||||||
|
|||||||
@@ -230,28 +230,56 @@ app.post('/api/observacao/transcrever', requireAuth, multerUpload.single('audio'
|
|||||||
// Salva arquivo temporário a partir do buffer
|
// Salva arquivo temporário a partir do buffer
|
||||||
fs.writeFileSync(tempPath, req.file.buffer);
|
fs.writeFileSync(tempPath, req.file.buffer);
|
||||||
|
|
||||||
// Transcreve via OpenAI Whisper
|
let transcribedText = '';
|
||||||
const whisperFormData = new FormData();
|
|
||||||
whisperFormData.append('file', fs.createReadStream(tempPath));
|
|
||||||
whisperFormData.append('model', 'whisper-1');
|
|
||||||
whisperFormData.append('language', 'pt');
|
|
||||||
whisperFormData.append('response_format', 'text');
|
|
||||||
|
|
||||||
const whisperRes = await fetch('https://api.openai.com/v1/audio/transcriptions', {
|
if (process.env.OPENAI_API_KEY) {
|
||||||
method: 'POST',
|
console.log('[Smart Transcription] Usando OpenAI Whisper...');
|
||||||
headers: {
|
const whisperFormData = new FormData();
|
||||||
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
|
whisperFormData.append('file', fs.createReadStream(tempPath));
|
||||||
},
|
whisperFormData.append('model', 'whisper-1');
|
||||||
body: whisperFormData
|
whisperFormData.append('language', 'pt');
|
||||||
});
|
whisperFormData.append('response_format', 'text');
|
||||||
|
|
||||||
if (!whisperRes.ok) {
|
const whisperRes = await fetch('https://api.openai.com/v1/audio/transcriptions', {
|
||||||
const errText = await whisperRes.text();
|
method: 'POST',
|
||||||
console.error('Whisper error:', errText);
|
headers: {
|
||||||
throw new Error('Erro na transcrição');
|
'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
|
fs.unlinkSync(tempPath); // Limpa tmp
|
||||||
|
|
||||||
if (!transcribedText || transcribedText.trim().length < 5) {
|
if (!transcribedText || transcribedText.trim().length < 5) {
|
||||||
@@ -286,26 +314,96 @@ ${transcribedText}
|
|||||||
|
|
||||||
Retorne APENAS o JSON válido:`;
|
Retorne APENAS o JSON válido:`;
|
||||||
|
|
||||||
const reportRes = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
let rawContent = '';
|
||||||
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) {
|
// Tentar OpenRouter
|
||||||
throw new Error('Erro ao gerar relatório');
|
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();
|
// Fallback para Groq se OpenRouter falhar
|
||||||
const rawContent = reportData.choices?.[0]?.message?.content || '';
|
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
|
// Parse JSON da resposta
|
||||||
let parsedData = { criancas: null, turma: 'não informada', tags: [], relatorio: rawContent };
|
let parsedData = { criancas: null, turma: 'não informada', tags: [], relatorio: rawContent };
|
||||||
|
|||||||
Reference in New Issue
Block a user