GeneratorIASLP / app.py
tufonoayuda's picture
Update app.py
0568fd0 verified
# app.py - Migración completa del Generador Fonoaudiológico a Gradio/HF
import gradio as gr
import requests
import json
from datetime import datetime
import PyPDF2
import io
from huggingface_hub import InferenceClient
import os
import re
# Configuración de modelos de Hugging Face (gratuitos)
MODEL_OPTIONS = {
"Llama 3.2 (Recomendado)": "meta-llama/Llama-3.2-1B-Instruct",
"Mistral 7B": "mistralai/Mistral-7B-Instruct-v0.1",
"Flan-T5 Large": "google/flan-t5-large",
"Phi-3 Mini": "microsoft/Phi-3-mini-4k-instruct"
}
# Cliente de inferencia HF
client = InferenceClient()
def extraer_edad_de_descripcion(descripcion):
"""Extrae la edad en meses de la descripción del usuario"""
# Buscar patrones como "48 meses", "4 años", "3a 6m", etc.
meses_match = re.search(r'(\d+)\s*meses?', descripcion.lower())
if meses_match:
return int(meses_match.group(1))
anos_match = re.search(r'(\d+)\s*años?', descripcion.lower())
if anos_match:
return int(anos_match.group(1)) * 12
# Buscar patrón "3a 6m" o similar
anos_meses_match = re.search(r'(\d+)a\s*(\d+)m', descripcion.lower())
if anos_meses_match:
anos = int(anos_meses_match.group(1))
meses = int(anos_meses_match.group(2))
return (anos * 12) + meses
# Por defecto, asumir que cualquier número es edad en meses
numero_match = re.search(r'\d+', descripcion)
if numero_match:
return int(numero_match.group())
return 60 # Default 5 años
def extract_text_from_pdf(pdf_files):
"""Extrae texto de archivos PDF subidos"""
if not pdf_files:
return ""
extracted_texts = []
for pdf_file in pdf_files:
try:
with open(pdf_file.name, 'rb') as file:
pdf_reader = PyPDF2.PdfReader(file)
text = ""
for page in pdf_reader.pages:
text += page.extract_text() + "\n"
# Limitar texto para evitar exceso de tokens
text_preview = text[:800] + "..." if len(text) > 800 else text
extracted_texts.append(f"📄 **{pdf_file.name}**:\n{text_preview}\n")
except Exception as e:
extracted_texts.append(f"❌ Error leyendo {pdf_file.name}: {str(e)}\n")
return "\n".join(extracted_texts)
def generar_actividad_fonoaudiologica(
modelo_seleccionado,
descripcion_usuario,
es_pediatrica,
objetivo_especifico,
duracion,
tipo_sesion,
contexto_adicional,
archivos_pdf,
progress=gr.Progress()
):
"""Función principal que genera la actividad fonoaudiológica"""
# Validaciones (igual que en el JS original)
if not descripcion_usuario or not descripcion_usuario.strip():
return "⚠️ **Error**: Por favor ingresa la descripción del usuario"
if not objetivo_especifico or not objetivo_especifico.strip():
return "⚠️ **Error**: Por favor ingresa el objetivo específico"
if not duracion or duracion < 15:
return "⚠️ **Error**: La duración debe ser de al menos 15 minutos"
# Progress bar
progress(0.1, "Analizando información del paciente...")
# Extraer edad de la descripción (lógica del JS original)
edad_meses = extraer_edad_de_descripcion(descripcion_usuario)
es_nino = edad_meses < 144 or es_pediatrica # < 12 años
progress(0.3, "Procesando referencias científicas...")
# Extraer texto de PDFs
referencias_pdf = extract_text_from_pdf(archivos_pdf) if archivos_pdf else ""
progress(0.5, "Generando actividad con IA...")
# Construir prompt detallado (basado en el JS original)
prompt = f"""Eres un fonoaudiólogo experto especializado en terapia del habla y lenguaje.
Genera una actividad terapéutica profesional y detallada con esta información:
INFORMACIÓN DEL PACIENTE:
- Descripción: {descripcion_usuario}
- Edad estimada: {edad_meses} meses ({edad_meses//12} años y {edad_meses%12} meses)
- Tipo de sesión: {'Pediátrica - usar lenguaje lúdico y juegos' if es_pediatrica else 'Adulto/Adolescente - lenguaje profesional'}
OBJETIVO TERAPÉUTICO:
- Objetivo específico: {objetivo_especifico}
- Duración de la sesión: {duracion} minutos
- Modalidad: {tipo_sesion}
CONTEXTO ADICIONAL:
{contexto_adicional if contexto_adicional else 'No se proporcionó contexto adicional'}
REFERENCIAS CIENTÍFICAS:
{referencias_pdf[:600] if referencias_pdf else 'Sin referencias adicionales proporcionadas'}
INSTRUCCIONES PARA GENERAR LA ACTIVIDAD:
1. **TÍTULO**: Crea un título específico y atractivo {'con emojis y lenguaje lúdico' if es_nino else 'profesional'}
2. **OBJETIVO SMART**: Reformula el objetivo usando criterios SMART (Específico, Medible, Alcanzable, Relevante, Temporal)
3. **DESCRIPCIÓN GENERAL**: {'Lenguaje amigable y motivador para el niño' if es_nino else 'Descripción técnica profesional'}
4. **MATERIALES**: Lista específica de materiales necesarios {'incluyendo juguetes y elementos lúdicos' if es_nino else 'con enfoque terapéutico profesional'}
5. **PROCEDIMIENTO PASO A PASO**: Divide en fases con tiempos estimados:
- Fase inicial: {int(duracion * 0.2)} minutos {'(Calentamiento con juegos)' if es_nino else '(Preparación)'}
- Fase principal: {int(duracion * 0.6)} minutos {'(Actividad principal divertida)' if es_nino else '(Desarrollo de la técnica)'}
- Fase final: {int(duracion * 0.2)} minutos {'(Celebración de logros)' if es_nino else '(Síntesis y cierre)'}
6. **EVALUACIÓN**:
- Criterios de logro {'con sistema de recompensas' if es_nino else 'objetivos y cuantificables'}
- Métodos de evaluación apropiados para la edad
- Tipo de retroalimentación
7. **ADAPTACIONES**: Sugerencias para diferentes niveles o necesidades especiales
8. **FUNDAMENTACIÓN TEÓRICA**: Base científica de la actividad (cuando sea apropiado)
Responde en español, con formato claro y estructurado. {'Usa emojis y lenguaje motivador' if es_nino else 'Mantén un tono profesional'}."""
try:
progress(0.7, "Conectando con modelo de IA...")
# Seleccionar modelo
model_name = MODEL_OPTIONS.get(modelo_seleccionado, MODEL_OPTIONS["Flan-T5 Large"])
# Generar respuesta con Hugging Face
response = client.text_generation(
prompt,
model=model_name,
max_new_tokens=1500,
temperature=0.7,
repetition_penalty=1.1,
do_sample=True
)
progress(0.9, "Formateando respuesta...")
# Formatear respuesta final (similar al renderizado del JS)
actividad_generada = f"""# 🧠 ACTIVIDAD FONOAUDIOLÓGICA GENERADA
**📅 Fecha de generación:** {datetime.now().strftime("%d/%m/%Y a las %H:%M")}
**👤 Paciente:** {edad_meses//12} años y {edad_meses%12} meses ({edad_meses} meses total)
**🤖 Modelo IA:** {modelo_seleccionado}
**⏱️ Duración:** {duracion} minutos | **👥 Modalidad:** {tipo_sesion}
---
{response}
---
## 📊 RESUMEN EJECUTIVO
**🎯 Enfoque de la sesión:** {'🧸 Pediátrico con metodología lúdica' if es_pediatrica else '👩‍⚕️ Clínico profesional'}
**📋 Información procesada:**
- ✅ Descripción del usuario analizada
- ✅ Objetivo específico integrado
- ✅ Duración y modalidad consideradas
{'- ✅ Referencias científicas incorporadas' if referencias_pdf else '- ⚪ Sin referencias adicionales'}
**⚠️ NOTA PROFESIONAL IMPORTANTE:**
*Esta actividad ha sido generada por Inteligencia Artificial basándose en la información proporcionada. Como profesional de la fonoaudiología, es fundamental que revises, adaptes y valides la pertinencia clínica de esta propuesta antes de implementarla con pacientes reales. La IA es una herramienta de apoyo, pero el criterio profesional y la evaluación clínica directa son insustituibles.*
---
## 👨‍⚕️ INFORMACIÓN DEL DESARROLLADOR
**Desarrollado por:** Flgo. Cristóbal San Martín
**Instagram:** [@tufonoayuda](https://instagram.com/tufonoayuda)
**Tecnología:** Hugging Face + Gradio + IA Generativa
*Herramienta diseñada específicamente para profesionales de la fonoaudiología en Chile y Latinoamérica.*"""
progress(1.0, "¡Actividad generada exitosamente!")
return actividad_generada
except Exception as e:
error_msg = f"""❌ **ERROR AL GENERAR LA ACTIVIDAD**
**Detalles del error:** {str(e)}
**Posibles soluciones:**
1. **Intenta con otro modelo:** Cambia el modelo de IA seleccionado
2. **Simplifica el contexto:** Reduce el texto en "Contexto adicional"
3. **Verifica los PDFs:** Asegúrate de que los archivos no estén dañados
4. **Revisa la conexión:** Problemas temporales con Hugging Face
**Información técnica:**
- Modelo utilizado: {modelo_seleccionado}
- Timestamp: {datetime.now().strftime("%d/%m/%Y %H:%M:%S")}
- Duración solicitada: {duracion} min
- Modalidad: {tipo_sesion}
*Si el problema persiste, intenta nuevamente en unos minutos.*"""
return error_msg
# Crear la interfaz principal de Gradio
def crear_interfaz():
with gr.Blocks(
title="🧠 Generador IA Fonoaudiológico - HF Spaces",
theme=gr.themes.Soft(
primary_hue="indigo",
secondary_hue="blue",
neutral_hue="slate"
),
css="""
.gradio-container {
max-width: 1400px !important;
}
.highlight-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 25px;
border-radius: 15px;
color: white;
text-align: center;
margin-bottom: 25px;
}
.form-section {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 20px;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.result-area {
background: #f8f9fa;
border-radius: 10px;
padding: 15px;
}
"""
) as demo:
# Header con el mismo estilo que tu app original
gr.HTML("""
<div class="highlight-header">
<h1 style="margin: 0; font-size: 2.5em;">🧠 Generador IA de Actividades Fonoaudiológicas</h1>
<p style="margin: 15px 0 5px 0; font-size: 1.2em;">✨ Potenciado con Hugging Face para Fonoaudiólog@s</p>
<p style="margin: 0; opacity: 0.9;">Migración completa desde tu app original - Todas las funcionalidades incluidas</p>
</div>
""")
with gr.Row():
# Columna izquierda - Formulario (replica tu diseño original)
with gr.Column(scale=1, elem_classes=["form-section"]):
# Selección de modelo IA
gr.Markdown("### 🤖 Configuración del Modelo IA")
modelo_ia = gr.Dropdown(
label="Modelo de Lenguaje",
choices=list(MODEL_OPTIONS.keys()),
value="Flan-T5 Large",
info="💡 Llama 3.2 da mejores resultados pero es más lento"
)
gr.Markdown("### 👤 Información del Usuario")
descripcion_usuario = gr.Textbox(
label="Descripción del usuario (edad en meses y contexto)",
placeholder="Ej: Niño de 48 meses con retraso en el desarrollo del lenguaje expresivo, comprende órdenes simples pero presenta dificultades en la articulación de fonemas líquidos",
lines=3,
info="💡 Incluye edad en meses/años y características principales"
)
es_pediatrica = gr.Checkbox(
label="🎯 Sesión Pediátrica (lenguaje lúdico adaptado)",
value=True,
info="Activa para usar metodología lúdica y lenguaje motivador"
)
gr.Markdown("### 🎯 Objetivo y Configuración de Sesión")
objetivo_especifico = gr.Textbox(
label="Objetivo específico (basado en Taxonomía de Bloom)",
placeholder="Ej: Mejorar la articulación del fonema /r/ en palabras bisílabas mediante ejercicios de repetición y modelado",
lines=2,
info="📚 Niveles Bloom: Recordar → Comprender → Aplicar → Analizar → Evaluar → Crear"
)
with gr.Row():
duracion = gr.Slider(
label="⏱️ Duración (minutos)",
minimum=15,
maximum=120,
value=45,
step=5,
info="Tiempo total de la sesión terapéutica"
)
tipo_sesion = gr.Dropdown(
label="👥 Tipo de sesión",
choices=["Individual", "Grupal", "Para el hogar", "Mixta"],
value="Individual"
)
gr.Markdown("### 📚 Contexto Adicional (Expandible)")
contexto_adicional = gr.Textbox(
label="Información adicional detallada",
placeholder="Estrategias específicas, materiales preferidos, observaciones clínicas, preferencias del paciente, historia clínica relevante...",
lines=6,
info="💡 Incluye estrategias como: Terapia Miofuncional, Bobath, Prompt, MIT, LSVT, VitalStim"
)
gr.Markdown("### 📄 Referencias Científicas (Opcional)")
archivos_pdf = gr.File(
label="Subir archivos PDF",
file_count="multiple",
file_types=[".pdf"],
info="📚 Arrastra archivos PDF aquí o selecciona - máximo 50MB por archivo"
)
# Botón principal (mismo diseño que tu app)
generar_btn = gr.Button(
"✨ Generar Actividad con IA",
variant="primary",
size="lg",
elem_id="generate-button"
)
# Columna derecha - Resultado
with gr.Column(scale=1):
gr.Markdown("### 📋 Actividad Lista para Generar")
resultado_actividad = gr.Textbox(
label="",
placeholder="""🧠 Actividad Lista para Generar
Completa los campos de la izquierda y haz clic en "Generar Actividad".
La IA creará una actividad personalizada que incluirá:
• 🎯 Título específico y atractivo
• 📝 Objetivo SMART reformulado
• 🎨 Lista de materiales necesarios
• ⚡ Procedimiento paso a paso con tiempos
• 📊 Criterios de evaluación
• 🔧 Adaptaciones sugeridas
• 📚 Fundamentación teórica
¡Tu actividad fonoaudiológica personalizada estará lista en segundos!""",
lines=35,
max_lines=40,
show_copy_button=True,
elem_classes=["result-area"]
)
# Conectar la funcionalidad (igual que tu addEventListener original)
generar_btn.click(
fn=generar_actividad_fonoaudiologica,
inputs=[
modelo_ia,
descripcion_usuario,
es_pediatrica,
objetivo_especifico,
duracion,
tipo_sesion,
contexto_adicional,
archivos_pdf
],
outputs=resultado_actividad,
show_progress=True
)
# Footer idéntico al tuyo
gr.HTML("""
<div style="margin-top: 40px; padding: 25px; background: #f8f9fa; border-radius: 15px; text-align: center;">
<div style="display: flex; justify-content: center; align-items: center; gap: 30px; flex-wrap: wrap; margin-bottom: 20px;">
<div>
<div style="font-size: 24px; margin-bottom: 5px;">👨‍⚕️</div>
<h4 style="margin: 0; color: #2563eb;">Desarrollado por</h4>
<p style="margin: 5px 0 0 0;"><strong>Flgo. Cristóbal San Martín</strong></p>
</div>
<div>
<div style="font-size: 24px; margin-bottom: 5px;">📱</div>
<h4 style="margin: 0; color: #2563eb;">Sígueme en</h4>
<p style="margin: 5px 0 0 0;"><a href="https://instagram.com/tufonoayuda" target="_blank" style="color: #dc2626; text-decoration: none; font-weight: bold;">@tufonoayuda</a></p>
</div>
<div>
<div style="font-size: 24px; margin-bottom: 5px;">✨</div>
<h4 style="margin: 0; color: #2563eb;">Tecnología</h4>
<p style="margin: 5px 0 0 0;">Hugging Face Spaces</p>
</div>
</div>
<div style="border-top: 2px solid #e5e7eb; padding-top: 20px;">
<p style="margin: 0; font-size: 0.95em; color: #6b7280; line-height: 1.5;">
<strong style="color: #dc2626;">⚠️ Nota Profesional:</strong>
Esta herramienta genera actividades basadas en IA como apoyo al trabajo profesional.
<strong>Siempre revisa y adapta las actividades según tu criterio clínico</strong> antes de implementar con pacientes.
</p>
<p style="margin: 10px 0 0 0; font-size: 0.9em; color: #9ca3af;">
🚀 Migrado desde tu aplicación original • Todas las funcionalidades preservadas • Optimizado para Hugging Face
</p>
</div>
</div>
""")
return demo
# Ejecutar aplicación
if __name__ == "__main__":
demo = crear_interfaz()
demo.launch(
share=True,
server_name="0.0.0.0",
show_error=True,
favicon_path=None,
show_tips=True
)