Update app.py
Browse files
app.py
CHANGED
|
@@ -4,6 +4,112 @@ import fitz # PyMuPDF
|
|
| 4 |
from groq import Groq
|
| 5 |
from langchain_groq import ChatGroq
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
# Inicializaci贸n del cliente
|
| 8 |
api_key = os.environ.get("GROQ_API_KEY")
|
| 9 |
if not api_key:
|
|
@@ -107,18 +213,19 @@ def process_input(audio, pdfs, current_text):
|
|
| 107 |
except Exception as e:
|
| 108 |
transcription_text = ""
|
| 109 |
print(f"Error en transcripci贸n de audio: {e}")
|
|
|
|
| 110 |
try:
|
| 111 |
pdf_text = extract_texts_from_pdfs(pdfs)
|
| 112 |
except Exception as e:
|
| 113 |
pdf_text = ""
|
| 114 |
print(f"Error en extracci贸n de PDFs: {e}")
|
| 115 |
|
| 116 |
-
# Now process the transcription_text and pdf_text in batches
|
| 117 |
updated_text = current_text
|
|
|
|
| 118 |
|
| 119 |
-
#
|
| 120 |
-
max_chunk_words = 2500
|
| 121 |
-
|
| 122 |
for text_label, text_content in [("Audio", transcription_text), ("PDF", pdf_text)]:
|
| 123 |
if not text_content:
|
| 124 |
continue
|
|
@@ -128,15 +235,18 @@ def process_input(audio, pdfs, current_text):
|
|
| 128 |
for chunk in text_chunks:
|
| 129 |
transcription_chunk = chunk if text_label == "Audio" else ""
|
| 130 |
pdf_chunk = chunk if text_label == "PDF" else ""
|
|
|
|
|
|
|
| 131 |
organized_record = organize_clinical_record(updated_text, transcription_chunk, pdf_chunk)
|
| 132 |
if organized_record:
|
| 133 |
updated_text = organized_record.content
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
|
|
|
|
|
|
| 138 |
|
| 139 |
-
return updated_text, "Informaci贸n de depuraci贸n"
|
| 140 |
|
| 141 |
|
| 142 |
|
|
@@ -178,17 +288,30 @@ initial_text = """
|
|
| 178 |
|
| 179 |
# Interfaz de Gradio
|
| 180 |
with gr.Blocks(theme=theme) as iface:
|
| 181 |
-
gr.Markdown("#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
-
iterative_output = gr.Textbox(
|
| 184 |
-
label="Registro Cl铆nico Organizado",
|
| 185 |
-
value=initial_text,
|
| 186 |
-
lines=20
|
| 187 |
-
)
|
| 188 |
current_state = gr.State(value=initial_text)
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
|
| 193 |
# Funci贸n para capturar cambios en el iterative_output
|
| 194 |
def on_text_change(updated_text):
|
|
|
|
| 4 |
from groq import Groq
|
| 5 |
from langchain_groq import ChatGroq
|
| 6 |
|
| 7 |
+
RAID_WEIGHTS = {
|
| 8 |
+
"Dolor": 0.21,
|
| 9 |
+
"Discapacidad Funcional": 0.16,
|
| 10 |
+
"Fatiga": 0.15,
|
| 11 |
+
"Sue帽o": 0.12,
|
| 12 |
+
"Bienestar F铆sico": 0.12,
|
| 13 |
+
"Bienestar Emocional": 0.12,
|
| 14 |
+
"Afronte": 0.12
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
RAID_PROMPT_TEMPLATE = """
|
| 18 |
+
Analiza la siguiente conversaci贸n y registro cl铆nico del paciente con artritis reumatoide.
|
| 19 |
+
Asigna un puntaje de 0 a 10 para cada categor铆a del RAID seg煤n la escala proporcionada.
|
| 20 |
+
La respuesta DEBE ser SOLO un JSON v谩lido con los puntajes y el c谩lculo final.
|
| 21 |
+
|
| 22 |
+
Escalas:
|
| 23 |
+
1. Dolor (0 = Ninguno, 10 = Extremo)
|
| 24 |
+
2. Discapacidad Funcional (0 = Sin dificultad, 10 = Dificultad extrema)
|
| 25 |
+
3. Fatiga (0 = Sin fatiga, 10 = Totalmente exhausto)
|
| 26 |
+
4. Sue帽o (0 = Sin dificultad, 10 = Dificultad extrema)
|
| 27 |
+
5. Bienestar F铆sico (0 = Muy bueno, 10 = Muy malo)
|
| 28 |
+
6. Bienestar Emocional (0 = Muy bueno, 10 = Muy malo)
|
| 29 |
+
7. Afronte (0 = Muy bien, 10 = Muy mal)
|
| 30 |
+
|
| 31 |
+
Texto del paciente:
|
| 32 |
+
{patient_text}
|
| 33 |
+
|
| 34 |
+
Registro cl铆nico relevante:
|
| 35 |
+
{clinical_record}
|
| 36 |
+
|
| 37 |
+
Calcula el puntaje total usando estos pesos:
|
| 38 |
+
{weights}
|
| 39 |
+
|
| 40 |
+
Respuesta JSON (ejemplo):
|
| 41 |
+
{{
|
| 42 |
+
"raid_scores": {{
|
| 43 |
+
"Dolor": 8,
|
| 44 |
+
"Discapacidad Funcional": 6,
|
| 45 |
+
"Fatiga": 7,
|
| 46 |
+
"Sue帽o": 5,
|
| 47 |
+
"Bienestar F铆sico": 6,
|
| 48 |
+
"Bienestar Emocional": 7,
|
| 49 |
+
"Afronte": 4
|
| 50 |
+
}},
|
| 51 |
+
"raid_total": 6.45
|
| 52 |
+
}}
|
| 53 |
+
"""
|
| 54 |
+
def evaluate_raid(patient_text, clinical_record):
|
| 55 |
+
"""Eval煤a el RAID score basado en el texto del paciente y registro cl铆nico"""
|
| 56 |
+
try:
|
| 57 |
+
prompt = RAID_PROMPT_TEMPLATE.format(
|
| 58 |
+
patient_text=patient_text[:2000], # Limitar texto para contexto
|
| 59 |
+
clinical_record=clinical_record[:2000],
|
| 60 |
+
weights=RAID_WEIGHTS
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
response = chat_groq.invoke(prompt)
|
| 64 |
+
|
| 65 |
+
# Extraer JSON de la respuesta
|
| 66 |
+
start = response.content.find('{')
|
| 67 |
+
end = response.content.rfind('}') + 1
|
| 68 |
+
json_response = json.loads(response.content[start:end])
|
| 69 |
+
|
| 70 |
+
# Validar estructura
|
| 71 |
+
if not all(key in json_response['raid_scores'] for key in RAID_WEIGHTS):
|
| 72 |
+
raise ValueError("Faltan componentes del RAID en la respuesta")
|
| 73 |
+
|
| 74 |
+
return json_response
|
| 75 |
+
|
| 76 |
+
except Exception as e:
|
| 77 |
+
print(f"Error en evaluaci贸n RAID: {e}")
|
| 78 |
+
return {
|
| 79 |
+
"raid_scores": {k: 0 for k in RAID_WEIGHTS},
|
| 80 |
+
"raid_total": 0
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
def format_raid_results(raid_data):
|
| 84 |
+
"""Formatea los resultados del RAID para visualizaci贸n"""
|
| 85 |
+
if not raid_data:
|
| 86 |
+
return "No se pudo calcular el RAID score"
|
| 87 |
+
|
| 88 |
+
scores = raid_data.get('raid_scores', {})
|
| 89 |
+
total = raid_data.get('raid_total', 0)
|
| 90 |
+
|
| 91 |
+
breakdown = "\n".join([
|
| 92 |
+
f"- {category}: {score} (Peso: {weight*100}%)"
|
| 93 |
+
for (category, score), weight in zip(scores.items(), RAID_WEIGHTS.values())
|
| 94 |
+
])
|
| 95 |
+
|
| 96 |
+
interpretation = "Interpretaci贸n del puntaje:\n"
|
| 97 |
+
if total >= 7:
|
| 98 |
+
interpretation += "RAID alto: Impacto significativo en la calidad de vida. Considerar ajuste terap茅utico."
|
| 99 |
+
elif total >= 4:
|
| 100 |
+
interpretation += "RAID moderado: Impacto notable. Monitoreo cercano recomendado."
|
| 101 |
+
else:
|
| 102 |
+
interpretation += "RAID bajo: Buen control sintom谩tico. Mantener seguimiento."
|
| 103 |
+
|
| 104 |
+
return f"""
|
| 105 |
+
**Puntaje Total RAID: {total:.2f}/10**
|
| 106 |
+
|
| 107 |
+
**Desglose:**
|
| 108 |
+
{breakdown}
|
| 109 |
+
|
| 110 |
+
{interpretation}
|
| 111 |
+
"""
|
| 112 |
+
|
| 113 |
# Inicializaci贸n del cliente
|
| 114 |
api_key = os.environ.get("GROQ_API_KEY")
|
| 115 |
if not api_key:
|
|
|
|
| 213 |
except Exception as e:
|
| 214 |
transcription_text = ""
|
| 215 |
print(f"Error en transcripci贸n de audio: {e}")
|
| 216 |
+
|
| 217 |
try:
|
| 218 |
pdf_text = extract_texts_from_pdfs(pdfs)
|
| 219 |
except Exception as e:
|
| 220 |
pdf_text = ""
|
| 221 |
print(f"Error en extracci贸n de PDFs: {e}")
|
| 222 |
|
|
|
|
| 223 |
updated_text = current_text
|
| 224 |
+
raid_results = None
|
| 225 |
|
| 226 |
+
# Procesamiento en lotes
|
| 227 |
+
max_chunk_words = 2500
|
| 228 |
+
|
| 229 |
for text_label, text_content in [("Audio", transcription_text), ("PDF", pdf_text)]:
|
| 230 |
if not text_content:
|
| 231 |
continue
|
|
|
|
| 235 |
for chunk in text_chunks:
|
| 236 |
transcription_chunk = chunk if text_label == "Audio" else ""
|
| 237 |
pdf_chunk = chunk if text_label == "PDF" else ""
|
| 238 |
+
|
| 239 |
+
# Actualizar registro cl铆nico
|
| 240 |
organized_record = organize_clinical_record(updated_text, transcription_chunk, pdf_chunk)
|
| 241 |
if organized_record:
|
| 242 |
updated_text = organized_record.content
|
| 243 |
+
|
| 244 |
+
# Evaluar RAID solo con el audio (conversaci贸n actual)
|
| 245 |
+
if text_label == "Audio" and chunk:
|
| 246 |
+
raid_results = evaluate_raid(chunk, updated_text)
|
| 247 |
+
|
| 248 |
+
return updated_text, format_raid_results(raid_results), ""
|
| 249 |
|
|
|
|
| 250 |
|
| 251 |
|
| 252 |
|
|
|
|
| 288 |
|
| 289 |
# Interfaz de Gradio
|
| 290 |
with gr.Blocks(theme=theme) as iface:
|
| 291 |
+
gr.Markdown("# Sistema de Evaluaci贸n Reumatol贸gica")
|
| 292 |
+
|
| 293 |
+
with gr.Row():
|
| 294 |
+
iterative_output = gr.Textbox(
|
| 295 |
+
label="Registro Cl铆nico",
|
| 296 |
+
value=initial_text,
|
| 297 |
+
lines=20,
|
| 298 |
+
elem_id="clinical_record"
|
| 299 |
+
)
|
| 300 |
+
|
| 301 |
+
raid_output = gr.Markdown(
|
| 302 |
+
"### Resultados RAID\nEsperando evaluaci贸n...",
|
| 303 |
+
elem_id="raid_results"
|
| 304 |
+
)
|
| 305 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
current_state = gr.State(value=initial_text)
|
| 307 |
+
|
| 308 |
+
with gr.Row():
|
| 309 |
+
audio_filepath = gr.Audio(sources=["microphone"], type="filepath",
|
| 310 |
+
label="Conversaci贸n con el Paciente")
|
| 311 |
+
pdf_files = gr.File(file_types=[".pdf"], label="Documentos M茅dicos",
|
| 312 |
+
file_count="multiple")
|
| 313 |
+
|
| 314 |
+
debug_output = gr.Textbox(label="Registros", lines=5, visible=False)
|
| 315 |
|
| 316 |
# Funci贸n para capturar cambios en el iterative_output
|
| 317 |
def on_text_change(updated_text):
|