Correção do upload de áudio, integração com OpenAI e ajuste de campos no frontend
This commit is contained in:
parent
bc7a2ed7d8
commit
78f125c279
2
.env
2
.env
@ -1,2 +1,2 @@
|
|||||||
OPENAI_API_KEY=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
|
OPENAI_API_KEY=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
|
||||||
PORT=5000
|
PORT=5000
|
||||||
@ -1 +1 @@
|
|||||||
OPENAI_API_KEY=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
|
OPENAI_API_KEY=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
|
||||||
@ -3,11 +3,11 @@ const multer = require('multer');
|
|||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const OpenAI = require('openai/index.js');
|
const OpenAI = require('openai');
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = 3001; // O backend rodará na porta 3001
|
const port = 3001;
|
||||||
|
|
||||||
// Configurações de Segurança e JSON
|
// Configurações de Segurança e JSON
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
@ -28,16 +28,22 @@ app.post('/api/transcrever-relatorio', upload.single('audio'), async (req, res)
|
|||||||
return res.status(400).json({ error: 'Nenhum arquivo de áudio enviado.' });
|
return res.status(400).json({ error: 'Nenhum arquivo de áudio enviado.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const audioPath = req.file.path;
|
// --- CORREÇÃO DO FORMATO DE ARQUIVO ---
|
||||||
|
const originalExt = path.extname(req.file.originalname);
|
||||||
|
const audioPath = req.file.path + originalExt;
|
||||||
|
|
||||||
|
// Renomeia o arquivo físico na pasta uploads
|
||||||
|
fs.renameSync(req.file.path, audioPath);
|
||||||
|
// ---------------------------------------
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log("1. Recebendo áudio e iniciando transcrição...");
|
console.log(`1. Processando arquivo: ${audioPath}`);
|
||||||
|
|
||||||
// PASSO 1: Whisper (Áudio -> Texto)
|
// PASSO 1: Whisper (Áudio -> Texto)
|
||||||
const transcription = await openai.audio.transcriptions.create({
|
const transcription = await openai.audio.transcriptions.create({
|
||||||
file: fs.createReadStream(audioPath),
|
file: fs.createReadStream(audioPath),
|
||||||
model: "whisper-1",
|
model: "whisper-1",
|
||||||
language: "pt", // Força português para melhorar precisão médica
|
language: "pt",
|
||||||
});
|
});
|
||||||
|
|
||||||
const textoTranscrevido = transcription.text;
|
const textoTranscrevido = transcription.text;
|
||||||
@ -48,7 +54,7 @@ app.post('/api/transcrever-relatorio', upload.single('audio'), async (req, res)
|
|||||||
// PASSO 2: GPT-4o (Texto -> JSON Estruturado)
|
// PASSO 2: GPT-4o (Texto -> JSON Estruturado)
|
||||||
const completion = await openai.chat.completions.create({
|
const completion = await openai.chat.completions.create({
|
||||||
model: "gpt-4o",
|
model: "gpt-4o",
|
||||||
response_format: { type: "json_object" }, // Garante que volta JSON válido
|
response_format: { type: "json_object" },
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
role: "system",
|
role: "system",
|
||||||
@ -57,8 +63,9 @@ app.post('/api/transcrever-relatorio', upload.single('audio'), async (req, res)
|
|||||||
Sua tarefa é ler a transcrição de um ditado médico e extrair as informações para um JSON.
|
Sua tarefa é ler a transcrição de um ditado médico e extrair as informações para um JSON.
|
||||||
|
|
||||||
Os campos obrigatórios do JSON são:
|
Os campos obrigatórios do JSON são:
|
||||||
|
- "paciente": O Nome completo do paciente mencionado. Se não houver, retorne string vazia "".
|
||||||
- "exam": Nome do exame realizado.
|
- "exam": Nome do exame realizado.
|
||||||
- "diagnostico": O texto completo referente aos achados/diagnóstico. Melhore a pontuação e corrija termos médicos se necessário.
|
- "diagnostico": O texto completo referente aos achados/diagnóstico. (Não repita o nome do paciente aqui, foque na doença).
|
||||||
- "conclusao": O texto da conclusão ou impressão diagnóstica.
|
- "conclusao": O texto da conclusão ou impressão diagnóstica.
|
||||||
|
|
||||||
Se o médico não mencionar algum campo, deixe como string vazia "".
|
Se o médico não mencionar algum campo, deixe como string vazia "".
|
||||||
@ -82,16 +89,21 @@ app.post('/api/transcrever-relatorio', upload.single('audio'), async (req, res)
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erro no processamento:", error);
|
console.error("Erro no processamento:", error);
|
||||||
res.status(500).json({ error: 'Erro ao processar o áudio.' });
|
|
||||||
|
if (error.code === 'insufficient_quota') {
|
||||||
|
return res.status(429).json({ error: 'Erro: Saldo insuficiente na API da OpenAI. Verifique o Billing.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(500).json({ error: 'Erro ao processar o áudio na IA.' });
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
// PASSO 3: Limpeza (Apagar o áudio da pasta uploads para não encher o servidor)
|
// PASSO 3: Limpeza
|
||||||
if (fs.existsSync(audioPath)) {
|
if (fs.existsSync(audioPath)) {
|
||||||
fs.unlinkSync(audioPath);
|
fs.unlinkSync(audioPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Iniciar Servidor
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`🚀 Backend rodando em http://localhost:${port}`);
|
console.log(`🚀 Backend rodando em http://localhost:${port}`);
|
||||||
});
|
});
|
||||||
@ -1,11 +1,11 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom'; // <--- 1. ADICIONADO useLocation
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import html2pdf from 'html2pdf.js';
|
import html2pdf from 'html2pdf.js';
|
||||||
import './styleMedico/NovoRelatorioAudio.css';
|
import './styleMedico/NovoRelatorioAudio.css';
|
||||||
|
|
||||||
const NovoRelatorioAudio = () => {
|
const NovoRelatorioAudio = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation(); // <--- 2. Inicializa o hook para ler os dados enviados
|
const location = useLocation();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
@ -14,33 +14,26 @@ const NovoRelatorioAudio = () => {
|
|||||||
exam: '',
|
exam: '',
|
||||||
diagnostico: '',
|
diagnostico: '',
|
||||||
conclusao: '',
|
conclusao: '',
|
||||||
medico_nome: 'Dr._______________________________' // Ajuste conforme necessário
|
medico_nome: 'Dr._______________________________'
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- 3. NOVO BLOCO: Receber dados da Videochamada ---
|
// --- Receber dados da Videochamada (Se houver) ---
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Verifica se a navegação trouxe dados no "state"
|
|
||||||
if (location.state && location.state.aiResult) {
|
if (location.state && location.state.aiResult) {
|
||||||
console.log("Dados recebidos da Videochamada:", location.state);
|
console.log("Dados recebidos da Videochamada:", location.state);
|
||||||
|
|
||||||
const { aiResult, paciente } = location.state;
|
const { aiResult, paciente } = location.state;
|
||||||
|
|
||||||
// Atualiza o formulário com o que a IA gerou
|
|
||||||
setFormData(prev => ({
|
setFormData(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
// Se tiver paciente vindo da chamada, usa ele. Se não, mantém vazio.
|
|
||||||
paciente_nome: paciente?.name || prev.paciente_nome,
|
paciente_nome: paciente?.name || prev.paciente_nome,
|
||||||
|
|
||||||
// Dados da IA
|
|
||||||
exam: aiResult.exam || 'Consulta Telemedicina',
|
exam: aiResult.exam || 'Consulta Telemedicina',
|
||||||
diagnostico: aiResult.diagnostico || '',
|
diagnostico: aiResult.diagnostico || '',
|
||||||
conclusao: aiResult.conclusao || ''
|
conclusao: aiResult.conclusao || ''
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [location]); // Executa assim que a página carrega
|
}, [location]);
|
||||||
|
|
||||||
// --- FIM DO BLOCO NOVO ---
|
|
||||||
|
|
||||||
|
// --- LÓGICA DO UPLOAD DE ÁUDIO ---
|
||||||
const handleAudioUpload = async (e) => {
|
const handleAudioUpload = async (e) => {
|
||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
@ -59,8 +52,10 @@ const NovoRelatorioAudio = () => {
|
|||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
|
// ATUALIZAÇÃO AQUI: Mapeando todos os campos retornados pela IA
|
||||||
setFormData(prev => ({
|
setFormData(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
|
paciente_nome: result.paciente || prev.paciente_nome, // Pega o nome do paciente
|
||||||
exam: result.exam || prev.exam,
|
exam: result.exam || prev.exam,
|
||||||
diagnostico: result.diagnostico || prev.diagnostico,
|
diagnostico: result.diagnostico || prev.diagnostico,
|
||||||
conclusao: result.conclusao || prev.conclusao
|
conclusao: result.conclusao || prev.conclusao
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import LaudoManager from "../../pages/LaudoManager";
|
|||||||
import ConsultaCadastroManager from "../../PagesPaciente/ConsultaCadastroManager";
|
import ConsultaCadastroManager from "../../PagesPaciente/ConsultaCadastroManager";
|
||||||
import ConsultasPaciente from "../../PagesPaciente/ConsultasPaciente";
|
import ConsultasPaciente from "../../PagesPaciente/ConsultasPaciente";
|
||||||
import ConsultaEditPage from "../../PagesPaciente/ConsultaEditPage";
|
import ConsultaEditPage from "../../PagesPaciente/ConsultaEditPage";
|
||||||
|
import BotaoVideoPaciente from "../../components/BotaoVideoPaciente";
|
||||||
function PerfilPaciente({ onLogout }) {
|
function PerfilPaciente({ onLogout }) {
|
||||||
|
|
||||||
const [dadosConsulta, setConsulta] = useState({})
|
const [dadosConsulta, setConsulta] = useState({})
|
||||||
@ -25,6 +26,7 @@ const [dadosConsulta, setConsulta] = useState({})
|
|||||||
<Route path="*" element={<h2>Página não encontrada</h2>} />
|
<Route path="*" element={<h2>Página não encontrada</h2>} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
|
<BotaoVideoPaciente />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user