Spaces:
Runtime error
Runtime error
| # 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 | |
| ) |