tufonoayuda commited on
Commit
f41fa1f
·
verified ·
1 Parent(s): e2594a1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -75
app.py CHANGED
@@ -5,12 +5,17 @@ import json
5
  import requests
6
  from pypdf import PdfReader
7
 
8
- # ✅ Obtener API Key (con fallback para diagnóstico)
9
- DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
10
- DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"
11
- MODEL = "deepseek-chat"
 
 
 
 
12
 
13
  def extract_text_from_pdf(pdf_file):
 
14
  try:
15
  reader = PdfReader(pdf_file)
16
  text = ""
@@ -18,22 +23,23 @@ def extract_text_from_pdf(pdf_file):
18
  extracted = page.extract_text()
19
  if extracted:
20
  text += extracted + "\n"
21
- return text[:5000]
22
  except Exception as e:
23
  return f"Error al leer PDF: {str(e)}"
24
 
25
  def generate_smart_objective(objective, age, duration):
 
26
  age_group = 'preescolar' if age < 36 else 'escolar' if age < 144 else 'adolescente/adulto'
27
  time_frame = 'corto plazo' if duration < 30 else 'mediano plazo' if duration < 60 else 'largo plazo'
28
- return f"El paciente {age_group} logrará {objective} con un 80% de precisión durante {duration} minutos."
29
 
30
- def call_deepseek_api(prompt):
31
- if not DEEPSEEK_API_KEY:
32
- raise Exception("❌ DEEPSEEK_API_KEY no está configurada. Revisa Settings > Variables de entorno.")
33
-
34
  headers = {
35
- "Authorization": f"Bearer {DEEPSEEK_API_KEY}",
36
- "Content-Type": "application/json"
 
 
37
  }
38
 
39
  data = {
@@ -44,115 +50,191 @@ def call_deepseek_api(prompt):
44
  }
45
 
46
  try:
47
- response = requests.post(DEEPSEEK_API_URL, headers=headers, json=data, timeout=60)
48
  response.raise_for_status()
49
  result = response.json()
50
  return result["choices"][0]["message"]["content"]
51
  except Exception as e:
52
- raise Exception(f"Error de la IA: {str(e)}")
53
 
54
  def generate_activity(user_desc, objective, duration, session_type, is_pediatric, context, pdf_files):
 
55
  if not all([user_desc, objective, duration]):
56
- return ("⚠️ Error", "Completa todos los campos.", "", "", "", "", "", "")
57
 
58
  try:
 
59
  age = int(re.search(r'\d+', user_desc).group()) if re.search(r'\d+', user_desc) else 60
60
  is_child = age < 144 or is_pediatric
61
  dur = int(duration)
62
 
 
63
  pdf_text = ""
64
  if pdf_files:
65
  for pdf_file in pdf_files:
66
- pdf_text += f"\n--- {pdf_file.name} ---\n"
67
  pdf_text += extract_text_from_pdf(pdf_file)
68
 
 
69
  prompt = f"""
70
- Eres un fonoaudiólogo experto. Genera una actividad para:
 
71
  PACIENTE: {user_desc}
72
- OBJETIVO: {objective}
73
- DURACIÓN: {duration} minutos
74
  TIPO DE SESIÓN: {session_type}
75
- SESIÓN PEDIÁTRICA: {'Sí' if is_child else 'No'}
76
- CONTEXTO: {context or 'Ninguno'}
77
- REFERENCIAS: {pdf_text[:1000] if pdf_text else 'Ninguna'}
78
-
79
- RESPONDE EN FORMATO JSON: {{"title": "", "smart_objective": "", "description": "", "materials": "", "procedure": "", "evaluation": "", "adaptations": "", "theoretical_foundation": ""}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  """
81
 
82
- content = call_deepseek_api(prompt)
83
-
84
- # Modo fallback: si falla el JSON, devuelve texto plano
85
- try:
86
- if content.startswith("```json"):
87
- content = content[7:]
88
- if content.endswith("```"):
89
- content = content[:-3]
90
- data = json.loads(content)
91
- except:
92
- return (
93
- "Actividad Generada",
94
- "Objetivo generado por IA",
95
- content,
96
- "Materiales generados por IA",
97
- "Procedimiento generado por IA",
98
- "Evaluación generada por IA",
99
- "Adaptaciones generadas por IA",
100
- "Fundamentación generada por IA"
101
- )
102
 
 
 
 
 
 
 
 
 
 
103
  return (
104
- data.get("title", "Actividad Generada"),
105
  data.get("smart_objective", generate_smart_objective(objective, age, dur)),
106
- data.get("description", "Descripción generada por IA."),
107
- data.get("materials", "Materiales generados por IA."),
108
- data.get("procedure", "Procedimiento generado por IA."),
109
- data.get("evaluation", "Evaluación generada por IA."),
110
- data.get("adaptations", "Adaptaciones generadas por IA."),
111
- data.get("theoretical_foundation", "Fundamentación generada por IA.")
 
 
 
 
 
 
 
 
 
 
112
  )
113
 
114
  except Exception as e:
 
115
  return (
116
- "❌ Error",
117
- str(e),
118
- "Por favor verifica tu API Key en Settings > Variables de entorno y reinicia el Space.",
119
- "Si el problema persiste, contacta al desarrollador.",
 
 
 
 
 
 
 
120
  "",
121
  "",
122
  "",
123
  ""
124
  )
125
 
126
- with gr.Blocks() as demo:
 
127
  gr.Markdown("# 🧠 Generador IA de Actividades Fonoaudiológicas")
 
128
 
129
  with gr.Row():
130
  with gr.Column():
131
- user_desc = gr.Textbox(label="👤 Descripción del usuario", placeholder="Ej: Niño de 48 meses...")
132
- objective = gr.Textbox(label="🎯 Objetivo específico")
133
- duration = gr.Number(label="⏱️ Duración (minutos)", value=30)
134
- session_type = gr.Dropdown(["individual", "grupal", "hogar"], label="👥 Tipo de sesión")
135
- is_pediatric = gr.Checkbox(label="🧸 Sesión Pediátrica")
136
- context = gr.Textbox(label="📚 Contexto Adicional", lines=3)
137
- pdf_files = gr.File(label="📖 Subir PDFs", file_types=[".pdf"], file_count="multiple")
138
- btn = gr.Button("✨ Generar Actividad con IA")
139
 
140
  with gr.Column():
141
- title = gr.Textbox(label="Título")
142
- smart_obj = gr.Textbox(label="📋 Objetivo SMART")
143
- description = gr.Textbox(label="📝 Descripción", lines=3)
144
- materials = gr.Textbox(label="🎯 Materiales", lines=3)
145
- procedure = gr.Textbox(label="⚡ Procedimiento", lines=3)
146
- evaluation = gr.Textbox(label="📊 Evaluación", lines=3)
147
- adaptations = gr.Textbox(label="🔧 Adaptaciones", lines=3)
148
- theory = gr.Textbox(label="📚 Fundamentación Teórica", lines=3)
149
 
 
150
  btn.click(
151
  fn=generate_activity,
152
  inputs=[user_desc, objective, duration, session_type, is_pediatric, context, pdf_files],
153
  outputs=[title, smart_obj, description, materials, procedure, evaluation, adaptations, theory]
154
  )
155
 
156
- demo.launch()
157
-
158
- demo.launch()
 
5
  import requests
6
  from pypdf import PdfReader
7
 
8
+ # ✅ Configurar API Key de OpenRouter desde variables de entorno
9
+ OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
10
+ if not OPENROUTER_API_KEY:
11
+ raise ValueError("❌ OPENROUTER_API_KEY no está configurada. Ve a Settings > Variables de entorno en tu Space.")
12
+
13
+ OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions"
14
+ # ✅ Modelo gratuito y potente: DeepSeek R1
15
+ MODEL = "deepseek/deepseek-r1-0528-qwen3-8b:free"
16
 
17
  def extract_text_from_pdf(pdf_file):
18
+ """Extrae texto de un archivo PDF"""
19
  try:
20
  reader = PdfReader(pdf_file)
21
  text = ""
 
23
  extracted = page.extract_text()
24
  if extracted:
25
  text += extracted + "\n"
26
+ return text[:5000] # Limitar para no exceder tokens
27
  except Exception as e:
28
  return f"Error al leer PDF: {str(e)}"
29
 
30
  def generate_smart_objective(objective, age, duration):
31
+ """Genera un objetivo SMART completo y detallado"""
32
  age_group = 'preescolar' if age < 36 else 'escolar' if age < 144 else 'adolescente/adulto'
33
  time_frame = 'corto plazo' if duration < 30 else 'mediano plazo' if duration < 60 else 'largo plazo'
34
+ 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}."
35
 
36
+ def call_openrouter_api(prompt):
37
+ """Llama a la API de OpenRouter"""
 
 
38
  headers = {
39
+ "Authorization": f"Bearer {OPENROUTER_API_KEY}",
40
+ "Content-Type": "application/json",
41
+ "HTTP-Referer": "https://tufonoayuda-pixel.github.io/ActFonoGenerator/", # Tu URL
42
+ "X-Title": "Generador IA de Actividades Fonoaudiológicas"
43
  }
44
 
45
  data = {
 
50
  }
51
 
52
  try:
53
+ response = requests.post(OPENROUTER_API_URL, headers=headers, json=data, timeout=60)
54
  response.raise_for_status()
55
  result = response.json()
56
  return result["choices"][0]["message"]["content"]
57
  except Exception as e:
58
+ raise Exception(f"Error al llamar a OpenRouter: {str(e)}")
59
 
60
  def generate_activity(user_desc, objective, duration, session_type, is_pediatric, context, pdf_files):
61
+ """Genera una actividad terapéutica COMPLETA y DETALLADA con IA real de OpenRouter"""
62
  if not all([user_desc, objective, duration]):
63
+ return ("⚠️ Error", "Por favor completa todos los campos obligatorios.", "", "", "", "", "", "")
64
 
65
  try:
66
+ # Extraer edad
67
  age = int(re.search(r'\d+', user_desc).group()) if re.search(r'\d+', user_desc) else 60
68
  is_child = age < 144 or is_pediatric
69
  dur = int(duration)
70
 
71
+ # Procesar PDFs
72
  pdf_text = ""
73
  if pdf_files:
74
  for pdf_file in pdf_files:
75
+ pdf_text += f"\n--- Contenido de {pdf_file.name} ---\n"
76
  pdf_text += extract_text_from_pdf(pdf_file)
77
 
78
+ # ✅ Construir prompt detallado para OpenRouter
79
  prompt = f"""
80
+ 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.
81
+
82
  PACIENTE: {user_desc}
83
+ OBJETIVO TERAPÉUTICO: {objective}
84
+ DURACIÓN DE LA SESIÓN: {duration} minutos
85
  TIPO DE SESIÓN: {session_type}
86
+ ¿SESIÓN PEDIÁTRICA?: {'Sí, usar lenguaje lúdico y adaptado' if is_child else 'No, lenguaje profesional y técnico'}
87
+ CONTEXTO ADICIONAL: {context or 'Ninguno'}
88
+ {f'REFERENCIAS CIENTÍFICAS (usa esta información para fundamentar): {pdf_text}' if pdf_text else ''}
89
+
90
+ INSTRUCCIONES ESPECÍFICAS PARA LA ACTIVIDAD:
91
+
92
+ 1. TÍTULO: Crea un título atractivo, profesional y descriptivo que refleje el contenido de la actividad.
93
+
94
+ 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.
95
+
96
+ 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.
97
+
98
+ 4. MATERIALES NECESARIOS: Lista todos los materiales requeridos con descripciones específicas y detalladas. No uses viñetas sueltas, escribe oraciones completas.
99
+
100
+ 5. PROCEDIMIENTO PASO A PASO: Describe detalladamente las tres fases de la actividad (calentamiento, desarrollo, cierre). Para cada fase:
101
+ - Nombre de la fase
102
+ - Duración estimada
103
+ - Instrucciones verbales exactas que debe dar el terapeuta
104
+ - Actividades específicas que realizará el paciente
105
+ - Posibles variaciones o adaptaciones durante la actividad
106
+ - Señales de progreso o dificultad a observar
107
+
108
+ 6. EVALUACIÓN: Define:
109
+ - Criterios de logro específicos y medibles
110
+ - Métodos de evaluación cuantitativos y cualitativos
111
+ - Cómo se registrará el progreso
112
+ - Cómo se dará retroalimentación al paciente/familia
113
+
114
+ 7. ADAPTACIONES: Sugiere adaptaciones específicas para:
115
+ - Diferentes niveles de habilidad
116
+ - Contextos (clínico, domiciliario, educativo)
117
+ - Características individuales del paciente
118
+ - Disponibilidad de recursos
119
+
120
+ 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.
121
+
122
+ FORMATO DE RESPUESTA OBLIGATORIO (JSON con estas claves):
123
+
124
+ {{
125
+ "title": "string (título completo y descriptivo)",
126
+ "smart_objective": "string (oración completa y detallada)",
127
+ "description": "string (párrafo completo de 5-7 oraciones mínimo)",
128
+ "materials": "string (párrafo detallado describiendo todos los materiales)",
129
+ "procedure": [
130
+ {{
131
+ "name": "string (nombre de la fase)",
132
+ "time": number (duración en minutos),
133
+ "instructions": "string (instrucciones verbales exactas del terapeuta)",
134
+ "activities": "string (descripción detallada de las actividades del paciente)",
135
+ "variations": "string (posibles variaciones o adaptaciones)",
136
+ "progress_indicators": "string (señales de progreso o dificultad a observar)"
137
+ }}
138
+ ],
139
+ "evaluation": {{
140
+ "criteria": "string (criterios de logro específicos y medibles)",
141
+ "methods": "string (métodos de evaluación cuantitativos y cualitativos)",
142
+ "recording": "string (cómo se registrará el progreso)",
143
+ "feedback": "string (cómo se dará retroalimentación al paciente/familia)"
144
+ }},
145
+ "adaptations": "string (párrafo completo describiendo todas las adaptaciones necesarias)",
146
+ "theoretical_foundation": "string (2-3 párrafos completos con fundamentación teórica)"
147
+ }}
148
+
149
+ 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.
150
  """
151
 
152
+ # Llamar a OpenRouter API
153
+ content = call_openrouter_api(prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
+ # Limpiar y parsear JSON
156
+ if content.startswith("```json"):
157
+ content = content[7:]
158
+ if content.endswith("```"):
159
+ content = content[:-3]
160
+
161
+ data = json.loads(content)
162
+
163
+ # ✅ Retornar resultados completos
164
  return (
165
+ data.get("title", f"Actividad Terapéutica para {objective}"),
166
  data.get("smart_objective", generate_smart_objective(objective, age, dur)),
167
+ data.get("description", "Descripción detallada generada por IA no disponible."),
168
+ data.get("materials", "Lista de materiales no disponible."),
169
+ "\n\n".join([
170
+ f"FASE: {p.get('name', 'Fase no especificada')} ({p.get('time', 0)} minutos)\n"
171
+ f"INSTRUCCIONES DEL TERAPEUTA: {p.get('instructions', 'No especificadas')}\n"
172
+ f"ACTIVIDADES DEL PACIENTE: {p.get('activities', 'No especificadas')}\n"
173
+ f"VARIACIONES: {p.get('variations', 'No especificadas')}\n"
174
+ f"INDICADORES DE PROGRESO: {p.get('progress_indicators', 'No especificados')}"
175
+ for p in data.get("procedure", [])
176
+ ]),
177
+ f"CRITERIOS DE LOGRO: {data.get('evaluation', {}).get('criteria', 'No especificados')}\n\n"
178
+ f"MÉTODOS DE EVALUACIÓN: {data.get('evaluation', {}).get('methods', 'No especificados')}\n\n"
179
+ f"REGISTRO DE PROGRESO: {data.get('evaluation', {}).get('recording', 'No especificado')}\n\n"
180
+ f"RETROALIMENTACIÓN: {data.get('evaluation', {}).get('feedback', 'No especificada')}",
181
+ data.get("adaptations", "Adaptaciones no disponibles."),
182
+ data.get("theoretical_foundation", "Fundamentación teórica no disponible.")
183
  )
184
 
185
  except Exception as e:
186
+ # ✅ Mostrar error real
187
  return (
188
+ "❌ Error al generar con IA",
189
+ f"Error: {str(e)}",
190
+ "La IA no pudo generar una respuesta completa. Esto puede deberse a:\n"
191
+ " El prompt es demasiado largo o complejo\n"
192
+ "• Problemas temporales con la API de OpenRouter\n"
193
+ "• Tu API Key no es válida o ha excedido límites\n\n"
194
+ "Sugerencias:\n"
195
+ "• Intenta con una descripción más concisa\n"
196
+ "• Reduce la cantidad de PDFs o contexto adicional\n"
197
+ "• Intenta nuevamente en unos minutos",
198
+ "",
199
  "",
200
  "",
201
  "",
202
  ""
203
  )
204
 
205
+ # Interfaz de Gradio
206
+ with gr.Blocks(title="🧠 Generador IA de Actividades Fonoaudiológicas") as demo:
207
  gr.Markdown("# 🧠 Generador IA de Actividades Fonoaudiológicas")
208
+ gr.Markdown("### ✨ Potenciado con DeepSeek R1 en OpenRouter • Creado por Flgo. Cristóbal San Martín [@tufonoayuda](https://instagram.com/tufonoayuda)")
209
 
210
  with gr.Row():
211
  with gr.Column():
212
+ 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)
213
+ 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)
214
+ duration = gr.Number(label="⏱️ Duración (minutos)", value=30, minimum=15, maximum=120)
215
+ session_type = gr.Dropdown(["individual", "grupal", "hogar"], label="👥 Tipo de sesión", value="individual")
216
+ is_pediatric = gr.Checkbox(label="🧸 Sesión Pediátrica (lenguaje lúdico)")
217
+ 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)
218
+ pdf_files = gr.File(label="📖 Subir PDFs de Referencia (Opcional)", file_types=[".pdf"], file_count="multiple")
219
+ btn = gr.Button("✨ Generar Actividad con IA", variant="primary")
220
 
221
  with gr.Column():
222
+ title = gr.Textbox(label="Título de la Actividad", lines=2)
223
+ smart_obj = gr.Textbox(label="📋 Objetivo SMART", lines=3)
224
+ description = gr.Textbox(label="📝 Descripción General", lines=6)
225
+ materials = gr.Textbox(label="🎯 Materiales Necesarios", lines=4)
226
+ procedure = gr.Textbox(label="⚡ Procedimiento Paso a Paso (Instrucciones completas)", lines=10)
227
+ evaluation = gr.Textbox(label="📊 Evaluación (Criterios, métodos, registro y retroalimentación)", lines=6)
228
+ adaptations = gr.Textbox(label="🔧 Adaptaciones (Párrafo completo)", lines=4)
229
+ theory = gr.Textbox(label="📚 Fundamentación Teórica (2-3 párrafos completos)", lines=6)
230
 
231
+ # Conectar botón
232
  btn.click(
233
  fn=generate_activity,
234
  inputs=[user_desc, objective, duration, session_type, is_pediatric, context, pdf_files],
235
  outputs=[title, smart_obj, description, materials, procedure, evaluation, adaptations, theory]
236
  )
237
 
238
+ # Lanzar app
239
+ if __name__ == "__main__":
240
+ demo.launch()