tufonoayuda's picture
Update app.py
22c150d verified
import gradio as gr
import os
import re
import json
import requests
from pypdf import PdfReader
# ✅ Configurar API Key de OpenRouter desde variables de entorno
OPENROUTER_API_KEY = os.getenv("sk-OsMMq65tXdfOIlTUYtocSL7NCsmA7CerN77OkEv29dODg1EA")
if not OPENROUTER_API_KEY:
raise ValueError("❌ OPENROUTER_API_KEY no está configurada. Ve a Settings > Variables de entorno en tu Space.")
OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions"
# ✅ Modelo gratuito y potente: DeepSeek R1
MODEL = "deepseek/deepseek-r1-0528-qwen3-8b:free"
def extract_text_from_pdf(pdf_file):
"""Extrae texto de un archivo PDF"""
try:
reader = PdfReader(pdf_file)
text = ""
for page in reader.pages:
extracted = page.extract_text()
if extracted:
text += extracted + "\n"
return text[:5000] # Limitar para no exceder tokens
except Exception as e:
return f"Error al leer PDF: {str(e)}"
def generate_smart_objective(objective, age, duration):
"""Genera un objetivo SMART completo y detallado"""
age_group = 'preescolar' if age < 36 else 'escolar' if age < 144 else 'adolescente/adulto'
time_frame = 'corto plazo' if duration < 30 else 'mediano plazo' if duration < 60 else 'largo plazo'
return f"El paciente {age_group} logrará {objective} con un 80% de precisión durante {duration} minutos, utilizando apoyo visual/auditivo según necesidad, medible a través de registro de respuestas correctas en {time_frame}."
def call_openrouter_api(prompt):
"""Llama a la API de OpenRouter"""
headers = {
"Authorization": f"Bearer {OPENROUTER_API_KEY}",
"Content-Type": "application/json",
"HTTP-Referer": "https://tufonoayuda-pixel.github.io/ActFonoGenerator/", # Tu URL
"X-Title": "Generador IA de Actividades Fonoaudiológicas"
}
data = {
"model": MODEL,
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.3,
"max_tokens": 4096
}
try:
response = requests.post(OPENROUTER_API_URL, headers=headers, json=data, timeout=60)
response.raise_for_status()
result = response.json()
return result["choices"][0]["message"]["content"]
except Exception as e:
raise Exception(f"Error al llamar a OpenRouter: {str(e)}")
def generate_activity(user_desc, objective, duration, session_type, is_pediatric, context, pdf_files):
"""Genera una actividad terapéutica COMPLETA y DETALLADA con IA real de OpenRouter"""
if not all([user_desc, objective, duration]):
return ("⚠️ Error", "Por favor completa todos los campos obligatorios.", "", "", "", "", "", "")
try:
# Extraer edad
age = int(re.search(r'\d+', user_desc).group()) if re.search(r'\d+', user_desc) else 60
is_child = age < 144 or is_pediatric
dur = int(duration)
# Procesar PDFs
pdf_text = ""
if pdf_files:
for pdf_file in pdf_files:
pdf_text += f"\n--- Contenido de {pdf_file.name} ---\n"
pdf_text += extract_text_from_pdf(pdf_file)
# ✅ Construir prompt detallado para OpenRouter
prompt = f"""
Eres un fonoaudiólogo experto con 20 años de experiencia clínica. Tu tarea es generar una ACTIVIDAD TERAPÉUTICA COMPLETA, DETALLADA Y LISTA PARA IMPLEMENTAR, basada en evidencia científica y buenas prácticas clínicas.
PACIENTE: {user_desc}
OBJETIVO TERAPÉUTICO: {objective}
DURACIÓN DE LA SESIÓN: {duration} minutos
TIPO DE SESIÓN: {session_type}
¿SESIÓN PEDIÁTRICA?: {'Sí, usar lenguaje lúdico y adaptado' if is_child else 'No, lenguaje profesional y técnico'}
CONTEXTO ADICIONAL: {context or 'Ninguno'}
{f'REFERENCIAS CIENTÍFICAS (usa esta información para fundamentar): {pdf_text}' if pdf_text else ''}
INSTRUCCIONES ESPECÍFICAS PARA LA ACTIVIDAD:
1. TÍTULO: Crea un título atractivo, profesional y descriptivo que refleje el contenido de la actividad.
2. OBJETIVO SMART: Formula un objetivo terapéutico específico, medible, alcanzable, relevante y con tiempo definido. Debe ser una oración completa y detallada.
3. DESCRIPCIÓN GENERAL: Escribe un párrafo completo (mínimo 5-7 oraciones) que describa la actividad, su propósito, población objetivo, y cómo se relaciona con el objetivo terapéutico.
4. MATERIALES NECESARIOS: Lista todos los materiales requeridos con descripciones específicas y detalladas. No uses viñetas sueltas, escribe oraciones completas.
5. PROCEDIMIENTO PASO A PASO: Describe detalladamente las tres fases de la actividad (calentamiento, desarrollo, cierre). Para cada fase:
- Nombre de la fase
- Duración estimada
- Instrucciones verbales exactas que debe dar el terapeuta
- Actividades específicas que realizará el paciente
- Posibles variaciones o adaptaciones durante la actividad
- Señales de progreso o dificultad a observar
6. EVALUACIÓN: Define:
- Criterios de logro específicos y medibles
- Métodos de evaluación cuantitativos y cualitativos
- Cómo se registrará el progreso
- Cómo se dará retroalimentación al paciente/familia
7. ADAPTACIONES: Sugiere adaptaciones específicas para:
- Diferentes niveles de habilidad
- Contextos (clínico, domiciliario, educativo)
- Características individuales del paciente
- Disponibilidad de recursos
8. FUNDAMENTACIÓN TEÓRICA: Explica brevemente (2-3 párrafos completos) en qué teorías, modelos o enfoques se basa la actividad. Cita autores o referencias cuando sea posible.
FORMATO DE RESPUESTA OBLIGATORIO (JSON con estas claves):
{{
"title": "string (título completo y descriptivo)",
"smart_objective": "string (oración completa y detallada)",
"description": "string (párrafo completo de 5-7 oraciones mínimo)",
"materials": "string (párrafo detallado describiendo todos los materiales)",
"procedure": [
{{
"name": "string (nombre de la fase)",
"time": number (duración en minutos),
"instructions": "string (instrucciones verbales exactas del terapeuta)",
"activities": "string (descripción detallada de las actividades del paciente)",
"variations": "string (posibles variaciones o adaptaciones)",
"progress_indicators": "string (señales de progreso o dificultad a observar)"
}}
],
"evaluation": {{
"criteria": "string (criterios de logro específicos y medibles)",
"methods": "string (métodos de evaluación cuantitativos y cualitativos)",
"recording": "string (cómo se registrará el progreso)",
"feedback": "string (cómo se dará retroalimentación al paciente/familia)"
}},
"adaptations": "string (párrafo completo describiendo todas las adaptaciones necesarias)",
"theoretical_foundation": "string (2-3 párrafos completos con fundamentación teórica)"
}}
IMPORTANTE: TODAS las respuestas deben ser TEXTOS COMPLETOS, NO viñetas sueltas ni palabras aisladas. Usa lenguaje profesional, claro y detallado. La actividad debe ser práctica, realista y lista para implementar en una sesión clínica real.
"""
# ✅ Llamar a OpenRouter API
content = call_openrouter_api(prompt)
# Limpiar y parsear JSON
if content.startswith("```json"):
content = content[7:]
if content.endswith("```"):
content = content[:-3]
data = json.loads(content)
# ✅ Retornar resultados completos
return (
data.get("title", f"Actividad Terapéutica para {objective}"),
data.get("smart_objective", generate_smart_objective(objective, age, dur)),
data.get("description", "Descripción detallada generada por IA no disponible."),
data.get("materials", "Lista de materiales no disponible."),
"\n\n".join([
f"FASE: {p.get('name', 'Fase no especificada')} ({p.get('time', 0)} minutos)\n"
f"INSTRUCCIONES DEL TERAPEUTA: {p.get('instructions', 'No especificadas')}\n"
f"ACTIVIDADES DEL PACIENTE: {p.get('activities', 'No especificadas')}\n"
f"VARIACIONES: {p.get('variations', 'No especificadas')}\n"
f"INDICADORES DE PROGRESO: {p.get('progress_indicators', 'No especificados')}"
for p in data.get("procedure", [])
]),
f"CRITERIOS DE LOGRO: {data.get('evaluation', {}).get('criteria', 'No especificados')}\n\n"
f"MÉTODOS DE EVALUACIÓN: {data.get('evaluation', {}).get('methods', 'No especificados')}\n\n"
f"REGISTRO DE PROGRESO: {data.get('evaluation', {}).get('recording', 'No especificado')}\n\n"
f"RETROALIMENTACIÓN: {data.get('evaluation', {}).get('feedback', 'No especificada')}",
data.get("adaptations", "Adaptaciones no disponibles."),
data.get("theoretical_foundation", "Fundamentación teórica no disponible.")
)
except Exception as e:
# ✅ Mostrar error real
return (
"❌ Error al generar con IA",
f"Error: {str(e)}",
"La IA no pudo generar una respuesta completa. Esto puede deberse a:\n"
"• El prompt es demasiado largo o complejo\n"
"• Problemas temporales con la API de OpenRouter\n"
"• Tu API Key no es válida o ha excedido límites\n\n"
"Sugerencias:\n"
"• Intenta con una descripción más concisa\n"
"• Reduce la cantidad de PDFs o contexto adicional\n"
"• Intenta nuevamente en unos minutos",
"",
"",
"",
"",
""
)
# ✅ Interfaz de Gradio
with gr.Blocks(title="🧠 Generador IA de Actividades Fonoaudiológicas") as demo:
gr.Markdown("# 🧠 Generador IA de Actividades Fonoaudiológicas")
gr.Markdown("### ✨ Potenciado con DeepSeek R1 en OpenRouter • Creado por Flgo. Cristóbal San Martín [@tufonoayuda](https://instagram.com/tufonoayuda)")
with gr.Row():
with gr.Column():
user_desc = gr.Textbox(label="👤 Descripción del usuario (edad en meses y contexto)", placeholder="Ej: Niño de 48 meses con dislalia funcional, buen nivel cognitivo pero con dificultades en la articulación de fonemas líquidos", lines=3)
objective = gr.Textbox(label="🎯 Objetivo específico", placeholder="Ej: Mejorar la articulación del fonema /r/ en posición inicial de palabras bisílabas en contexto estructurado con 80% de precisión", lines=2)
duration = gr.Number(label="⏱️ Duración (minutos)", value=30, minimum=15, maximum=120)
session_type = gr.Dropdown(["individual", "grupal", "hogar"], label="👥 Tipo de sesión", value="individual")
is_pediatric = gr.Checkbox(label="🧸 Sesión Pediátrica (lenguaje lúdico)")
context = gr.Textbox(label="📚 Contexto Adicional (Opcional - Sé lo más específico posible)", placeholder="Ej: El niño responde bien a refuerzos visuales, tiene interés por los dinosaurios, la familia puede reforzar en casa 10 minutos diarios, se ha intentado terapia miofuncional sin éxito...", lines=4)
pdf_files = gr.File(label="📖 Subir PDFs de Referencia (Opcional)", file_types=[".pdf"], file_count="multiple")
btn = gr.Button("✨ Generar Actividad con IA", variant="primary")
with gr.Column():
title = gr.Textbox(label="Título de la Actividad", lines=2)
smart_obj = gr.Textbox(label="📋 Objetivo SMART", lines=3)
description = gr.Textbox(label="📝 Descripción General", lines=6)
materials = gr.Textbox(label="🎯 Materiales Necesarios", lines=4)
procedure = gr.Textbox(label="⚡ Procedimiento Paso a Paso (Instrucciones completas)", lines=10)
evaluation = gr.Textbox(label="📊 Evaluación (Criterios, métodos, registro y retroalimentación)", lines=6)
adaptations = gr.Textbox(label="🔧 Adaptaciones (Párrafo completo)", lines=4)
theory = gr.Textbox(label="📚 Fundamentación Teórica (2-3 párrafos completos)", lines=6)
# Conectar botón
btn.click(
fn=generate_activity,
inputs=[user_desc, objective, duration, session_type, is_pediatric, context, pdf_files],
outputs=[title, smart_obj, description, materials, procedure, evaluation, adaptations, theory]
)
# Lanzar app
if __name__ == "__main__":
demo.launch()