C2MV commited on
Commit
f45845d
·
verified ·
1 Parent(s): 4b06f5e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +638 -403
app.py CHANGED
@@ -1,491 +1,726 @@
1
  import gradio as gr
2
  import anthropic
3
  import PyPDF2
 
 
4
  import io
5
  import os
6
  import json
7
- from typing import Dict, List, Tuple
 
 
8
  import re
 
 
 
 
 
 
 
9
 
10
  # Inicializar cliente Anthropic
11
  client = anthropic.Anthropic()
12
 
13
- # Modelos de Claude disponibles (actualizados)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  CLAUDE_MODELS = {
15
- "claude-opus-4-20250514": {
16
- "name": "Claude Opus 4 (Latest)",
17
- "description": "Modelo más potente para desafíos complejos",
18
- "max_tokens": 4000,
19
- "best_for": "Análisis muy detallados y complejos"
20
- },
21
- "claude-sonnet-4-20250514": {
22
- "name": "Claude Sonnet 4 (Latest)",
23
- "description": "Modelo inteligente y eficiente para uso cotidiano",
24
- "max_tokens": 4000,
25
- "best_for": "Análisis general, recomendado para la mayoría de casos"
26
- },
27
- "claude-3-5-haiku-20241022": {
28
- "name": "Claude 3.5 Haiku (Latest)",
29
- "description": "Modelo más rápido para tareas diarias",
30
  "max_tokens": 4000,
31
- "best_for": "Análisis rápidos y económicos"
32
  },
33
- "claude-3-7-sonnet-20250219": {
34
- "name": "Claude 3.7 Sonnet",
35
- "description": "Modelo avanzado de la serie 3.7",
36
  "max_tokens": 4000,
37
- "best_for": "Análisis equilibrados con alta calidad"
38
  },
39
- "claude-3-5-sonnet-20241022": {
40
- "name": "Claude 3.5 Sonnet (Oct 2024)",
41
- "description": "Excelente balance entre velocidad y capacidad",
42
  "max_tokens": 4000,
43
- "best_for": "Análisis rápidos y precisos"
44
  }
45
  }
46
 
47
- # Base de conocimientos de modelos matemáticos biotecnológicos
48
- BIOTECH_MODELS = {
49
- "crecimiento_biomasa": {
50
- "Monod": {
51
- "ecuacion": "μ = μmax × (S / (Ks + S))",
52
- "parametros": ["μmax (h⁻¹)", "Ks (g/L)"],
53
- "aplicacion": "Crecimiento limitado por sustrato único",
54
- "fuentes": "Cambridge, MIT, DTU"
55
- },
56
- "Logístico": {
57
- "ecuacion": "dX/dt = μmax × X × (1 - X/Xmax)",
58
- "parametros": ["μmax (h⁻¹)", "Xmax (g/L)"],
59
- "aplicacion": "Sistemas cerrados batch",
60
- "fuentes": "Cranfield, Swansea, HAL Theses"
61
- },
62
- "Gompertz": {
63
- "ecuacion": "X(t) = Xmax × exp(-exp((μmax × e / Xmax) × (λ - t) + 1))",
64
- "parametros": ["λ (h)", "μmax (h⁻¹)", "Xmax (g/L)"],
65
- "aplicacion": "Crecimiento con fase lag pronunciada",
66
- "fuentes": "Lund University, NC State"
67
- },
68
- "Contois": {
69
- "ecuacion": "μ = μmax × S / (Ks × X + S)",
70
- "parametros": ["μmax (h⁻¹)", "Ks (adimensional)"],
71
- "aplicacion": "Dependencia de concentración de biomasa",
72
- "fuentes": "Virginia Tech, UMONS"
73
- },
74
- "Andrews": {
75
- "ecuacion": = μmax × S / (Ks + S + S²/Ki)",
76
- "parametros": ["μmax (h⁻¹)", "Ks (g/L)", "Ki (g/L)"],
77
- "aplicacion": "Inhibición a altas concentraciones de sustrato",
78
- "fuentes": "RWTH Aachen, TU Berlin"
79
- }
80
- },
81
- "consumo_sustrato": {
82
- "Michaelis-Menten": {
83
- "ecuacion": "v = Vmax × S / (Km + S)",
84
- "parametros": ["Vmax", "Km"],
85
- "aplicacion": "Cinética enzimática básica",
86
- "fuentes": "Warsaw Univ Tech, Food Processing"
87
- },
88
- "Inhibición Competitiva": {
89
- "ecuacion": "v = Vmax × S / (Km × (1 + I/Ki) + S)",
90
- "parametros": ["Vmax", "Km", "I", "Ki"],
91
- "aplicacion": "Inhibición competitiva",
92
- "fuentes": "TU Delft, Uni Düsseldorf"
93
- },
94
- "Sustrato Dual": {
95
- "ecuacion": "μ = μmax × (S1/(Ks1 + S1)) × (S2/(Ks2 + S2))",
96
- "parametros": ["μmax", "S1", "S2", "Ks1", "Ks2"],
97
- "aplicacion": "Crecimiento con múltiples sustratos limitantes",
98
- "fuentes": "Cornell, TU Clausthal"
99
- }
100
- },
101
- "formacion_producto": {
102
- "Luedeking-Piret": {
103
- "ecuacion": "dP/dt = α × (dX/dt) + β × X",
104
- "parametros": ["α (asociado)", "β (no asociado)"],
105
- "aplicacion": "Producción mixta asociada/no asociada",
106
- "fuentes": "Cambridge, E-Century"
107
- },
108
- "Inhibición por Producto": {
109
- "ecuacion": "μ = μmax × (1 - P/Pmax)^n",
110
- "parametros": ["μmax", "Pmax", "n"],
111
- "aplicacion": "Fermentaciones inhibidas por producto",
112
- "fuentes": "Virginia Tech, EcoEET"
113
- }
114
- },
115
- "biorreactores": {
116
- "Batch": {
117
- "ecuaciones": [
118
- "dX/dt = μ × X",
119
- "dS/dt = -μ × X / YX/S",
120
- "dP/dt = α × μ × X + β × X"
121
- ],
122
- "aplicacion": "Procesos discontinuos",
123
- "fuentes": "DTU, UCL"
124
- },
125
- "Fed-Batch": {
126
- "ecuaciones": [
127
- "dX/dt = μ × X - D × X",
128
- "dS/dt = D × (Sf - S) - μ × X / YX/S"
129
- ],
130
- "parametros": ["D (tasa dilución)", "Sf"],
131
- "aplicacion": "Alimentación controlada",
132
- "fuentes": "Core Academic, UNESP"
133
- },
134
- "CSTR": {
135
- "ecuaciones": [
136
- "dX/dt = μ × X - D × X",
137
- "dS/dt = D × (Sf - S) - μ × X / YX/S"
138
- ],
139
- "aplicacion": "Estado estacionario continuo",
140
- "fuentes": "MIT, UCL"
141
- }
142
- },
143
- "transferencia_masa": {
144
- "OTR": {
145
- "ecuacion": "OTR = kLa × (C* - CL)",
146
- "parametros": ["kLa", "C*", "CL"],
147
- "aplicacion": "Transferencia de oxígeno",
148
- "fuentes": "UK Kentucky, TU Delft"
149
- }
150
- },
151
- "metabolicos_avanzados": {
152
- "FBA": {
153
- "ecuacion": "S × v = 0, vmin ≤ v ≤ vmax",
154
- "aplicacion": "Análisis de redes metabólicas",
155
- "fuentes": "Cornell, TU Clausthal"
156
- }
157
- }
158
- }
159
-
160
- def extract_text_from_pdf(pdf_file) -> str:
161
- """Extrae texto de un archivo PDF"""
162
- try:
163
- pdf_reader = PyPDF2.PdfReader(io.BytesIO(pdf_file))
164
- text = ""
165
- for page in pdf_reader.pages:
166
- text += page.extract_text() + "\n"
167
- return text
168
- except Exception as e:
169
- return f"Error al leer PDF: {str(e)}"
170
 
171
- def analyze_with_ai(pdf_text: str, analysis_type: str, claude_model: str = "claude-opus-4-20250514") -> str:
172
- """Analiza el texto del PDF usando IA con el modelo de Claude seleccionado"""
 
 
 
 
173
 
174
- prompts = {
175
- "identificar_proceso": """
176
- Analiza este texto científico y identifica:
177
- 1. ¿Qué tipo de proceso biotecnológico se describe?
178
- 2. ¿Qué microorganismos están involucrados?
179
- 3. ¿Qué sustratos y productos se mencionan?
180
- 4. ¿Qué tipo de reactor o sistema se utiliza?
181
- 5. ¿Hay menciones de inhibición, limitación o efectos específicos?
182
-
183
- Responde de manera concisa y técnica.
184
- """,
185
-
186
- "recomendar_modelos": """
187
- Basado en el análisis del proceso biotecnológico, recomienda los modelos matemáticos más apropiados de esta lista:
188
-
189
- MODELOS DISPONIBLES:
190
- - Crecimiento: Monod, Logístico, Gompertz, Contois, Andrews
191
- - Enzimático: Michaelis-Menten, Inhibición Competitiva, Sustrato Dual
192
- - Producto: Luedeking-Piret, Inhibición por Producto
193
- - Reactores: Batch, Fed-Batch, CSTR
194
- - Transferencia: OTR
195
- - Avanzados: FBA
196
-
197
- Para cada modelo recomendado, explica por qué es apropiado para este proceso específico.
198
- """,
199
-
200
- "parametros_estimacion": """
201
- Identifica qué parámetros cinéticos podrían necesitar estimación experimental para este proceso:
202
- 1. Parámetros de crecimiento (μmax, Ks, etc.)
203
- 2. Parámetros de producto (α, β, etc.)
204
- 3. Parámetros de inhibición (Ki, Pmax, etc.)
205
- 4. Coeficientes de rendimiento (YX/S, YP/S, etc.)
206
-
207
- Sugiere métodos experimentales para determinar cada parámetro.
208
  """
209
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
 
211
- try:
212
- # Obtener configuración del modelo seleccionado
213
- model_config = CLAUDE_MODELS.get(claude_model, CLAUDE_MODELS["claude-opus-4-20250514"])
214
-
215
- response = client.messages.create(
216
- model=claude_model,
217
- max_tokens=model_config["max_tokens"],
218
- system="Eres un experto en biotecnología y modelado matemático de bioprocesos. Analiza textos científicos y proporciona recomendaciones técnicas precisas basadas en la extensa base de conocimientos de 140 modelos matemáticos biotecnológicos de universidades prestigiosas.",
219
- messages=[
220
- {
221
- "role": "user",
222
- "content": f"{prompts[analysis_type]}\n\nTEXTO A ANALIZAR:\n{pdf_text[:4000]}"
223
- }
224
- ]
225
- )
226
- return response.content[0].text
227
- except Exception as e:
228
- return f"Error en análisis con IA ({claude_model}): {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
- def get_model_details(recommended_models: List[str]) -> str:
231
- """Obtiene detalles de los modelos recomendados"""
232
- details = "## 📋 DETALLES DE MODELOS RECOMENDADOS\n\n"
 
 
233
 
234
- for category, models in BIOTECH_MODELS.items():
235
- for model_name, model_info in models.items():
236
- if any(model_name.lower() in rec.lower() for rec in recommended_models):
237
- details += f"### {model_name}\n"
238
- if "ecuacion" in model_info:
239
- details += f"**Ecuación:** `{model_info['ecuacion']}`\n\n"
240
- elif "ecuaciones" in model_info:
241
- details += "**Ecuaciones:**\n"
242
- for eq in model_info['ecuaciones']:
243
- details += f"- `{eq}`\n"
244
- details += "\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
 
246
- if "parametros" in model_info:
247
- details += f"**Parámetros:** {', '.join(model_info['parametros'])}\n\n"
 
 
 
 
 
 
 
248
 
249
- details += f"**Aplicación:** {model_info['aplicacion']}\n\n"
250
- details += f"**Fuentes académicas:** {model_info['fuentes']}\n\n"
251
- details += "---\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
- return details
254
 
255
- def generate_implementation_code(models: List[str]) -> str:
256
- """Genera código Python para implementar los modelos"""
257
  code = """
258
  import numpy as np
 
259
  import matplotlib.pyplot as plt
260
  from scipy.integrate import odeint
261
- from scipy.optimize import curve_fit
 
 
262
 
263
- # Implementación de modelos biotecnológicos recomendados
 
 
264
 
265
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
 
267
- if any("monod" in m.lower() for m in models):
268
- code += """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
  def monod_model(S, mu_max, Ks):
270
- \"\"\"Modelo de Monod para crecimiento\"\"\"
271
  return mu_max * S / (Ks + S)
272
 
273
- def batch_monod(y, t, mu_max, Ks, Yxs):
274
- \"\"\"Sistema batch con cinética de Monod\"\"\"
275
- X, S = y
276
- mu = monod_model(S, mu_max, Ks)
277
- dXdt = mu * X
278
- dSdt = -mu * X / Yxs
279
- return [dXdt, dSdt]
280
 
281
- """
282
-
283
- if any("luedeking" in m.lower() for m in models):
284
- code += """
285
- def luedeking_piret(X, dXdt, alpha, beta):
286
- \"\"\"Modelo de Luedeking-Piret para formación de producto\"\"\"
287
- return alpha * dXdt + beta * X
288
 
289
- """
290
-
291
- if any("michaelis" in m.lower() for m in models):
292
- code += """
293
  def michaelis_menten(S, Vmax, Km):
294
- \"\"\"Cinética de Michaelis-Menten\"\"\"
295
  return Vmax * S / (Km + S)
296
 
297
- """
 
 
 
298
 
299
- code += """
300
- # Ejemplo de ajuste de parámetros
301
- def fit_model_parameters(time_data, concentration_data, model_function):
302
- \"\"\"Ajusta parámetros del modelo a datos experimentales\"\"\"
303
- try:
304
- popt, pcov = curve_fit(model_function, time_data, concentration_data)
305
- return popt, pcov
306
- except Exception as e:
307
- print(f"Error en ajuste: {e}")
308
- return None, None
309
-
310
- # Ejemplo de simulación
311
- def simulate_process(initial_conditions, time_span, parameters):
312
- \"\"\"Simula el proceso biotecnológico\"\"\"
313
- t = np.linspace(0, time_span, 100)
314
- # Aquí integrarías tu sistema de ecuaciones específico
315
- # sol = odeint(your_system, initial_conditions, t, args=parameters)
316
- return t, None # Reemplazar con solución real
317
-
318
- print("Modelos implementados exitosamente!")
319
- print("Personaliza los parámetros según tus datos experimentales.")
320
  """
321
 
322
  return code
323
 
324
- def comprehensive_analysis(pdf_file, claude_model: str = "claude-opus-4-20250514") -> Tuple[str, str, str]:
325
- """Análisis completo del PDF con el modelo de Claude seleccionado"""
326
- if pdf_file is None:
327
- return "❌ Por favor sube un archivo PDF", "", "⚠️ No hay archivo para analizar"
328
-
329
- try:
330
- # Extraer texto
331
- pdf_text = extract_text_from_pdf(pdf_file)
332
- if "Error" in pdf_text:
333
- return pdf_text, "", "❌ Error al procesar PDF"
334
-
335
- # Mostrar modelo seleccionado
336
- model_info = CLAUDE_MODELS.get(claude_model, CLAUDE_MODELS["claude-opus-4-20250514"])
337
- status_msg = f"🤖 Analizando con {model_info['name']}..."
338
-
339
- # Análisis por etapas
340
- process_analysis = analyze_with_ai(pdf_text, "identificar_proceso", claude_model)
341
- model_recommendations = analyze_with_ai(pdf_text, "recomendar_modelos", claude_model)
342
- parameter_analysis = analyze_with_ai(pdf_text, "parametros_estimacion", claude_model)
343
-
344
- # Extraer modelos recomendados para obtener detalles
345
- recommended_models = []
346
- for category, models in BIOTECH_MODELS.items():
347
- for model_name in models.keys():
348
- if model_name.lower() in model_recommendations.lower():
349
- recommended_models.append(model_name)
350
-
351
- model_details = get_model_details(recommended_models)
352
- implementation_code = generate_implementation_code(recommended_models)
353
-
354
- # Formatear respuesta final
355
- final_report = f"""
356
- # 🧬 ANÁLISIS BIOTECNOLÓGICO COMPLETO
357
-
358
- ## 🔍 IDENTIFICACIÓN DEL PROCESO
359
- {process_analysis}
360
-
361
- ## 🎯 MODELOS RECOMENDADOS
362
- {model_recommendations}
363
-
364
- ## ⚙️ ANÁLISIS DE PARÁMETROS
365
- {parameter_analysis}
366
-
367
- {model_details}
368
-
369
- ## 💡 RECOMENDACIONES FINALES
370
- - Validar modelos con datos experimentales
371
- - Considerar efectos de escala en el reactor
372
- - Monitorear parámetros críticos identificados
373
- - Implementar control adaptativo si es necesario
374
- """
375
-
376
- success_msg = f"✅ Análisis completado con {model_info['name']} - {len(recommended_models)} modelos identificados"
377
-
378
- return final_report, implementation_code, success_msg
379
-
380
- except Exception as e:
381
- return f"❌ Error durante el análisis: {str(e)}", "", "❌ Error en el procesamiento"
382
-
383
- # Crear interfaz Gradio
384
  def create_interface():
385
- with gr.Blocks(title="Analizador de Modelos Biotecnológicos", theme=gr.themes.Soft()) as demo:
 
 
 
 
 
 
 
 
 
386
  gr.Markdown("""
387
- # 🧬 Analizador de Modelos Matemáticos Biotecnológicos
388
 
389
- **Herramienta inteligente basada en 140+ modelos de universidades prestigiosas**
 
 
 
 
 
390
 
391
- 📄 Sube tu PDF científico y obtén:
392
- - Identificación automática del proceso biotecnológico
393
- - 🎯 Recomendación de modelos matemáticos apropiados
394
- - 📊 Análisis de parámetros a estimar
395
- - 🔬 Código Python listo para implementar
396
- - 📚 Referencias académicas validadas
397
  """)
398
 
399
  with gr.Row():
400
  with gr.Column(scale=1):
401
- pdf_input = gr.File(
402
- label="📄 Subir PDF Científico",
403
- file_types=[".pdf"],
404
- type="binary"
 
405
  )
406
 
407
- # Selector de modelo Claude
408
  model_selector = gr.Dropdown(
409
  choices=list(CLAUDE_MODELS.keys()),
410
- value="claude-opus-4-20250514",
411
- label="🤖 Seleccionar Modelo Claude",
412
- info="Elige el modelo que mejor se adapte a tu análisis"
413
- )
414
-
415
- # Mostrar información del modelo seleccionado
416
- def update_model_info(selected_model):
417
- model_info = CLAUDE_MODELS.get(selected_model, {})
418
- return f"**{model_info.get('name', 'N/A')}**\n{model_info.get('description', 'N/A')}\n\n*Mejor para:* {model_info.get('best_for', 'N/A')}"
419
-
420
- model_info_display = gr.Markdown(
421
- value=update_model_info("claude-opus-4-20250514"),
422
- label="ℹ️ Información del Modelo"
423
  )
424
 
425
  analyze_btn = gr.Button(
426
- "🚀 Analizar con IA",
427
  variant="primary",
428
  size="lg"
429
  )
430
 
431
- status = gr.Textbox(
432
- label="📊 Estado del Análisis",
433
- interactive=False,
434
- value="Listo para analizar..."
 
 
 
 
 
 
 
 
 
 
 
 
 
435
  )
436
 
437
  with gr.Column(scale=2):
438
  analysis_output = gr.Markdown(
439
- label="📋 Reporte de Análisis",
440
- value="**Instrucciones:**\n1. Sube un archivo PDF con contenido biotecnológico\n2. Selecciona el modelo Claude apropiado\n3. Haz clic en 'Analizar con IA'\n4. Revisa el análisis y código generado"
 
 
 
 
 
441
  )
442
 
443
- with gr.Row():
444
- code_output = gr.Code(
445
- label="🐍 Código Python Generado",
446
- language="python",
447
- interactive=True,
448
- value="# El código Python se generará aquí después del análisis..."
449
- )
 
 
 
450
 
451
- with gr.Row():
452
- gr.Markdown("""
453
- ### 📚 Base de Conocimientos Incluye:
454
- - **35+ Universidades:** MIT, Cambridge, UCL, Cornell, TU Delft, DTU, etc.
455
- - **8 Categorías:** Crecimiento, Sustrato, Producto, Reactores, Transferencia, Metabólicos
456
- - **40+ Modelos:** Desde Monod clásico hasta FBA avanzado
457
- - **Validación académica:** 140 PDFs científicos analizados
458
-
459
- ### 🔧 Modelos Claude Disponibles:
460
- - **Opus 4:** Máximo rendimiento para análisis complejos
461
- - **Sonnet 4:** Equilibrio perfecto para uso general
462
- - **Haiku 3.5:** Velocidad optimizada para análisis rápidos
463
- """)
464
 
465
- # Conectar eventos
 
 
 
 
 
 
 
466
  analyze_btn.click(
467
- comprehensive_analysis,
468
- inputs=[pdf_input, model_selector],
469
- outputs=[analysis_output, code_output, status]
 
 
 
470
  )
471
 
472
- # Actualizar información del modelo cuando se cambie la selección
473
- model_selector.change(
474
- update_model_info,
475
- inputs=[model_selector],
476
- outputs=[model_info_display]
477
  )
478
 
479
  return demo
480
 
481
- # Ejecutar aplicación
482
- if __name__ == "__main__":
483
  if not os.getenv("ANTHROPIC_API_KEY"):
484
- print("⚠️ Configura ANTHROPIC_API_KEY como variable de entorno")
485
- print("export ANTHROPIC_API_KEY='tu-clave-api'")
486
- else:
487
- demo = create_interface()
488
- print("🚀 Iniciando Analizador de Modelos Biotecnológicos...")
 
 
 
 
 
 
 
 
 
489
  demo.launch(
490
  server_name="0.0.0.0",
491
  server_port=7860,
 
1
  import gradio as gr
2
  import anthropic
3
  import PyPDF2
4
+ import pandas as pd
5
+ import numpy as np
6
  import io
7
  import os
8
  import json
9
+ import zipfile
10
+ import tempfile
11
+ from typing import Dict, List, Tuple, Union
12
  import re
13
+ from pathlib import Path
14
+ import openpyxl
15
+ from dataclasses import dataclass
16
+ from enum import Enum
17
+
18
+ # Configuración para HuggingFace
19
+ os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'
20
 
21
  # Inicializar cliente Anthropic
22
  client = anthropic.Anthropic()
23
 
24
+ # Enum para tipos de análisis
25
+ class AnalysisType(Enum):
26
+ MATHEMATICAL_MODEL = "mathematical_model"
27
+ DATA_FITTING = "data_fitting"
28
+ UNKNOWN = "unknown"
29
+
30
+ # Estructura modular para modelos
31
+ @dataclass
32
+ class MathematicalModel:
33
+ name: str
34
+ equation: str
35
+ parameters: List[str]
36
+ application: str
37
+ sources: List[str]
38
+ category: str
39
+
40
+ # Sistema de registro de modelos escalable
41
+ class ModelRegistry:
42
+ def __init__(self):
43
+ self.models = {}
44
+ self._initialize_default_models()
45
+
46
+ def register_model(self, model: MathematicalModel):
47
+ """Registra un nuevo modelo matemático"""
48
+ if model.category not in self.models:
49
+ self.models[model.category] = {}
50
+ self.models[model.category][model.name] = model
51
+
52
+ def get_model(self, category: str, name: str) -> MathematicalModel:
53
+ """Obtiene un modelo específico"""
54
+ return self.models.get(category, {}).get(name)
55
+
56
+ def get_all_models(self) -> Dict:
57
+ """Retorna todos los modelos registrados"""
58
+ return self.models
59
+
60
+ def _initialize_default_models(self):
61
+ """Inicializa los modelos por defecto"""
62
+ # Modelos de crecimiento
63
+ self.register_model(MathematicalModel(
64
+ name="Monod",
65
+ equation="μ = μmax × (S / (Ks + S))",
66
+ parameters=["μmax (h⁻¹)", "Ks (g/L)"],
67
+ application="Crecimiento limitado por sustrato único",
68
+ sources=["Cambridge", "MIT", "DTU"],
69
+ category="crecimiento_biomasa"
70
+ ))
71
+
72
+ self.register_model(MathematicalModel(
73
+ name="Logístico",
74
+ equation="dX/dt = μmax × X × (1 - X/Xmax)",
75
+ parameters=["μmax (h⁻¹)", "Xmax (g/L)"],
76
+ application="Sistemas cerrados batch",
77
+ sources=["Cranfield", "Swansea", "HAL Theses"],
78
+ category="crecimiento_biomasa"
79
+ ))
80
+
81
+ self.register_model(MathematicalModel(
82
+ name="Gompertz",
83
+ equation="X(t) = Xmax × exp(-exp((μmax × e / Xmax) × (λ - t) + 1))",
84
+ parameters=["λ (h)", "μmax (h⁻¹)", "Xmax (g/L)"],
85
+ application="Crecimiento con fase lag pronunciada",
86
+ sources=["Lund University", "NC State"],
87
+ category="crecimiento_biomasa"
88
+ ))
89
+
90
+ # Modelos enzimáticos
91
+ self.register_model(MathematicalModel(
92
+ name="Michaelis-Menten",
93
+ equation="v = Vmax × S / (Km + S)",
94
+ parameters=["Vmax", "Km"],
95
+ application="Cinética enzimática básica",
96
+ sources=["Warsaw Univ Tech", "Food Processing"],
97
+ category="consumo_sustrato"
98
+ ))
99
+
100
+ # Modelos de producto
101
+ self.register_model(MathematicalModel(
102
+ name="Luedeking-Piret",
103
+ equation="dP/dt = α × (dX/dt) + β × X",
104
+ parameters=["α (asociado)", "β (no asociado)"],
105
+ application="Producción mixta asociada/no asociada",
106
+ sources=["Cambridge", "E-Century"],
107
+ category="formacion_producto"
108
+ ))
109
+
110
+ # Instancia global del registro
111
+ model_registry = ModelRegistry()
112
+
113
+ # Modelos de Claude disponibles
114
  CLAUDE_MODELS = {
115
+ "claude-3-5-sonnet-20241022": {
116
+ "name": "Claude 3.5 Sonnet",
117
+ "description": "Modelo rápido y eficiente",
 
 
 
 
 
 
 
 
 
 
 
 
118
  "max_tokens": 4000,
119
+ "best_for": "Análisis general"
120
  },
121
+ "claude-3-opus-20240229": {
122
+ "name": "Claude 3 Opus",
123
+ "description": "Modelo más potente",
124
  "max_tokens": 4000,
125
+ "best_for": "Análisis complejos"
126
  },
127
+ "claude-3-haiku-20240307": {
128
+ "name": "Claude 3 Haiku",
129
+ "description": "Modelo más rápido",
130
  "max_tokens": 4000,
131
+ "best_for": "Análisis rápidos"
132
  }
133
  }
134
 
135
+ class FileProcessor:
136
+ """Clase para procesar diferentes tipos de archivos"""
137
+
138
+ @staticmethod
139
+ def extract_text_from_pdf(pdf_file) -> str:
140
+ """Extrae texto de un archivo PDF"""
141
+ try:
142
+ pdf_reader = PyPDF2.PdfReader(io.BytesIO(pdf_file))
143
+ text = ""
144
+ for page in pdf_reader.pages:
145
+ text += page.extract_text() + "\n"
146
+ return text
147
+ except Exception as e:
148
+ return f"Error al leer PDF: {str(e)}"
149
+
150
+ @staticmethod
151
+ def read_csv(csv_file) -> pd.DataFrame:
152
+ """Lee archivo CSV"""
153
+ try:
154
+ return pd.read_csv(io.BytesIO(csv_file))
155
+ except Exception as e:
156
+ return None
157
+
158
+ @staticmethod
159
+ def read_excel(excel_file) -> pd.DataFrame:
160
+ """Lee archivo Excel"""
161
+ try:
162
+ return pd.read_excel(io.BytesIO(excel_file))
163
+ except Exception as e:
164
+ return None
165
+
166
+ @staticmethod
167
+ def extract_from_zip(zip_file) -> List[Tuple[str, bytes]]:
168
+ """Extrae archivos de un ZIP"""
169
+ files = []
170
+ try:
171
+ with zipfile.ZipFile(io.BytesIO(zip_file), 'r') as zip_ref:
172
+ for file_name in zip_ref.namelist():
173
+ if not file_name.startswith('__MACOSX'):
174
+ file_data = zip_ref.read(file_name)
175
+ files.append((file_name, file_data))
176
+ except Exception as e:
177
+ print(f"Error procesando ZIP: {e}")
178
+ return files
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
+ class AIAnalyzer:
181
+ """Clase para análisis con IA"""
182
+
183
+ def __init__(self, client, model_registry):
184
+ self.client = client
185
+ self.model_registry = model_registry
186
 
187
+ def detect_analysis_type(self, content: Union[str, pd.DataFrame]) -> AnalysisType:
188
+ """Detecta el tipo de análisis necesario"""
189
+ if isinstance(content, pd.DataFrame):
190
+ # Si es DataFrame, probablemente son datos para ajustar
191
+ return AnalysisType.DATA_FITTING
192
+
193
+ # Analizar texto para determinar tipo
194
+ prompt = """
195
+ Analiza este contenido y determina si es:
196
+ 1. Un artículo científico que describe modelos matemáticos biotecnológicos
197
+ 2. Datos experimentales para ajuste de parámetros
198
+
199
+ Responde solo con: "MODELO" o "DATOS"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  """
201
+
202
+ try:
203
+ response = self.client.messages.create(
204
+ model="claude-3-haiku-20240307",
205
+ max_tokens=10,
206
+ messages=[{"role": "user", "content": f"{prompt}\n\n{content[:1000]}"}]
207
+ )
208
+
209
+ result = response.content[0].text.strip().upper()
210
+ if "MODELO" in result:
211
+ return AnalysisType.MATHEMATICAL_MODEL
212
+ elif "DATOS" in result:
213
+ return AnalysisType.DATA_FITTING
214
+ else:
215
+ return AnalysisType.UNKNOWN
216
+
217
+ except:
218
+ return AnalysisType.UNKNOWN
219
 
220
+ def analyze_mathematical_article(self, text: str, claude_model: str) -> Dict:
221
+ """Analiza artículo con modelos matemáticos"""
222
+ prompts = {
223
+ "identificar_modelos": """
224
+ Analiza este texto científico e identifica:
225
+ 1. Modelos matemáticos biotecnológicos descritos
226
+ 2. Ecuaciones específicas
227
+ 3. Parámetros mencionados
228
+ 4. Aplicaciones biotecnológicas
229
+ 5. Microorganismos y procesos
230
+
231
+ Formato JSON con estructura:
232
+ {
233
+ "modelos": ["nombre1", "nombre2"],
234
+ "ecuaciones": ["eq1", "eq2"],
235
+ "parametros": ["param1", "param2"],
236
+ "aplicaciones": ["app1", "app2"],
237
+ "microorganismos": ["org1", "org2"]
238
+ }
239
+ """,
240
+
241
+ "recomendar_implementacion": """
242
+ Basado en los modelos identificados, proporciona:
243
+ 1. Estrategia de implementación
244
+ 2. Consideraciones experimentales
245
+ 3. Métodos de validación
246
+ 4. Posibles limitaciones
247
+ """
248
+ }
249
+
250
+ try:
251
+ # Identificar modelos
252
+ response = self.client.messages.create(
253
+ model=claude_model,
254
+ max_tokens=2000,
255
+ messages=[{
256
+ "role": "user",
257
+ "content": f"{prompts['identificar_modelos']}\n\nTEXTO:\n{text[:3000]}"
258
+ }]
259
+ )
260
+
261
+ models_info = response.content[0].text
262
+
263
+ # Recomendaciones
264
+ response2 = self.client.messages.create(
265
+ model=claude_model,
266
+ max_tokens=2000,
267
+ messages=[{
268
+ "role": "user",
269
+ "content": f"{prompts['recomendar_implementacion']}\n\nMODELOS:\n{models_info}"
270
+ }]
271
+ )
272
+
273
+ return {
274
+ "tipo": "Artículo de Modelos Matemáticos",
275
+ "modelos": models_info,
276
+ "recomendaciones": response2.content[0].text
277
+ }
278
+
279
+ except Exception as e:
280
+ return {"error": str(e)}
281
+
282
+ def analyze_fitting_data(self, data: pd.DataFrame, claude_model: str) -> Dict:
283
+ """Analiza datos para ajuste de parámetros"""
284
+ # Preparar resumen de datos
285
+ data_summary = f"""
286
+ Columnas: {list(data.columns)}
287
+ Forma: {data.shape}
288
+ Primeras filas:
289
+ {data.head().to_string()}
290
+
291
+ Estadísticas:
292
+ {data.describe().to_string()}
293
+ """
294
+
295
+ prompt = """
296
+ Analiza estos datos experimentales y determina:
297
+ 1. Variables independientes y dependientes
298
+ 2. Posibles modelos matemáticos aplicables
299
+ 3. Método de ajuste recomendado
300
+ 4. Parámetros a estimar
301
+ 5. Calidad esperada del ajuste
302
+
303
+ Proporciona código Python para el ajuste.
304
+ """
305
+
306
+ try:
307
+ response = self.client.messages.create(
308
+ model=claude_model,
309
+ max_tokens=3000,
310
+ messages=[{
311
+ "role": "user",
312
+ "content": f"{prompt}\n\nDATOS:\n{data_summary}"
313
+ }]
314
+ )
315
+
316
+ return {
317
+ "tipo": "Datos para Ajuste",
318
+ "analisis": response.content[0].text,
319
+ "resumen_datos": data_summary
320
+ }
321
+
322
+ except Exception as e:
323
+ return {"error": str(e)}
324
 
325
+ def process_files(files, claude_model: str) -> str:
326
+ """Procesa múltiples archivos"""
327
+ processor = FileProcessor()
328
+ analyzer = AIAnalyzer(client, model_registry)
329
+ results = []
330
 
331
+ for file in files:
332
+ if file is None:
333
+ continue
334
+
335
+ file_name = file.name if hasattr(file, 'name') else "archivo"
336
+ file_ext = Path(file_name).suffix.lower()
337
+
338
+ # Leer contenido del archivo
339
+ with open(file.name, 'rb') as f:
340
+ file_content = f.read()
341
+
342
+ # Procesar según tipo
343
+ if file_ext == '.zip':
344
+ # Extraer y procesar archivos del ZIP
345
+ extracted_files = processor.extract_from_zip(file_content)
346
+ results.append(f"## 📦 Archivo ZIP: {file_name}")
347
+ results.append(f"Contiene {len(extracted_files)} archivos\n")
348
+
349
+ for name, content in extracted_files:
350
+ sub_ext = Path(name).suffix.lower()
351
+ results.append(f"### 📄 {name}")
352
+
353
+ if sub_ext == '.pdf':
354
+ text = processor.extract_text_from_pdf(content)
355
+ analysis_type = analyzer.detect_analysis_type(text)
356
+
357
+ if analysis_type == AnalysisType.MATHEMATICAL_MODEL:
358
+ result = analyzer.analyze_mathematical_article(text, claude_model)
359
+ else:
360
+ result = {"tipo": "PDF no reconocido", "contenido": text[:500]}
361
+
362
+ results.append(json.dumps(result, indent=2, ensure_ascii=False))
363
 
364
+ elif sub_ext in ['.csv', '.xlsx', '.xls']:
365
+ if sub_ext == '.csv':
366
+ df = processor.read_csv(content)
367
+ else:
368
+ df = processor.read_excel(content)
369
+
370
+ if df is not None:
371
+ result = analyzer.analyze_fitting_data(df, claude_model)
372
+ results.append(json.dumps(result, indent=2, ensure_ascii=False))
373
 
374
+ results.append("\n---\n")
375
+
376
+ elif file_ext == '.pdf':
377
+ text = processor.extract_text_from_pdf(file_content)
378
+ analysis_type = analyzer.detect_analysis_type(text)
379
+
380
+ results.append(f"## 📄 PDF: {file_name}")
381
+
382
+ if analysis_type == AnalysisType.MATHEMATICAL_MODEL:
383
+ result = analyzer.analyze_mathematical_article(text, claude_model)
384
+ else:
385
+ result = {"tipo": "PDF - Contenido no identificado", "texto": text[:1000]}
386
+
387
+ results.append(json.dumps(result, indent=2, ensure_ascii=False))
388
+
389
+ elif file_ext in ['.csv', '.xlsx', '.xls']:
390
+ results.append(f"## 📊 Archivo de datos: {file_name}")
391
+
392
+ if file_ext == '.csv':
393
+ df = processor.read_csv(file_content)
394
+ else:
395
+ df = processor.read_excel(file_content)
396
+
397
+ if df is not None:
398
+ result = analyzer.analyze_fitting_data(df, claude_model)
399
+ results.append(json.dumps(result, indent=2, ensure_ascii=False))
400
+
401
+ results.append("\n---\n")
402
 
403
+ return "\n".join(results)
404
 
405
+ def generate_implementation_code(analysis_results: str) -> str:
406
+ """Genera código de implementación basado en el análisis"""
407
  code = """
408
  import numpy as np
409
+ import pandas as pd
410
  import matplotlib.pyplot as plt
411
  from scipy.integrate import odeint
412
+ from scipy.optimize import curve_fit, differential_evolution
413
+ from sklearn.metrics import r2_score, mean_squared_error
414
+ import seaborn as sns
415
 
416
+ # Configuración de visualización
417
+ plt.style.use('seaborn-v0_8-darkgrid')
418
+ sns.set_palette("husl")
419
 
420
+ class BiotechModelFitter:
421
+ \"\"\"Clase para ajuste de modelos biotecnológicos\"\"\"
422
+
423
+ def __init__(self):
424
+ self.models = {}
425
+ self.fitted_params = {}
426
+ self.results = {}
427
+
428
+ def add_model(self, name, func, param_names):
429
+ \"\"\"Registra un nuevo modelo\"\"\"
430
+ self.models[name] = {
431
+ 'function': func,
432
+ 'parameters': param_names
433
+ }
434
+
435
+ def fit_model(self, model_name, x_data, y_data, bounds=None):
436
+ \"\"\"Ajusta modelo a datos\"\"\"
437
+ if model_name not in self.models:
438
+ raise ValueError(f"Modelo {model_name} no registrado")
439
+
440
+ model_func = self.models[model_name]['function']
441
+
442
+ # Intentar ajuste con curve_fit
443
+ try:
444
+ if bounds:
445
+ popt, pcov = curve_fit(model_func, x_data, y_data, bounds=bounds)
446
+ else:
447
+ popt, pcov = curve_fit(model_func, x_data, y_data)
448
+
449
+ # Calcular métricas
450
+ y_pred = model_func(x_data, *popt)
451
+ r2 = r2_score(y_data, y_pred)
452
+ rmse = np.sqrt(mean_squared_error(y_data, y_pred))
453
+
454
+ self.fitted_params[model_name] = popt
455
+ self.results[model_name] = {
456
+ 'parameters': dict(zip(self.models[model_name]['parameters'], popt)),
457
+ 'covariance': pcov,
458
+ 'r2': r2,
459
+ 'rmse': rmse
460
+ }
461
+
462
+ return True
463
+
464
+ except Exception as e:
465
+ print(f"Error en ajuste: {e}")
466
+ # Intentar con optimización global
467
+ return self._global_fit(model_name, x_data, y_data, bounds)
468
+
469
+ def _global_fit(self, model_name, x_data, y_data, bounds):
470
+ \"\"\"Ajuste global con differential evolution\"\"\"
471
+ model_func = self.models[model_name]['function']
472
+
473
+ def objective(params):
474
+ y_pred = model_func(x_data, *params)
475
+ return np.sum((y_data - y_pred)**2)
476
+
477
+ if not bounds:
478
+ # Bounds por defecto
479
+ n_params = len(self.models[model_name]['parameters'])
480
+ bounds = [(0, 100)] * n_params
481
+
482
+ result = differential_evolution(objective, bounds)
483
+
484
+ if result.success:
485
+ popt = result.x
486
+ y_pred = model_func(x_data, *popt)
487
+ r2 = r2_score(y_data, y_pred)
488
+ rmse = np.sqrt(mean_squared_error(y_data, y_pred))
489
+
490
+ self.fitted_params[model_name] = popt
491
+ self.results[model_name] = {
492
+ 'parameters': dict(zip(self.models[model_name]['parameters'], popt)),
493
+ 'r2': r2,
494
+ 'rmse': rmse,
495
+ 'optimization_result': result
496
+ }
497
+
498
+ return True
499
+
500
+ return False
501
+
502
+ def plot_results(self, x_data, y_data, models_to_plot=None):
503
+ \"\"\"Visualiza resultados del ajuste\"\"\"
504
+ plt.figure(figsize=(12, 8))
505
+
506
+ # Datos experimentales
507
+ plt.scatter(x_data, y_data, label='Datos experimentales',
508
+ s=50, alpha=0.7, edgecolors='black')
509
+
510
+ # Modelos ajustados
511
+ if models_to_plot is None:
512
+ models_to_plot = self.fitted_params.keys()
513
+
514
+ x_smooth = np.linspace(x_data.min(), x_data.max(), 300)
515
+
516
+ for model_name in models_to_plot:
517
+ if model_name in self.fitted_params:
518
+ model_func = self.models[model_name]['function']
519
+ params = self.fitted_params[model_name]
520
+ y_smooth = model_func(x_smooth, *params)
521
+
522
+ r2 = self.results[model_name]['r2']
523
+ plt.plot(x_smooth, y_smooth,
524
+ label=f'{model_name} (R² = {r2:.4f})',
525
+ linewidth=2.5)
526
+
527
+ plt.xlabel('Variable Independiente', fontsize=12)
528
+ plt.ylabel('Variable Dependiente', fontsize=12)
529
+ plt.title('Ajuste de Modelos Biotecnológicos', fontsize=14, fontweight='bold')
530
+ plt.legend(loc='best', frameon=True, shadow=True)
531
+ plt.grid(True, alpha=0.3)
532
+ plt.tight_layout()
533
+
534
+ return plt.gcf()
535
 
536
+ def generate_report(self):
537
+ \"\"\"Genera reporte de resultados\"\"\"
538
+ report = "# Reporte de Ajuste de Modelos\\n\\n"
539
+
540
+ for model_name, results in self.results.items():
541
+ report += f"## Modelo: {model_name}\\n\\n"
542
+ report += f"### Parámetros ajustados:\\n"
543
+
544
+ for param, value in results['parameters'].items():
545
+ report += f"- **{param}**: {value:.6f}\\n"
546
+
547
+ report += f"\\n### Métricas de ajuste:\\n"
548
+ report += f"- **R²**: {results['r2']:.6f}\\n"
549
+ report += f"- **RMSE**: {results['rmse']:.6f}\\n\\n"
550
+
551
+ return report
552
+
553
+ # Modelos predefinidos comunes
554
  def monod_model(S, mu_max, Ks):
 
555
  return mu_max * S / (Ks + S)
556
 
557
+ def logistic_growth(t, K, r, t0):
558
+ return K / (1 + np.exp(-r * (t - t0)))
 
 
 
 
 
559
 
560
+ def gompertz_model(t, A, mu, lambda_param):
561
+ return A * np.exp(-np.exp(mu * np.e / A * (lambda_param - t) + 1))
 
 
 
 
 
562
 
 
 
 
 
563
  def michaelis_menten(S, Vmax, Km):
 
564
  return Vmax * S / (Km + S)
565
 
566
+ # Ejemplo de uso
567
+ if __name__ == "__main__":
568
+ # Crear instancia del ajustador
569
+ fitter = BiotechModelFitter()
570
 
571
+ # Registrar modelos
572
+ fitter.add_model('Monod', monod_model, ['mu_max', 'Ks'])
573
+ fitter.add_model('Michaelis-Menten', michaelis_menten, ['Vmax', 'Km'])
574
+ fitter.add_model('Logistic', logistic_growth, ['K', 'r', 't0'])
575
+
576
+ print("Sistema de ajuste listo para usar!")
577
+ print("Carga tus datos y utiliza fitter.fit_model()")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578
  """
579
 
580
  return code
581
 
582
+ # Interfaz Gradio optimizada para HuggingFace
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
583
  def create_interface():
584
+ with gr.Blocks(
585
+ title="Analizador Inteligente de Modelos Biotecnológicos",
586
+ theme=gr.themes.Soft(),
587
+ css="""
588
+ .gradio-container {
589
+ font-family: 'Arial', sans-serif;
590
+ }
591
+ """
592
+ ) as demo:
593
+
594
  gr.Markdown("""
595
+ # 🧬 Analizador Inteligente de Modelos Biotecnológicos
596
 
597
+ ### 🎯 Capacidades:
598
+ - **Detección automática** del tipo de documento (artículo científico vs datos experimentales)
599
+ - **Análisis de PDFs** con modelos matemáticos biotecnológicos
600
+ - **Procesamiento de datos** CSV/Excel para ajuste de parámetros
601
+ - **Soporte para múltiples archivos** y archivos ZIP
602
+ - **Generación de código** Python para implementación
603
 
604
+ ### 📁 Tipos de archivo soportados:
605
+ - PDF (artículos científicos o reportes de datos)
606
+ - CSV/Excel (datos experimentales)
607
+ - ZIP (múltiples archivos)
 
 
608
  """)
609
 
610
  with gr.Row():
611
  with gr.Column(scale=1):
612
+ files_input = gr.File(
613
+ label="📁 Subir archivos",
614
+ file_count="multiple",
615
+ file_types=[".pdf", ".csv", ".xlsx", ".xls", ".zip"],
616
+ type="filepath"
617
  )
618
 
 
619
  model_selector = gr.Dropdown(
620
  choices=list(CLAUDE_MODELS.keys()),
621
+ value="claude-3-5-sonnet-20241022",
622
+ label="🤖 Modelo Claude",
623
+ info="Selecciona el modelo de IA"
 
 
 
 
 
 
 
 
 
 
624
  )
625
 
626
  analyze_btn = gr.Button(
627
+ "🚀 Analizar",
628
  variant="primary",
629
  size="lg"
630
  )
631
 
632
+ # Información del modelo
633
+ model_info = gr.Markdown()
634
+
635
+ def update_model_info(model):
636
+ info = CLAUDE_MODELS[model]
637
+ return f"""
638
+ **{info['name']}**
639
+
640
+ {info['description']}
641
+
642
+ *Mejor para: {info['best_for']}*
643
+ """
644
+
645
+ model_selector.change(
646
+ update_model_info,
647
+ inputs=[model_selector],
648
+ outputs=[model_info]
649
  )
650
 
651
  with gr.Column(scale=2):
652
  analysis_output = gr.Markdown(
653
+ label="📊 Resultados del Análisis"
654
+ )
655
+
656
+ code_output = gr.Code(
657
+ label="💻 Código de Implementación",
658
+ language="python",
659
+ interactive=True
660
  )
661
 
662
+ # Ejemplos
663
+ gr.Examples(
664
+ examples=[
665
+ [["examples/growth_kinetics.pdf"]],
666
+ [["examples/experimental_data.csv"]],
667
+ [["examples/multiple_files.zip"]]
668
+ ],
669
+ inputs=[files_input],
670
+ label="📚 Ejemplos"
671
+ )
672
 
673
+ # Footer
674
+ gr.Markdown("""
675
+ ---
676
+ ### 🔧 Características técnicas:
677
+ - **Base de modelos escalable**: Fácil adición de nuevos modelos matemáticos
678
+ - **Análisis con IA**: Detección automática del contexto y tipo de análisis
679
+ - **Optimizado para HuggingFace**: Configuración lista para deployment
680
+ - **Código modular**: Arquitectura flexible y mantenible
 
 
 
 
 
681
 
682
+ ### 📖 Instrucciones:
683
+ 1. Sube uno o varios archivos (PDF, CSV, Excel o ZIP)
684
+ 2. El sistema detectará automáticamente el tipo de análisis necesario
685
+ 3. Revisa los resultados y el código generado
686
+ 4. Copia el código para tu implementación
687
+ """)
688
+
689
+ # Eventos
690
  analyze_btn.click(
691
+ fn=lambda files, model: (
692
+ process_files(files, model) if files else "Por favor sube archivos para analizar",
693
+ generate_implementation_code("") if files else ""
694
+ ),
695
+ inputs=[files_input, model_selector],
696
+ outputs=[analysis_output, code_output]
697
  )
698
 
699
+ # Cargar info inicial del modelo
700
+ demo.load(
701
+ fn=lambda: update_model_info("claude-3-5-sonnet-20241022"),
702
+ outputs=[model_info]
 
703
  )
704
 
705
  return demo
706
 
707
+ # Función principal para HuggingFace Spaces
708
+ def main():
709
  if not os.getenv("ANTHROPIC_API_KEY"):
710
+ print("⚠️ Configura ANTHROPIC_API_KEY en los secretos de HuggingFace Space")
711
+ return gr.Interface(
712
+ fn=lambda x: "Por favor configura ANTHROPIC_API_KEY en los secretos del Space",
713
+ inputs=gr.Textbox(),
714
+ outputs=gr.Textbox(),
715
+ title="Error de Configuración"
716
+ )
717
+
718
+ return create_interface()
719
+
720
+ # Para ejecución local
721
+ if __name__ == "__main__":
722
+ demo = main()
723
+ if demo:
724
  demo.launch(
725
  server_name="0.0.0.0",
726
  server_port=7860,