Update app.py
Browse files
app.py
CHANGED
|
@@ -1,15 +1,18 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import os
|
| 3 |
-
import fitz # PyMuPDF
|
| 4 |
-
from groq import Groq
|
| 5 |
-
from langchain_groq import ChatGroq
|
| 6 |
import json
|
| 7 |
import logging
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
# Configuración de logging
|
| 10 |
logging.basicConfig(level=logging.INFO)
|
| 11 |
logger = logging.getLogger(__name__)
|
| 12 |
|
|
|
|
| 13 |
RAID_WEIGHTS = {
|
| 14 |
"Dolor": 0.21,
|
| 15 |
"Discapacidad Funcional": 0.16,
|
|
@@ -20,40 +23,40 @@ RAID_WEIGHTS = {
|
|
| 20 |
"Afronte": 0.12
|
| 21 |
}
|
| 22 |
|
|
|
|
| 23 |
RAID_PROMPT_TEMPLATE = """
|
| 24 |
-
Analiza la siguiente conversación
|
| 25 |
|
| 26 |
-
Si
|
| 27 |
"No hay información suficiente para calcular el RAID."
|
| 28 |
|
| 29 |
En caso de haber información, extrae y asigna una puntuación del 0 al 10 para cada una de las siguientes categorías, basándote en los detalles proporcionados:
|
| 30 |
|
| 31 |
1. Dolor:
|
| 32 |
-
-
|
| 33 |
- Escala: 0 = Ninguno, 10 = Extremo.
|
| 34 |
2. Discapacidad Funcional:
|
| 35 |
-
-
|
| 36 |
- Escala: 0 = Sin dificultad, 10 = Dificultad extrema.
|
| 37 |
3. Fatiga:
|
| 38 |
-
-
|
| 39 |
- Escala: 0 = Sin fatiga, 10 = Totalmente exhausto.
|
| 40 |
4. Sueño:
|
| 41 |
-
-
|
| 42 |
- Escala: 0 = Sin dificultad, 10 = Dificultad extrema.
|
| 43 |
5. Bienestar Físico:
|
| 44 |
-
-
|
| 45 |
- Escala: 0 = Muy bueno, 10 = Muy malo.
|
| 46 |
6. Bienestar Emocional:
|
| 47 |
-
-
|
| 48 |
- Escala: 0 = Muy bueno, 10 = Muy malo.
|
| 49 |
7. Afronte:
|
| 50 |
-
-
|
| 51 |
- Escala: 0 = Muy bien, 10 = Muy mal.
|
| 52 |
|
| 53 |
Utiliza los siguientes pesos para calcular el valor final del RAID:
|
| 54 |
{weights}
|
| 55 |
|
| 56 |
-
|
| 57 |
La fórmula de cálculo es:
|
| 58 |
RAID final = (Dolor × 0.21) + (Discapacidad Funcional × 0.16) + (Fatiga × 0.15) + (Bienestar Físico × 0.12) + (Sueño × 0.12) + (Bienestar Emocional × 0.12) + (Afronte × 0.12)
|
| 59 |
|
|
@@ -80,27 +83,53 @@ Ejemplo de respuesta JSON:
|
|
| 80 |
|
| 81 |
Texto del paciente:
|
| 82 |
{patient_text}
|
| 83 |
-
|
| 84 |
-
Registro clínico:
|
| 85 |
-
{clinical_record}
|
| 86 |
"""
|
| 87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
-
def
|
| 90 |
-
"""
|
| 91 |
-
|
| 92 |
-
|
| 93 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
prompt = RAID_PROMPT_TEMPLATE.format(
|
| 95 |
-
patient_text=patient_text
|
| 96 |
-
clinical_record=clinical_record[:2000],
|
| 97 |
weights=json.dumps(RAID_WEIGHTS, ensure_ascii=False)
|
| 98 |
)
|
| 99 |
-
logger.info("Evaluando RAID con prompt
|
| 100 |
|
| 101 |
response = chat_groq.invoke(prompt)
|
| 102 |
content = response.content
|
| 103 |
-
logger.info("Respuesta de chat_groq
|
| 104 |
|
| 105 |
# Extraer JSON de la respuesta
|
| 106 |
start = content.find('{')
|
|
@@ -154,8 +183,7 @@ def evaluate_raid(patient_text, clinical_record):
|
|
| 154 |
return partial_result
|
| 155 |
|
| 156 |
def format_raid_results(raid_data):
|
| 157 |
-
"""Formatea los resultados del RAID para visualización
|
| 158 |
-
Si se produjo un error, se añade una nota indicando el problema."""
|
| 159 |
if not raid_data:
|
| 160 |
return "No se pudo calcular el RAID score"
|
| 161 |
|
|
@@ -188,151 +216,29 @@ def format_raid_results(raid_data):
|
|
| 188 |
result += f"\n\n**Nota:** Se produjo un error durante el cálculo del RAID: {raid_data['error']}"
|
| 189 |
return result
|
| 190 |
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
raise ValueError("GROQ_API_KEY no está configurada en las variables de entorno.")
|
| 195 |
-
client = Groq(api_key=api_key)
|
| 196 |
-
model_name = "llama-3.3-70b-versatile"
|
| 197 |
-
chat_groq = ChatGroq(model=model_name)
|
| 198 |
-
|
| 199 |
-
# Funciones de procesamiento
|
| 200 |
-
def transcribe_audio(audio_filepath):
|
| 201 |
-
if not audio_filepath:
|
| 202 |
-
return ""
|
| 203 |
-
try:
|
| 204 |
-
with open(audio_filepath, "rb") as file:
|
| 205 |
-
transcription = client.audio.transcriptions.create(
|
| 206 |
-
file=(audio_filepath, file.read()),
|
| 207 |
-
model="whisper-large-v3",
|
| 208 |
-
response_format="json",
|
| 209 |
-
temperature=0.0
|
| 210 |
-
)
|
| 211 |
-
logger.info("Transcripción de audio exitosa.")
|
| 212 |
-
return transcription.text
|
| 213 |
-
except Exception as e:
|
| 214 |
-
logger.error("Error en transcripción de audio: %s", e, exc_info=True)
|
| 215 |
-
return ""
|
| 216 |
-
|
| 217 |
-
def extract_text_from_pdf(pdf_path):
|
| 218 |
-
try:
|
| 219 |
-
text = ''
|
| 220 |
-
with fitz.open(pdf_path) as doc:
|
| 221 |
-
for page in doc:
|
| 222 |
-
text += page.get_text()
|
| 223 |
-
logger.info("Extracción de texto de PDF exitosa para: %s", pdf_path)
|
| 224 |
-
return text
|
| 225 |
-
except Exception as e:
|
| 226 |
-
logger.error("Error al extraer texto de PDF %s: %s", pdf_path, e, exc_info=True)
|
| 227 |
-
return ""
|
| 228 |
-
|
| 229 |
-
def extract_texts_from_pdfs(pdfs):
|
| 230 |
-
text = ''
|
| 231 |
-
if not pdfs:
|
| 232 |
-
return text
|
| 233 |
-
for pdf in pdfs:
|
| 234 |
-
if isinstance(pdf, dict) and 'name' in pdf:
|
| 235 |
-
pdf_path = pdf['name']
|
| 236 |
-
elif isinstance(pdf, str):
|
| 237 |
-
pdf_path = pdf
|
| 238 |
-
else:
|
| 239 |
-
continue
|
| 240 |
-
pdf_text = extract_text_from_pdf(pdf_path)
|
| 241 |
-
text += pdf_text + "\n"
|
| 242 |
-
return text
|
| 243 |
-
|
| 244 |
-
def split_text_into_chunks(text, max_words_per_chunk):
|
| 245 |
-
words = text.split()
|
| 246 |
-
chunks = []
|
| 247 |
-
for i in range(0, len(words), max_words_per_chunk):
|
| 248 |
-
chunk = ' '.join(words[i:i + max_words_per_chunk])
|
| 249 |
-
chunks.append(chunk)
|
| 250 |
-
return chunks
|
| 251 |
-
|
| 252 |
-
def organize_clinical_record(current_text, transcription_text, pdf_text):
|
| 253 |
-
clinical_record_template = """
|
| 254 |
-
MOTIVO DE CONSULTA: usa una frase en palabras del paciente entre comillas
|
| 255 |
-
ENFERMEDAD ACTUAL:
|
| 256 |
-
(usa terminología médica. En orden cronológico desde el inicio de los síntomas, no incluir la edad ni los antecedentes en esta sección, evolución de los síntomas, factores desencadenantes, hitos de la enfermedad del paciente, finaliza describiendo cómo se siente hoy)
|
| 257 |
-
REVISIÓN POR SISTEMAS:
|
| 258 |
-
(usa terminología médica)
|
| 259 |
-
ANTECEDENTES:
|
| 260 |
-
**Patológicos: (describir en lenguaje técnico médico la enfermedad con clasificación y complicaciones relacionadas de cada antecedente)
|
| 261 |
-
**Alérgicos: (con tipo de reacción y a cuál medicamento)
|
| 262 |
-
**Tóxicos: (exposición a biomasa, IPA, Alcohol por unidades estándar, otros, naturales)
|
| 263 |
-
**Familiares:
|
| 264 |
-
**Transfusionales:
|
| 265 |
-
**Traumáticos:
|
| 266 |
-
**Ginecológicos:
|
| 267 |
-
**Quirúrgicos: (fecha y procedimiento)
|
| 268 |
-
**Estado de vacunación:
|
| 269 |
-
**Hospitalizaciones previas (fecha y descripción breve)
|
| 270 |
-
**Medicamentos:
|
| 271 |
-
AYUDAS DIAGNÓSTICAS:
|
| 272 |
-
(ordenar todas las ayudas diagnósticas por fecha de forma que sea simple y sencillo leer los resultados para el médico, cuando se requiera presenta los resultados en miles ya x10e3 o similares; es decir, multiplica el resultado por 1000, asegurándote de no omitir ninguna ayuda, y no interpretes, solo pon los valores sin rango de normalidad en prosa. Separa cada examen con una coma, usa minúsculas y organiza por fechas.
|
| 273 |
-
Por ejemplo:
|
| 274 |
-
11/10/2024: resultado 1 , resultado 2, ...
|
| 275 |
-
12/11/2023: resultado 1 , resultado 2, ...)
|
| 276 |
-
"""
|
| 277 |
-
prompt = f"""
|
| 278 |
-
Toma el siguiente borrador del registro clínico y actualízalo con la nueva información proporcionada, siguiendo la estructura dada:
|
| 279 |
-
Estructura del Registro Clínico:
|
| 280 |
-
{clinical_record_template}
|
| 281 |
-
Borrador Actual del Registro Clínico:
|
| 282 |
-
{current_text}
|
| 283 |
-
Nueva Información de Audio:
|
| 284 |
-
{transcription_text}
|
| 285 |
-
Nueva Información del PDF:
|
| 286 |
-
{pdf_text}
|
| 287 |
-
Actualiza el borrador incorporando la nueva información en las secciones correspondientes, sin eliminar información previa que aún sea relevante.
|
| 288 |
-
"""
|
| 289 |
-
try:
|
| 290 |
-
organized_text = chat_groq.invoke(prompt)
|
| 291 |
-
logger.info("Organización del registro clínico exitosa.")
|
| 292 |
-
return organized_text.content
|
| 293 |
-
except Exception as e:
|
| 294 |
-
logger.error("Error al invocar ChatGroq para organizar el registro clínico: %s", e, exc_info=True)
|
| 295 |
-
return current_text # Se retorna el texto actual si falla la invocación
|
| 296 |
-
|
| 297 |
-
def process_input(audio, pdfs, current_text):
|
| 298 |
-
try:
|
| 299 |
-
transcription_text = transcribe_audio(audio)
|
| 300 |
-
except Exception as e:
|
| 301 |
-
transcription_text = ""
|
| 302 |
-
logger.error("Error en transcripción de audio: %s", e, exc_info=True)
|
| 303 |
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
updated_text = current_text
|
| 311 |
-
raid_results = None
|
| 312 |
-
|
| 313 |
-
# Procesamiento en lotes
|
| 314 |
-
max_chunk_words = 2500
|
| 315 |
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
updated_text = organized_record
|
| 330 |
-
|
| 331 |
-
# Evaluar RAID solo con el audio (conversación actual)
|
| 332 |
-
if text_label == "Audio" and chunk:
|
| 333 |
-
raid_results = evaluate_raid(chunk, updated_text)
|
| 334 |
-
|
| 335 |
-
return updated_text, format_raid_results(raid_results), ""
|
| 336 |
|
| 337 |
# Configuración del tema
|
| 338 |
theme = gr.themes.Base(
|
|
@@ -346,95 +252,43 @@ theme = gr.themes.Base(
|
|
| 346 |
neutral_hue="neutral",
|
| 347 |
)
|
| 348 |
|
| 349 |
-
#
|
| 350 |
-
initial_text = """
|
| 351 |
-
MOTIVO DE CONSULTA:
|
| 352 |
-
|
| 353 |
-
ENFERMEDAD ACTUAL:
|
| 354 |
-
|
| 355 |
-
REVISIÓN POR SISTEMAS:
|
| 356 |
-
|
| 357 |
-
ANTECEDENTES:
|
| 358 |
-
**Patológicos:
|
| 359 |
-
**Alérgicos:
|
| 360 |
-
**Tóxicos:
|
| 361 |
-
**Familiares:
|
| 362 |
-
**Transfusionales:
|
| 363 |
-
**Traumáticos:
|
| 364 |
-
**Ginecológicos:
|
| 365 |
-
**Quirúrgicos:
|
| 366 |
-
**Estado de vacunación:
|
| 367 |
-
**Hospitalizaciones previas:
|
| 368 |
-
**Medicamentos:
|
| 369 |
-
|
| 370 |
-
AYUDAS DIAGNÓSTICAS:
|
| 371 |
-
"""
|
| 372 |
-
|
| 373 |
-
# Interfaz de Gradio
|
| 374 |
with gr.Blocks(theme=theme) as iface:
|
| 375 |
-
gr.Markdown("#
|
| 376 |
|
| 377 |
with gr.Row():
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 384 |
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
current_state = gr.State(value=initial_text)
|
| 391 |
-
|
| 392 |
-
with gr.Row():
|
| 393 |
-
audio_filepath = gr.Audio(sources=["microphone"], type="filepath",
|
| 394 |
-
label="Conversación con el Paciente")
|
| 395 |
-
pdf_files = gr.File(file_types=[".pdf"], label="Documentos Médicos",
|
| 396 |
-
file_count="multiple")
|
| 397 |
-
|
| 398 |
-
debug_output = gr.Textbox(label="Registros", lines=5, visible=True)
|
| 399 |
-
|
| 400 |
-
# Actualizar current_state cuando se edite el registro clínico
|
| 401 |
-
def on_text_change(updated_text):
|
| 402 |
-
return updated_text
|
| 403 |
-
|
| 404 |
-
iterative_output.change(
|
| 405 |
-
fn=on_text_change,
|
| 406 |
-
inputs=iterative_output,
|
| 407 |
-
outputs=current_state
|
| 408 |
-
)
|
| 409 |
-
|
| 410 |
-
def on_audio_change(audio_filepath, pdfs, current_text):
|
| 411 |
-
logger.info("on_audio_change: audio_filepath = %s", audio_filepath)
|
| 412 |
-
if not audio_filepath:
|
| 413 |
-
return current_text, "No se proporcionó audio.", current_text
|
| 414 |
-
if not current_text or not current_text.strip():
|
| 415 |
-
current_text = initial_text
|
| 416 |
-
updated_text, debug_info, _ = process_input(audio_filepath, pdfs, current_text)
|
| 417 |
-
return updated_text, debug_info, updated_text
|
| 418 |
|
| 419 |
-
|
| 420 |
-
fn=
|
| 421 |
-
inputs=[
|
| 422 |
-
outputs=
|
| 423 |
)
|
| 424 |
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
current_text = initial_text
|
| 431 |
-
updated_text, debug_info, _ = process_input(audio_filepath, pdfs, current_text)
|
| 432 |
-
return updated_text, debug_info, updated_text
|
| 433 |
-
|
| 434 |
-
pdf_files.upload(
|
| 435 |
-
fn=on_pdfs_change,
|
| 436 |
-
inputs=[audio_filepath, pdf_files, iterative_output],
|
| 437 |
-
outputs=[iterative_output, debug_output, current_state]
|
| 438 |
)
|
| 439 |
|
| 440 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import os
|
|
|
|
|
|
|
|
|
|
| 3 |
import json
|
| 4 |
import logging
|
| 5 |
+
from groq import Groq
|
| 6 |
+
from langchain_groq import ChatGroq
|
| 7 |
+
|
| 8 |
+
# Cargar variables de entorno desde archivo .env
|
| 9 |
+
|
| 10 |
|
| 11 |
# Configuración de logging
|
| 12 |
logging.basicConfig(level=logging.INFO)
|
| 13 |
logger = logging.getLogger(__name__)
|
| 14 |
|
| 15 |
+
# Pesos para el cálculo del RAID
|
| 16 |
RAID_WEIGHTS = {
|
| 17 |
"Dolor": 0.21,
|
| 18 |
"Discapacidad Funcional": 0.16,
|
|
|
|
| 23 |
"Afronte": 0.12
|
| 24 |
}
|
| 25 |
|
| 26 |
+
# Plantilla de prompt para evaluación RAID
|
| 27 |
RAID_PROMPT_TEMPLATE = """
|
| 28 |
+
Analiza la siguiente conversación o texto del paciente con artritis reumatoide.
|
| 29 |
|
| 30 |
+
Si el texto está vacío o no contiene información relevante, responde únicamente con el siguiente mensaje:
|
| 31 |
"No hay información suficiente para calcular el RAID."
|
| 32 |
|
| 33 |
En caso de haber información, extrae y asigna una puntuación del 0 al 10 para cada una de las siguientes categorías, basándote en los detalles proporcionados:
|
| 34 |
|
| 35 |
1. Dolor:
|
| 36 |
+
- Asigne el número que mejor describa el dolor que sintió debido a su Artritis durante la última semana.
|
| 37 |
- Escala: 0 = Ninguno, 10 = Extremo.
|
| 38 |
2. Discapacidad Funcional:
|
| 39 |
+
- Asigne el número que mejor describa la dificultad que tuvo para realizar actividades diarias debido a su Artritis Reumatoidea durante la última semana.
|
| 40 |
- Escala: 0 = Sin dificultad, 10 = Dificultad extrema.
|
| 41 |
3. Fatiga:
|
| 42 |
+
- Asigne el número que mejor describa la fatiga que experimentó debido a su Artritis Reumatoidea durante la última semana.
|
| 43 |
- Escala: 0 = Sin fatiga, 10 = Totalmente exhausto.
|
| 44 |
4. Sueño:
|
| 45 |
+
- Asigne el número que mejor describa las dificultades para dormir que experimentó durante la última semana.
|
| 46 |
- Escala: 0 = Sin dificultad, 10 = Dificultad extrema.
|
| 47 |
5. Bienestar Físico:
|
| 48 |
+
- Asigne el número que mejor describa su nivel de bienestar físico en relación a su Artritis Reumatoidea durante la última semana.
|
| 49 |
- Escala: 0 = Muy bueno, 10 = Muy malo.
|
| 50 |
6. Bienestar Emocional:
|
| 51 |
+
- Asigne el número que mejor describa su nivel de bienestar emocional en relación a su Artritis Reumatoidea durante la última semana.
|
| 52 |
- Escala: 0 = Muy bueno, 10 = Muy malo.
|
| 53 |
7. Afronte:
|
| 54 |
+
- Asigne el número que mejor describa cómo enfrentó o se adaptó a su enfermedad durante la última semana.
|
| 55 |
- Escala: 0 = Muy bien, 10 = Muy mal.
|
| 56 |
|
| 57 |
Utiliza los siguientes pesos para calcular el valor final del RAID:
|
| 58 |
{weights}
|
| 59 |
|
|
|
|
| 60 |
La fórmula de cálculo es:
|
| 61 |
RAID final = (Dolor × 0.21) + (Discapacidad Funcional × 0.16) + (Fatiga × 0.15) + (Bienestar Físico × 0.12) + (Sueño × 0.12) + (Bienestar Emocional × 0.12) + (Afronte × 0.12)
|
| 62 |
|
|
|
|
| 83 |
|
| 84 |
Texto del paciente:
|
| 85 |
{patient_text}
|
|
|
|
|
|
|
|
|
|
| 86 |
"""
|
| 87 |
|
| 88 |
+
# Inicialización del cliente Groq
|
| 89 |
+
# Inicialización del cliente
|
| 90 |
+
api_key = os.environ.get("GROQ_API_KEY")
|
| 91 |
+
if not api_key:
|
| 92 |
+
raise ValueError("GROQ_API_KEY no está configurada en las variables de entorno.")
|
| 93 |
+
client = Groq(api_key=api_key)
|
| 94 |
+
model_name = "llama-3.3-70b-versatile"
|
| 95 |
+
chat_groq = ChatGroq(model=model_name)
|
| 96 |
+
|
| 97 |
|
| 98 |
+
def transcribe_audio(audio_filepath):
|
| 99 |
+
"""Transcribe el audio usando Whisper de Groq"""
|
| 100 |
+
if not audio_filepath:
|
| 101 |
+
return ""
|
| 102 |
try:
|
| 103 |
+
with open(audio_filepath, "rb") as file:
|
| 104 |
+
transcription = client.audio.transcriptions.create(
|
| 105 |
+
file=(audio_filepath, file.read()),
|
| 106 |
+
model="whisper-large-v3",
|
| 107 |
+
response_format="json",
|
| 108 |
+
temperature=0.0
|
| 109 |
+
)
|
| 110 |
+
logger.info("Transcripción de audio exitosa.")
|
| 111 |
+
return transcription.text
|
| 112 |
+
except Exception as e:
|
| 113 |
+
logger.error("Error en transcripción de audio: %s", e, exc_info=True)
|
| 114 |
+
return ""
|
| 115 |
+
|
| 116 |
+
def evaluate_raid(patient_text):
|
| 117 |
+
"""Evalúa el RAID score basado en el texto del paciente"""
|
| 118 |
+
try:
|
| 119 |
+
# Limitar texto para contexto
|
| 120 |
+
if len(patient_text) > 4000:
|
| 121 |
+
logger.warning("Texto demasiado largo, se truncará a 4000 caracteres")
|
| 122 |
+
patient_text = patient_text[:4000]
|
| 123 |
+
|
| 124 |
prompt = RAID_PROMPT_TEMPLATE.format(
|
| 125 |
+
patient_text=patient_text,
|
|
|
|
| 126 |
weights=json.dumps(RAID_WEIGHTS, ensure_ascii=False)
|
| 127 |
)
|
| 128 |
+
logger.info("Evaluando RAID con prompt")
|
| 129 |
|
| 130 |
response = chat_groq.invoke(prompt)
|
| 131 |
content = response.content
|
| 132 |
+
logger.info("Respuesta de chat_groq recibida")
|
| 133 |
|
| 134 |
# Extraer JSON de la respuesta
|
| 135 |
start = content.find('{')
|
|
|
|
| 183 |
return partial_result
|
| 184 |
|
| 185 |
def format_raid_results(raid_data):
|
| 186 |
+
"""Formatea los resultados del RAID para visualización"""
|
|
|
|
| 187 |
if not raid_data:
|
| 188 |
return "No se pudo calcular el RAID score"
|
| 189 |
|
|
|
|
| 216 |
result += f"\n\n**Nota:** Se produjo un error durante el cálculo del RAID: {raid_data['error']}"
|
| 217 |
return result
|
| 218 |
|
| 219 |
+
def process_input(audio, text_input):
|
| 220 |
+
"""Procesa la entrada de audio y texto para calcular el RAID"""
|
| 221 |
+
patient_text = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
|
| 223 |
+
# Procesar audio si está disponible
|
| 224 |
+
if audio:
|
| 225 |
+
transcription = transcribe_audio(audio)
|
| 226 |
+
if transcription:
|
| 227 |
+
patient_text += transcription + "\n\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
|
| 229 |
+
# Añadir texto manual si está disponible
|
| 230 |
+
if text_input:
|
| 231 |
+
patient_text += text_input
|
| 232 |
+
|
| 233 |
+
# Si no hay texto para analizar, retornar mensaje
|
| 234 |
+
if not patient_text.strip():
|
| 235 |
+
return "No se proporcionó texto ni audio para analizar. Por favor, añada información del paciente."
|
| 236 |
+
|
| 237 |
+
# Evaluar RAID con el texto disponible
|
| 238 |
+
raid_results = evaluate_raid(patient_text)
|
| 239 |
+
formatted_results = format_raid_results(raid_results)
|
| 240 |
+
|
| 241 |
+
return formatted_results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
|
| 243 |
# Configuración del tema
|
| 244 |
theme = gr.themes.Base(
|
|
|
|
| 252 |
neutral_hue="neutral",
|
| 253 |
)
|
| 254 |
|
| 255 |
+
# Interfaz de Gradio simple para el cálculo de RAID
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 256 |
with gr.Blocks(theme=theme) as iface:
|
| 257 |
+
gr.Markdown("# Calculadora RAID para Artritis Reumatoide")
|
| 258 |
|
| 259 |
with gr.Row():
|
| 260 |
+
with gr.Column():
|
| 261 |
+
audio_input = gr.Audio(
|
| 262 |
+
sources=["microphone", "upload"],
|
| 263 |
+
type="filepath",
|
| 264 |
+
label="Audio del paciente"
|
| 265 |
+
)
|
| 266 |
+
text_input = gr.Textbox(
|
| 267 |
+
label="Texto del paciente (opcional)",
|
| 268 |
+
placeholder="Ingrese aquí cualquier información adicional del paciente...",
|
| 269 |
+
lines=5
|
| 270 |
+
)
|
| 271 |
+
submit_btn = gr.Button("Calcular RAID", variant="primary")
|
| 272 |
|
| 273 |
+
with gr.Column():
|
| 274 |
+
raid_output = gr.Markdown(
|
| 275 |
+
"### Resultados RAID\n\nEspere mientras se procesa la información...",
|
| 276 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
|
| 278 |
+
submit_btn.click(
|
| 279 |
+
fn=process_input,
|
| 280 |
+
inputs=[audio_input, text_input],
|
| 281 |
+
outputs=raid_output
|
| 282 |
)
|
| 283 |
|
| 284 |
+
# También calcular automáticamente cuando se cambia el audio
|
| 285 |
+
audio_input.change(
|
| 286 |
+
fn=process_input,
|
| 287 |
+
inputs=[audio_input, text_input],
|
| 288 |
+
outputs=raid_output
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
)
|
| 290 |
|
| 291 |
+
if __name__ == "__main__":
|
| 292 |
+
iface.launch()
|
| 293 |
+
|
| 294 |
+
|