Adicionado fluxo de revisão de transcrição de voz antes da análise de IA
This commit is contained in:
@@ -342,7 +342,7 @@ async function initDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
// Rota: POST /api/observacao/transcrever — recebe áudio, transcreve via Whisper, gera relatório
|
||||
// Rota: POST /api/observacao/transcrever — recebe áudio e transcreve via Whisper
|
||||
app.post('/api/observacao/transcrever', requireAuth, multerUpload.single('audio'), async (req, res) => {
|
||||
if (!req.file) {
|
||||
return res.status(400).json({ error: 'Nenhum áudio recebido' });
|
||||
@@ -355,18 +355,15 @@ app.post('/api/observacao/transcrever', requireAuth, multerUpload.single('audio'
|
||||
else if (req.file.mimetype.includes('wav')) ext = 'wav';
|
||||
else if (req.file.mimetype.includes('mpeg')) ext = 'mp3';
|
||||
}
|
||||
const tempPath = `/tmp/obs_${Date.now()}.${ext}`;
|
||||
|
||||
try {
|
||||
// Salva arquivo temporário a partir do buffer
|
||||
fs.writeFileSync(tempPath, req.file.buffer);
|
||||
|
||||
let transcribedText = '';
|
||||
const fileBlob = new Blob([req.file.buffer], { type: req.file.mimetype || 'audio/webm' });
|
||||
|
||||
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('file', fileBlob, `audio.${ext}`);
|
||||
whisperFormData.append('model', 'whisper-1');
|
||||
whisperFormData.append('language', 'pt');
|
||||
whisperFormData.append('response_format', 'text');
|
||||
@@ -386,9 +383,9 @@ app.post('/api/observacao/transcrever', requireAuth, multerUpload.single('audio'
|
||||
}
|
||||
transcribedText = await whisperRes.text();
|
||||
} else if (process.env.GROQ_API_KEY) {
|
||||
console.log('[Smart Transcription] Usando Groq Whisper como fallback...');
|
||||
console.log('[Smart Transcription] Usando Groq Whisper...');
|
||||
const whisperFormData = new FormData();
|
||||
whisperFormData.append('file', fs.createReadStream(tempPath));
|
||||
whisperFormData.append('file', fileBlob, `audio.${ext}`);
|
||||
whisperFormData.append('model', 'whisper-large-v3');
|
||||
whisperFormData.append('language', 'pt');
|
||||
whisperFormData.append('response_format', 'text');
|
||||
@@ -411,34 +408,49 @@ app.post('/api/observacao/transcrever', requireAuth, multerUpload.single('audio'
|
||||
throw new Error('Nenhuma chave de API configurada para transcrição de áudio (OPENAI_API_KEY ou GROQ_API_KEY).');
|
||||
}
|
||||
|
||||
fs.unlinkSync(tempPath); // Limpa tmp
|
||||
|
||||
if (!transcribedText || transcribedText.trim().length < 5) {
|
||||
return res.json({ report: '⚠️ O áudio estava muito curto ou não pôde ser transcrito. Tente gravar por mais alguns segundos.' });
|
||||
if (!transcribedText || transcribedText.trim().length < 2) {
|
||||
return res.status(400).json({ error: 'O áudio não contém fala identificável.' });
|
||||
}
|
||||
|
||||
// Gera relatório estruturado via GPT
|
||||
// Envia transcrição + pedido de extrair: crianças, turma, tags, relatório
|
||||
const reportPrompt = `Você é a Kemily, assistente pedagógica da professora Camila Martella Gasparini Reifonas.
|
||||
res.json({ transcribedText });
|
||||
|
||||
Ela acabou de gravar uma observação em voz sobre seus alunos da educação infantil. Sua tarefa é ANALISAR a transcrição e retornar UM OBJETO JSON (sem markdown, sem código, apenas o JSON puro) com:
|
||||
} catch (err) {
|
||||
console.error('Erro transcrever:', err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Rota: POST /api/observacao/analisar — recebe texto transcrito e gera relatório pedagógico
|
||||
app.post('/api/observacao/analisar', requireAuth, async (req, res) => {
|
||||
const { transcribedText } = req.body;
|
||||
if (!transcribedText || transcribedText.trim().length < 5) {
|
||||
return res.status(400).json({ error: 'Texto muito curto ou inexistente para análise.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const agentConfig = readAgentConfig();
|
||||
const agentName = agentConfig.agentName || "Kemily";
|
||||
|
||||
const reportPrompt = `Você é a ${agentName}, assistente pedagógica da professora Camila Martella Gasparini Reifonas.
|
||||
|
||||
Ela acabou de registrar uma observação sobre seus alunos da educação infantil. Sua tarefa é ANALISAR o texto e retornar UM OBJETO JSON (sem markdown, sem código, apenas o JSON puro) com:
|
||||
|
||||
{
|
||||
"criancas": ["lista de nomes de crianças mencionadas ou 'null' se não identificar nenhuma"],
|
||||
"turma": "turma mencionada ou 'não informada'",
|
||||
"tags": ["tag1", "tag2"] (social, cognitivo, motor, linguagem, emocional,轮的哪个),
|
||||
"tags": ["tag1", "tag2"] (social, cognitivo, motor, linguagem, emocional, colaborativo, autónomo),
|
||||
"relatorio": "...markdown estruturado do relatório pedagógico..."
|
||||
}
|
||||
|
||||
REGRAS:
|
||||
- Identifique crianças pelo nome próprio mencionado na fala
|
||||
- Identifique crianças pelo nome próprio mencionado na fala/texto
|
||||
- Se não conseguir identificar nomes, use "criancas": null
|
||||
- Tags: escolha entre social, cognitivo, motor, linguagem, emocional, colaborativo, autónomo
|
||||
- Relatório em português, linguagem formal-pedagógica, seções claras em **markdown**
|
||||
- Foque em evidências observáveis
|
||||
- Máximo 120 palavras no relatório
|
||||
|
||||
TRANSCRIÇÃO:
|
||||
OBSERVAÇÃO REGISTRADA:
|
||||
"""
|
||||
${transcribedText}
|
||||
"""
|
||||
@@ -583,19 +595,14 @@ Retorne APENAS o JSON válido:`;
|
||||
throw new Error('Nenhum JSON encontrado na resposta da IA');
|
||||
}
|
||||
} catch (parseErr) {
|
||||
// Se não conseguir parsear JSON, retorna o texto como relatório
|
||||
console.warn('Não foi possível parsear JSON da resposta da IA:', parseErr.message);
|
||||
parsedData.relatorio = rawContent;
|
||||
}
|
||||
|
||||
res.json({
|
||||
transcribedText,
|
||||
...parsedData
|
||||
});
|
||||
res.json(parsedData);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erro transcrever/salvar:', err);
|
||||
if (fs.existsSync(tempPath)) fs.unlinkSync(tempPath);
|
||||
console.error('Erro ao analisar observação:', err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user