C2MV commited on
Commit
58be36b
·
verified ·
1 Parent(s): dda9552

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +546 -98
app.py CHANGED
@@ -1,6 +1,4 @@
1
  import gradio as gr
2
- # import anthropic <- Eliminado
3
- from openai import OpenAI # <- Añadido
4
  import PyPDF2
5
  import pandas as pd
6
  import numpy as np
@@ -27,11 +25,12 @@ from reportlab.pdfbase import pdfmetrics
27
  from reportlab.pdfbase.ttfonts import TTFont
28
  import matplotlib.pyplot as plt
29
  from datetime import datetime
 
30
 
31
  # Configuración para HuggingFace
32
  os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'
33
 
34
- # Inicializar cliente OpenAI para Nebius API
35
  client = OpenAI(
36
  base_url="https://api.studio.nebius.com/v1/",
37
  api_key=os.environ.get("NEBIUS_API_KEY")
@@ -43,7 +42,7 @@ TRANSLATIONS = {
43
  'title': '🧬 Comparative Analyzer of Biotechnological Models',
44
  'subtitle': 'Specialized in comparative analysis of mathematical model fitting results',
45
  'upload_files': '📁 Upload fitting results (CSV/Excel)',
46
- 'select_model': '🤖 AI Model', # Cambiado de "Claude Model"
47
  'select_language': '🌐 Language',
48
  'select_theme': '🎨 Theme',
49
  'detail_level': '📋 Analysis detail level',
@@ -60,7 +59,7 @@ TRANSLATIONS = {
60
  'dark': 'Dark',
61
  'best_for': 'Best for',
62
  'loading': 'Loading...',
63
- 'error_no_api': 'Please configure NEBIUS_API_KEY in HuggingFace Space secrets', # Actualizado
64
  'error_no_files': 'Please upload fitting result files to analyze',
65
  'report_exported': 'Report exported successfully as',
66
  'specialized_in': '🎯 Specialized in:',
@@ -74,7 +73,7 @@ TRANSLATIONS = {
74
  'title': '🧬 Analizador Comparativo de Modelos Biotecnológicos',
75
  'subtitle': 'Especializado en análisis comparativo de resultados de ajuste de modelos matemáticos',
76
  'upload_files': '📁 Subir resultados de ajuste (CSV/Excel)',
77
- 'select_model': '🤖 Modelo de IA', # Cambiado de "Modelo Claude"
78
  'select_language': '🌐 Idioma',
79
  'select_theme': '🎨 Tema',
80
  'detail_level': '📋 Nivel de detalle del análisis',
@@ -91,7 +90,7 @@ TRANSLATIONS = {
91
  'dark': 'Oscuro',
92
  'best_for': 'Mejor para',
93
  'loading': 'Cargando...',
94
- 'error_no_api': 'Por favor configura NEBIUS_API_KEY en los secretos del Space', # Actualizado
95
  'error_no_files': 'Por favor sube archivos con resultados de ajuste para analizar',
96
  'report_exported': 'Reporte exportado exitosamente como',
97
  'specialized_in': '🎯 Especializado en:',
@@ -105,7 +104,7 @@ TRANSLATIONS = {
105
  'title': '🧬 Analyseur Comparatif de Modèles Biotechnologiques',
106
  'subtitle': 'Spécialisé dans l\'analyse comparative des résultats d\'ajustement',
107
  'upload_files': '📁 Télécharger les résultats (CSV/Excel)',
108
- 'select_model': '🤖 Modèle d\'IA', # Cambiado
109
  'select_language': '🌐 Langue',
110
  'select_theme': '🎨 Thème',
111
  'detail_level': '📋 Niveau de détail',
@@ -122,7 +121,7 @@ TRANSLATIONS = {
122
  'dark': 'Sombre',
123
  'best_for': 'Meilleur pour',
124
  'loading': 'Chargement...',
125
- 'error_no_api': 'Veuillez configurer NEBIUS_API_KEY', # Actualizado
126
  'error_no_files': 'Veuillez télécharger des fichiers à analyser',
127
  'report_exported': 'Rapport exporté avec succès comme',
128
  'specialized_in': '🎯 Spécialisé dans:',
@@ -136,7 +135,7 @@ TRANSLATIONS = {
136
  'title': '🧬 Vergleichender Analysator für Biotechnologische Modelle',
137
  'subtitle': 'Spezialisiert auf vergleichende Analyse von Modellanpassungsergebnissen',
138
  'upload_files': '📁 Ergebnisse hochladen (CSV/Excel)',
139
- 'select_model': '🤖 KI-Modell', # Cambiado
140
  'select_language': '🌐 Sprache',
141
  'select_theme': '🎨 Thema',
142
  'detail_level': '📋 Detailgrad der Analyse',
@@ -153,7 +152,7 @@ TRANSLATIONS = {
153
  'dark': 'Dunkel',
154
  'best_for': 'Am besten für',
155
  'loading': 'Laden...',
156
- 'error_no_api': 'Bitte konfigurieren Sie NEBIUS_API_KEY', # Actualizado
157
  'error_no_files': 'Bitte laden Sie Dateien zur Analyse hoch',
158
  'report_exported': 'Bericht erfolgreich exportiert als',
159
  'specialized_in': '🎯 Spezialisiert auf:',
@@ -167,7 +166,7 @@ TRANSLATIONS = {
167
  'title': '🧬 Analisador Comparativo de Modelos Biotecnológicos',
168
  'subtitle': 'Especializado em análise comparativa de resultados de ajuste',
169
  'upload_files': '📁 Carregar resultados (CSV/Excel)',
170
- 'select_model': '🤖 Modelo de IA', # Cambiado
171
  'select_language': '🌐 Idioma',
172
  'select_theme': '🎨 Tema',
173
  'detail_level': '📋 Nível de detalhe',
@@ -184,7 +183,7 @@ TRANSLATIONS = {
184
  'dark': 'Escuro',
185
  'best_for': 'Melhor para',
186
  'loading': 'Carregando...',
187
- 'error_no_api': 'Por favor configure NEBIUS_API_KEY', # Actualizado
188
  'error_no_files': 'Por favor carregue arquivos para analisar',
189
  'report_exported': 'Relatório exportado com sucesso como',
190
  'specialized_in': '🎯 Especializado em:',
@@ -245,21 +244,21 @@ class ModelRegistry:
245
  def __init__(self):
246
  self.models = {}
247
  self._initialize_default_models()
248
-
249
  def register_model(self, model: MathematicalModel):
250
  """Registra un nuevo modelo matemático"""
251
  if model.category not in self.models:
252
  self.models[model.category] = {}
253
  self.models[model.category][model.name] = model
254
-
255
  def get_model(self, category: str, name: str) -> MathematicalModel:
256
  """Obtiene un modelo específico"""
257
  return self.models.get(category, {}).get(name)
258
-
259
  def get_all_models(self) -> Dict:
260
  """Retorna todos los modelos registrados"""
261
  return self.models
262
-
263
  def _initialize_default_models(self):
264
  """Inicializa los modelos por defecto"""
265
  # Modelos de crecimiento
@@ -296,21 +295,20 @@ class ModelRegistry:
296
  # Instancia global del registro
297
  model_registry = ModelRegistry()
298
 
299
-
300
- # Modelos de Nebius disponibles (reemplaza a CLAUDE_MODELS)
301
  NEBIUS_MODELS = {
302
  "Qwen/Qwen3-14B": {
303
- "name": "Qwen 3 14B",
304
- "description": "Potente modelo de Alibaba Cloud, alojado en Nebius",
305
- "max_tokens": 8192,
306
  "best_for": "Análisis detallados y generación de código complejo."
307
- }
 
308
  }
309
 
310
-
311
  class FileProcessor:
312
  """Clase para procesar diferentes tipos de archivos"""
313
-
314
  @staticmethod
315
  def extract_text_from_pdf(pdf_file) -> str:
316
  """Extrae texto de un archivo PDF"""
@@ -322,7 +320,7 @@ class FileProcessor:
322
  return text
323
  except Exception as e:
324
  return f"Error reading PDF: {str(e)}"
325
-
326
  @staticmethod
327
  def read_csv(csv_file) -> pd.DataFrame:
328
  """Lee archivo CSV"""
@@ -330,7 +328,7 @@ class FileProcessor:
330
  return pd.read_csv(io.BytesIO(csv_file))
331
  except Exception as e:
332
  return None
333
-
334
  @staticmethod
335
  def read_excel(excel_file) -> pd.DataFrame:
336
  """Lee archivo Excel"""
@@ -338,7 +336,7 @@ class FileProcessor:
338
  return pd.read_excel(io.BytesIO(excel_file))
339
  except Exception as e:
340
  return None
341
-
342
  @staticmethod
343
  def extract_from_zip(zip_file) -> List[Tuple[str, bytes]]:
344
  """Extrae archivos de un ZIP"""
@@ -353,10 +351,9 @@ class FileProcessor:
353
  print(f"Error processing ZIP: {e}")
354
  return files
355
 
356
-
357
  class ReportExporter:
358
  """Clase para exportar reportes a diferentes formatos"""
359
-
360
  @staticmethod
361
  def export_to_docx(content: str, filename: str, language: str = 'en') -> str:
362
  """Exporta el contenido a un archivo DOCX"""
@@ -427,7 +424,7 @@ class ReportExporter:
427
  # Guardar documento
428
  doc.save(filename)
429
  return filename
430
-
431
  @staticmethod
432
  def export_to_pdf(content: str, filename: str, language: str = 'en') -> str:
433
  """Exporta el contenido a un archivo PDF"""
@@ -509,11 +506,11 @@ class ReportExporter:
509
 
510
  class AIAnalyzer:
511
  """Clase para análisis con IA"""
512
-
513
  def __init__(self, client, model_registry):
514
  self.client = client
515
  self.model_registry = model_registry
516
-
517
  def detect_analysis_type(self, content: Union[str, pd.DataFrame]) -> AnalysisType:
518
  """Detecta el tipo de análisis necesario"""
519
  if isinstance(content, pd.DataFrame):
@@ -543,18 +540,15 @@ class AIAnalyzer:
543
  """
544
 
545
  try:
546
- # CAMBIO: Se usa el nuevo método de API y el nuevo modelo
547
  response = self.client.chat.completions.create(
548
- model="Qwen/Qwen3-14B",
 
549
  max_tokens=10,
550
- temperature=0.2, # Temperatura baja para clasificación
551
- top_p=0.95,
552
  messages=[{"role": "user", "content": f"{prompt}\n\n{content[:1000]}"}]
553
  )
554
 
555
- # CAMBIO: Se ajusta el parseo de la respuesta
556
  result = response.choices[0].message.content.strip().upper()
557
-
558
  if "MODEL" in result:
559
  return AnalysisType.MATHEMATICAL_MODEL
560
  elif "RESULTS" in result:
@@ -567,7 +561,7 @@ class AIAnalyzer:
567
  except Exception as e:
568
  print(f"Error in detect_analysis_type: {e}")
569
  return AnalysisType.UNKNOWN
570
-
571
  def get_language_prompt_prefix(self, language: str) -> str:
572
  """Obtiene el prefijo del prompt según el idioma"""
573
  prefixes = {
@@ -578,8 +572,8 @@ class AIAnalyzer:
578
  'pt': "Por favor responda em português. "
579
  }
580
  return prefixes.get(language, prefixes['en'])
581
-
582
- def analyze_fitting_results(self, data: pd.DataFrame, model_name: str, detail_level: str = "detailed",
583
  language: str = "en", additional_specs: str = "") -> Dict:
584
  """Analiza resultados de ajuste de modelos con soporte multiidioma y especificaciones adicionales"""
585
 
@@ -613,45 +607,156 @@ class AIAnalyzer:
613
  Please ensure to address these specific requirements in your analysis.
614
  """ if additional_specs else ""
615
 
616
- # Prompt mejorado con instrucciones específicas para cada nivel (sin cambios)
617
  if detail_level == "detailed":
618
  prompt = f"""
619
  {lang_prefix}
 
620
  You are an expert in biotechnology and mathematical modeling. Analyze these kinetic/biotechnological model fitting results.
 
621
  {user_specs_section}
 
622
  DETAIL LEVEL: DETAILED - Provide comprehensive analysis BY EXPERIMENT
 
623
  PERFORM A COMPREHENSIVE COMPARATIVE ANALYSIS PER EXPERIMENT:
 
624
  1. **EXPERIMENT IDENTIFICATION AND OVERVIEW**
 
 
 
 
 
 
625
  2. **MODEL IDENTIFICATION AND CLASSIFICATION BY EXPERIMENT**
 
 
 
 
 
 
626
  3. **COMPARATIVE ANALYSIS PER EXPERIMENT**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
627
  4. **DETAILED COMPARATIVE TABLES**
 
 
 
 
 
 
 
 
 
 
 
 
628
  5. **PARAMETER ANALYSIS ACROSS EXPERIMENTS**
 
 
 
 
 
629
  6. **BIOLOGICAL INTERPRETATION BY EXPERIMENT**
 
 
 
 
 
 
630
  7. **OVERALL BEST MODELS DETERMINATION**
 
 
 
 
 
 
631
  8. **CONCLUSIONS AND RECOMMENDATIONS**
 
 
 
 
 
632
  Use Markdown format with clear structure. Include ALL numerical values from the data.
633
  Create clear sections for EACH EXPERIMENT.
634
  """
635
  else: # summarized
636
  prompt = f"""
637
  {lang_prefix}
 
638
  You are an expert in biotechnology. Provide a CONCISE but COMPLETE analysis BY EXPERIMENT.
 
639
  {user_specs_section}
 
640
  DETAIL LEVEL: SUMMARIZED - Be concise but include all experiments and essential information
 
641
  PROVIDE A FOCUSED COMPARATIVE ANALYSIS:
 
642
  1. **EXPERIMENTS OVERVIEW**
 
 
 
 
643
  2. **BEST MODELS BY EXPERIMENT - QUICK SUMMARY**
 
 
 
 
 
 
 
 
 
 
 
 
 
644
  3. **OVERALL WINNERS ACROSS ALL EXPERIMENTS**
 
 
 
 
 
645
  4. **QUICK COMPARISON TABLE**
 
 
 
 
 
646
  5. **KEY FINDINGS**
 
 
 
 
647
  6. **PRACTICAL RECOMMENDATIONS**
 
 
 
 
 
648
  Keep it concise but include ALL experiments and model names with their key metrics.
649
  """
650
 
651
  try:
652
- # CAMBIO: Llamada a la API de Nebius
653
  response = self.client.chat.completions.create(
654
- model=model_name,
655
  temperature=0.6,
656
  top_p=0.95,
657
  max_tokens=4000,
@@ -661,9 +766,6 @@ class AIAnalyzer:
661
  }]
662
  )
663
 
664
- # CAMBIO: Parseo de la respuesta
665
- analysis_text = response.choices[0].message.content
666
-
667
  # Análisis adicional para generar código con valores numéricos reales
668
  code_prompt = f"""
669
  {lang_prefix}
@@ -696,9 +798,9 @@ class AIAnalyzer:
696
  Format: Complete, executable Python code with actual data values embedded.
697
  """
698
 
699
- # CAMBIO: Llamada a la API para generar código
700
  code_response = self.client.chat.completions.create(
701
- model=model_name,
702
  temperature=0.6,
703
  top_p=0.95,
704
  max_tokens=3000,
@@ -708,13 +810,10 @@ class AIAnalyzer:
708
  }]
709
  )
710
 
711
- # CAMBIO: Parseo de la respuesta del código
712
- code_text = code_response.choices[0].message.content
713
-
714
  return {
715
  "tipo": "Comparative Analysis of Mathematical Models",
716
- "analisis_completo": analysis_text,
717
- "codigo_implementacion": code_text,
718
  "resumen_datos": {
719
  "n_modelos": len(data),
720
  "columnas": list(data.columns),
@@ -729,14 +828,14 @@ class AIAnalyzer:
729
  except Exception as e:
730
  return {"error": str(e)}
731
 
732
- def process_files(files, model_name: str, detail_level: str = "detailed",
733
- language: str = "en", additional_specs: str = "") -> Tuple[str, str]:
734
  """Procesa múltiples archivos con soporte de idioma y especificaciones adicionales"""
735
  processor = FileProcessor()
736
  analyzer = AIAnalyzer(client, model_registry)
737
  results = []
738
  all_code = []
739
-
740
  for file in files:
741
  if file is None:
742
  continue
@@ -776,19 +875,346 @@ def process_files(files, model_name: str, detail_level: str = "detailed",
776
  all_code.append(result["codigo_implementacion"])
777
 
778
  results.append("\n---\n")
779
-
780
  analysis_text = "\n".join(results)
781
  code_text = "\n\n# " + "="*50 + "\n\n".join(all_code) if all_code else generate_implementation_code(analysis_text)
782
-
783
  return analysis_text, code_text
784
 
785
  def generate_implementation_code(analysis_results: str) -> str:
786
- """Genera código de implementación con análisis por experimento (función de respaldo)"""
787
- # Esta función no necesita cambios, ya que no hace llamadas a la API
788
- # ... (el código de esta función permanece igual) ...
789
- # Se omite por brevedad, el código original es correcto.
790
- return "" # Devuelve un string vacío o un esqueleto básico si se necesita.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
791
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
792
 
793
  # Estado global para almacenar resultados
794
  class AppState:
@@ -810,9 +1236,9 @@ def export_report(export_format: str, language: str) -> Tuple[str, str]:
810
  'pt': "Nenhuma análise disponível para exportar"
811
  }
812
  return error_msg.get(language, error_msg['en']), ""
813
-
814
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
815
-
816
  try:
817
  if export_format == "DOCX":
818
  filename = f"biotech_analysis_report_{timestamp}.docx"
@@ -831,29 +1257,29 @@ def create_interface():
831
  # Estado inicial
832
  current_theme = "light"
833
  current_language = "en"
834
-
835
  def update_interface_language(language):
836
  """Actualiza el idioma de la interfaz"""
837
  app_state.current_language = language
838
  t = TRANSLATIONS[language]
839
 
840
  return [
841
- gr.update(value=f"# {t['title']}"),
842
- gr.update(value=t['subtitle']),
843
- gr.update(label=t['upload_files']),
844
- gr.update(label=t['select_model']),
845
- gr.update(label=t['select_language']),
846
- gr.update(label=t['select_theme']),
847
- gr.update(label=t['detail_level']),
848
- gr.update(label=t['additional_specs'], placeholder=t['additional_specs_placeholder']),
849
- gr.update(value=t['analyze_button']),
850
- gr.update(label=t['export_format']),
851
- gr.update(value=t['export_button']),
852
- gr.update(label=t['comparative_analysis']),
853
- gr.update(label=t['implementation_code']),
854
- gr.update(label=t['data_format'])
855
  ]
856
-
857
  def process_and_store(files, model, detail, language, additional_specs):
858
  """Procesa archivos y almacena resultados"""
859
  if not files:
@@ -864,8 +1290,9 @@ def create_interface():
864
  app_state.current_analysis = analysis
865
  app_state.current_code = code
866
  return analysis, code
867
-
868
  with gr.Blocks(theme=THEMES[current_theme]) as demo:
 
869
  with gr.Row():
870
  with gr.Column(scale=3):
871
  title_text = gr.Markdown(f"# {TRANSLATIONS[current_language]['title']}")
@@ -895,12 +1322,13 @@ def create_interface():
895
  type="filepath"
896
  )
897
 
898
- # CAMBIO: Selector de modelo actualizado a Nebius
 
899
  model_selector = gr.Dropdown(
900
  choices=list(NEBIUS_MODELS.keys()),
901
- value="Qwen/Qwen3-14B",
902
  label=TRANSLATIONS[current_language]['select_model'],
903
- info=f"{TRANSLATIONS[current_language]['best_for']}: {NEBIUS_MODELS['Qwen/Qwen3-14B']['best_for']}"
904
  )
905
 
906
  detail_level = gr.Radio(
@@ -912,6 +1340,7 @@ def create_interface():
912
  label=TRANSLATIONS[current_language]['detail_level']
913
  )
914
 
 
915
  additional_specs = gr.Textbox(
916
  label=TRANSLATIONS[current_language]['additional_specs'],
917
  placeholder=TRANSLATIONS[current_language]['additional_specs_placeholder'],
@@ -975,6 +1404,8 @@ def create_interface():
975
  |------------|-------|------|-----|------|-----|-----|--------|-------|------------|
976
  | pH_7.0 | Monod | Biomass | 0.985 | 0.023 | -45.2 | -42.1 | 0.45 | 2.1 | {...} |
977
  | pH_7.0 | Logistic | Biomass | 0.976 | 0.031 | -42.1 | -39.5 | 0.42 | - | {...} |
 
 
978
 
979
  **Important columns:**
980
  - **Experiment**: Experimental condition identifier
@@ -984,7 +1415,7 @@ def create_interface():
984
  - **Parameters**: Model-specific parameters
985
  """)
986
 
987
- # CAMBIO: Ejemplos actualizados para usar el nuevo modelo
988
  examples = gr.Examples(
989
  examples=[
990
  [["examples/biomass_models_comparison.csv"], "Qwen/Qwen3-14B", "detailed", ""],
@@ -994,6 +1425,7 @@ def create_interface():
994
  label=TRANSLATIONS[current_language]['examples']
995
  )
996
 
 
997
  language_selector.change(
998
  update_interface_language,
999
  inputs=[language_selector],
@@ -1006,6 +1438,7 @@ def create_interface():
1006
  )
1007
 
1008
  def change_theme(theme_name):
 
1009
  return gr.Info("Theme will be applied on next page load")
1010
 
1011
  theme_selector.change(
@@ -1032,34 +1465,49 @@ def create_interface():
1032
  inputs=[export_format, language_selector],
1033
  outputs=[export_status, export_file]
1034
  )
1035
-
1036
  return demo
1037
 
1038
  # Función principal
1039
  def main():
1040
- # CAMBIO: Verificación de la nueva clave API
1041
  if not os.getenv("NEBIUS_API_KEY"):
1042
  print("⚠️ Configure NEBIUS_API_KEY in HuggingFace Space secrets")
1043
- # Actualizar el mensaje de error en la UI
1044
- error_msg = TRANSLATIONS['en']['error_no_api']
1045
  return gr.Interface(
1046
- fn=lambda x: error_msg,
1047
  inputs=gr.Textbox(),
1048
  outputs=gr.Textbox(),
1049
  title="Configuration Error"
1050
  )
1051
-
1052
  return create_interface()
1053
 
1054
  # Para ejecución local
1055
  if __name__ == "__main__":
1056
- # He eliminado el código de ejemplo de `__main__` que estaba fuera de la función
1057
- # `generate_implementation_code` para mayor claridad y evitar que se ejecute al importar.
1058
- # El punto de entrada principal es la interfaz de Gradio.
1059
  demo = main()
1060
  if demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1061
  demo.launch(
1062
  server_name="0.0.0.0",
1063
  server_port=7860,
1064
- share=os.getenv("GRADIO_SHARE", "false").lower() == "true"
1065
  )
 
1
  import gradio as gr
 
 
2
  import PyPDF2
3
  import pandas as pd
4
  import numpy as np
 
25
  from reportlab.pdfbase.ttfonts import TTFont
26
  import matplotlib.pyplot as plt
27
  from datetime import datetime
28
+ from openai import OpenAI # CAMBIO: Importación de la nueva librería
29
 
30
  # Configuración para HuggingFace
31
  os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'
32
 
33
+ # CAMBIO: Inicializar cliente OpenAI para Nebius
34
  client = OpenAI(
35
  base_url="https://api.studio.nebius.com/v1/",
36
  api_key=os.environ.get("NEBIUS_API_KEY")
 
42
  'title': '🧬 Comparative Analyzer of Biotechnological Models',
43
  'subtitle': 'Specialized in comparative analysis of mathematical model fitting results',
44
  'upload_files': '📁 Upload fitting results (CSV/Excel)',
45
+ 'select_model': '🤖 AI Model', # CAMBIO
46
  'select_language': '🌐 Language',
47
  'select_theme': '🎨 Theme',
48
  'detail_level': '📋 Analysis detail level',
 
59
  'dark': 'Dark',
60
  'best_for': 'Best for',
61
  'loading': 'Loading...',
62
+ 'error_no_api': 'Please configure NEBIUS_API_KEY in HuggingFace Space secrets', # CAMBIO
63
  'error_no_files': 'Please upload fitting result files to analyze',
64
  'report_exported': 'Report exported successfully as',
65
  'specialized_in': '🎯 Specialized in:',
 
73
  'title': '🧬 Analizador Comparativo de Modelos Biotecnológicos',
74
  'subtitle': 'Especializado en análisis comparativo de resultados de ajuste de modelos matemáticos',
75
  'upload_files': '📁 Subir resultados de ajuste (CSV/Excel)',
76
+ 'select_model': '🤖 Modelo de IA', # CAMBIO
77
  'select_language': '🌐 Idioma',
78
  'select_theme': '🎨 Tema',
79
  'detail_level': '📋 Nivel de detalle del análisis',
 
90
  'dark': 'Oscuro',
91
  'best_for': 'Mejor para',
92
  'loading': 'Cargando...',
93
+ 'error_no_api': 'Por favor configura NEBIUS_API_KEY en los secretos del Space', # CAMBIO
94
  'error_no_files': 'Por favor sube archivos con resultados de ajuste para analizar',
95
  'report_exported': 'Reporte exportado exitosamente como',
96
  'specialized_in': '🎯 Especializado en:',
 
104
  'title': '🧬 Analyseur Comparatif de Modèles Biotechnologiques',
105
  'subtitle': 'Spécialisé dans l\'analyse comparative des résultats d\'ajustement',
106
  'upload_files': '📁 Télécharger les résultats (CSV/Excel)',
107
+ 'select_model': '🤖 Modèle d\'IA', # CAMBIO
108
  'select_language': '🌐 Langue',
109
  'select_theme': '🎨 Thème',
110
  'detail_level': '📋 Niveau de détail',
 
121
  'dark': 'Sombre',
122
  'best_for': 'Meilleur pour',
123
  'loading': 'Chargement...',
124
+ 'error_no_api': 'Veuillez configurer NEBIUS_API_KEY', # CAMBIO
125
  'error_no_files': 'Veuillez télécharger des fichiers à analyser',
126
  'report_exported': 'Rapport exporté avec succès comme',
127
  'specialized_in': '🎯 Spécialisé dans:',
 
135
  'title': '🧬 Vergleichender Analysator für Biotechnologische Modelle',
136
  'subtitle': 'Spezialisiert auf vergleichende Analyse von Modellanpassungsergebnissen',
137
  'upload_files': '📁 Ergebnisse hochladen (CSV/Excel)',
138
+ 'select_model': '🤖 KI-Modell', # CAMBIO
139
  'select_language': '🌐 Sprache',
140
  'select_theme': '🎨 Thema',
141
  'detail_level': '📋 Detailgrad der Analyse',
 
152
  'dark': 'Dunkel',
153
  'best_for': 'Am besten für',
154
  'loading': 'Laden...',
155
+ 'error_no_api': 'Bitte konfigurieren Sie NEBIUS_API_KEY', # CAMBIO
156
  'error_no_files': 'Bitte laden Sie Dateien zur Analyse hoch',
157
  'report_exported': 'Bericht erfolgreich exportiert als',
158
  'specialized_in': '🎯 Spezialisiert auf:',
 
166
  'title': '🧬 Analisador Comparativo de Modelos Biotecnológicos',
167
  'subtitle': 'Especializado em análise comparativa de resultados de ajuste',
168
  'upload_files': '📁 Carregar resultados (CSV/Excel)',
169
+ 'select_model': '🤖 Modelo de IA', # CAMBIO
170
  'select_language': '🌐 Idioma',
171
  'select_theme': '🎨 Tema',
172
  'detail_level': '📋 Nível de detalhe',
 
183
  'dark': 'Escuro',
184
  'best_for': 'Melhor para',
185
  'loading': 'Carregando...',
186
+ 'error_no_api': 'Por favor configure NEBIUS_API_KEY', # CAMBIO
187
  'error_no_files': 'Por favor carregue arquivos para analisar',
188
  'report_exported': 'Relatório exportado com sucesso como',
189
  'specialized_in': '🎯 Especializado em:',
 
244
  def __init__(self):
245
  self.models = {}
246
  self._initialize_default_models()
247
+
248
  def register_model(self, model: MathematicalModel):
249
  """Registra un nuevo modelo matemático"""
250
  if model.category not in self.models:
251
  self.models[model.category] = {}
252
  self.models[model.category][model.name] = model
253
+
254
  def get_model(self, category: str, name: str) -> MathematicalModel:
255
  """Obtiene un modelo específico"""
256
  return self.models.get(category, {}).get(name)
257
+
258
  def get_all_models(self) -> Dict:
259
  """Retorna todos los modelos registrados"""
260
  return self.models
261
+
262
  def _initialize_default_models(self):
263
  """Inicializa los modelos por defecto"""
264
  # Modelos de crecimiento
 
295
  # Instancia global del registro
296
  model_registry = ModelRegistry()
297
 
298
+ # CAMBIO: Modelos de Nebius en lugar de Claude
 
299
  NEBIUS_MODELS = {
300
  "Qwen/Qwen3-14B": {
301
+ "name": "Qwen 3 (14B)",
302
+ "description": "Modelo potente y versátil de la familia Qwen.",
303
+ "max_tokens": 4096,
304
  "best_for": "Análisis detallados y generación de código complejo."
305
+ },
306
+ # Puedes añadir más modelos de Nebius aquí si están disponibles
307
  }
308
 
 
309
  class FileProcessor:
310
  """Clase para procesar diferentes tipos de archivos"""
311
+
312
  @staticmethod
313
  def extract_text_from_pdf(pdf_file) -> str:
314
  """Extrae texto de un archivo PDF"""
 
320
  return text
321
  except Exception as e:
322
  return f"Error reading PDF: {str(e)}"
323
+
324
  @staticmethod
325
  def read_csv(csv_file) -> pd.DataFrame:
326
  """Lee archivo CSV"""
 
328
  return pd.read_csv(io.BytesIO(csv_file))
329
  except Exception as e:
330
  return None
331
+
332
  @staticmethod
333
  def read_excel(excel_file) -> pd.DataFrame:
334
  """Lee archivo Excel"""
 
336
  return pd.read_excel(io.BytesIO(excel_file))
337
  except Exception as e:
338
  return None
339
+
340
  @staticmethod
341
  def extract_from_zip(zip_file) -> List[Tuple[str, bytes]]:
342
  """Extrae archivos de un ZIP"""
 
351
  print(f"Error processing ZIP: {e}")
352
  return files
353
 
 
354
  class ReportExporter:
355
  """Clase para exportar reportes a diferentes formatos"""
356
+
357
  @staticmethod
358
  def export_to_docx(content: str, filename: str, language: str = 'en') -> str:
359
  """Exporta el contenido a un archivo DOCX"""
 
424
  # Guardar documento
425
  doc.save(filename)
426
  return filename
427
+
428
  @staticmethod
429
  def export_to_pdf(content: str, filename: str, language: str = 'en') -> str:
430
  """Exporta el contenido a un archivo PDF"""
 
506
 
507
  class AIAnalyzer:
508
  """Clase para análisis con IA"""
509
+
510
  def __init__(self, client, model_registry):
511
  self.client = client
512
  self.model_registry = model_registry
513
+
514
  def detect_analysis_type(self, content: Union[str, pd.DataFrame]) -> AnalysisType:
515
  """Detecta el tipo de análisis necesario"""
516
  if isinstance(content, pd.DataFrame):
 
540
  """
541
 
542
  try:
543
+ # CAMBIO: Llamada a la API y acceso a la respuesta
544
  response = self.client.chat.completions.create(
545
+ model="Qwen/Qwen3-14B", # Usar un modelo rápido disponible en Nebius
546
+ temperature=0.1,
547
  max_tokens=10,
 
 
548
  messages=[{"role": "user", "content": f"{prompt}\n\n{content[:1000]}"}]
549
  )
550
 
 
551
  result = response.choices[0].message.content.strip().upper()
 
552
  if "MODEL" in result:
553
  return AnalysisType.MATHEMATICAL_MODEL
554
  elif "RESULTS" in result:
 
561
  except Exception as e:
562
  print(f"Error in detect_analysis_type: {e}")
563
  return AnalysisType.UNKNOWN
564
+
565
  def get_language_prompt_prefix(self, language: str) -> str:
566
  """Obtiene el prefijo del prompt según el idioma"""
567
  prefixes = {
 
572
  'pt': "Por favor responda em português. "
573
  }
574
  return prefixes.get(language, prefixes['en'])
575
+
576
+ def analyze_fitting_results(self, data: pd.DataFrame, nebius_model: str, detail_level: str = "detailed",
577
  language: str = "en", additional_specs: str = "") -> Dict:
578
  """Analiza resultados de ajuste de modelos con soporte multiidioma y especificaciones adicionales"""
579
 
 
607
  Please ensure to address these specific requirements in your analysis.
608
  """ if additional_specs else ""
609
 
610
+ # Prompt mejorado con instrucciones específicas para cada nivel
611
  if detail_level == "detailed":
612
  prompt = f"""
613
  {lang_prefix}
614
+
615
  You are an expert in biotechnology and mathematical modeling. Analyze these kinetic/biotechnological model fitting results.
616
+
617
  {user_specs_section}
618
+
619
  DETAIL LEVEL: DETAILED - Provide comprehensive analysis BY EXPERIMENT
620
+
621
  PERFORM A COMPREHENSIVE COMPARATIVE ANALYSIS PER EXPERIMENT:
622
+
623
  1. **EXPERIMENT IDENTIFICATION AND OVERVIEW**
624
+ - List ALL experiments/conditions tested (e.g., pH levels, temperatures, time points)
625
+ - For EACH experiment, identify:
626
+ * Experimental conditions
627
+ * Number of models tested
628
+ * Variables measured (biomass, substrate, product)
629
+
630
  2. **MODEL IDENTIFICATION AND CLASSIFICATION BY EXPERIMENT**
631
+ For EACH EXPERIMENT separately:
632
+ - Identify ALL fitted mathematical models BY NAME
633
+ - Classify them: biomass growth, substrate consumption, product formation
634
+ - Show the mathematical equation of each model
635
+ - List parameter values obtained for that specific experiment
636
+
637
  3. **COMPARATIVE ANALYSIS PER EXPERIMENT**
638
+ Create a section for EACH EXPERIMENT showing:
639
+
640
+ **EXPERIMENT [Name/Condition]:**
641
+
642
+ a) **BIOMASS MODELS** (if applicable):
643
+ - Best model: [Name] with R²=[value], RMSE=[value]
644
+ - Parameters: μmax=[value], Xmax=[value], etc.
645
+ - Ranking of all biomass models tested
646
+
647
+ b) **SUBSTRATE MODELS** (if applicable):
648
+ - Best model: [Name] with R²=[value], RMSE=[value]
649
+ - Parameters: Ks=[value], Yxs=[value], etc.
650
+ - Ranking of all substrate models tested
651
+
652
+ c) **PRODUCT MODELS** (if applicable):
653
+ - Best model: [Name] with R²=[value], RMSE=[value]
654
+ - Parameters: α=[value], β=[value], etc.
655
+ - Ranking of all product models tested
656
+
657
  4. **DETAILED COMPARATIVE TABLES**
658
+
659
+ **Table 1: Summary by Experiment and Variable Type**
660
+ | Experiment | Variable | Best Model | R² | RMSE | Key Parameters | Ranking |
661
+ |------------|----------|------------|-------|------|----------------|---------|
662
+ | Exp1 | Biomass | [Name] | [val] | [val]| μmax=X | 1 |
663
+ | Exp1 | Substrate| [Name] | [val] | [val]| Ks=Y | 1 |
664
+ | Exp1 | Product | [Name] | [val] | [val]| α=Z | 1 |
665
+ | Exp2 | Biomass | [Name] | [val] | [val]| μmax=X2 | 1 |
666
+
667
+ **Table 2: Complete Model Comparison Across All Experiments**
668
+ | Model Name | Type | Exp1_R² | Exp1_RMSE | Exp2_R² | Exp2_RMSE | Avg_R² | Best_For |
669
+
670
  5. **PARAMETER ANALYSIS ACROSS EXPERIMENTS**
671
+ - Compare how parameters change between experiments
672
+ - Identify trends (e.g., μmax increases with temperature)
673
+ - Calculate average parameters and variability
674
+ - Suggest optimal conditions based on parameters
675
+
676
  6. **BIOLOGICAL INTERPRETATION BY EXPERIMENT**
677
+ For each experiment, explain:
678
+ - What the parameter values mean biologically
679
+ - Whether values are realistic for the conditions
680
+ - Key differences between experiments
681
+ - Critical control parameters identified
682
+
683
  7. **OVERALL BEST MODELS DETERMINATION**
684
+ - **BEST BIOMASS MODEL OVERALL**: [Name] - performs best in [X] out of [Y] experiments
685
+ - **BEST SUBSTRATE MODEL OVERALL**: [Name] - average R²=[value]
686
+ - **BEST PRODUCT MODEL OVERALL**: [Name] - most consistent across conditions
687
+
688
+ Justify with numerical evidence from multiple experiments.
689
+
690
  8. **CONCLUSIONS AND RECOMMENDATIONS**
691
+ - Which models are most robust across different conditions
692
+ - Specific models to use for each experimental condition
693
+ - Confidence intervals and prediction reliability
694
+ - Scale-up recommendations with specific values
695
+
696
  Use Markdown format with clear structure. Include ALL numerical values from the data.
697
  Create clear sections for EACH EXPERIMENT.
698
  """
699
  else: # summarized
700
  prompt = f"""
701
  {lang_prefix}
702
+
703
  You are an expert in biotechnology. Provide a CONCISE but COMPLETE analysis BY EXPERIMENT.
704
+
705
  {user_specs_section}
706
+
707
  DETAIL LEVEL: SUMMARIZED - Be concise but include all experiments and essential information
708
+
709
  PROVIDE A FOCUSED COMPARATIVE ANALYSIS:
710
+
711
  1. **EXPERIMENTS OVERVIEW**
712
+ - Total experiments analyzed: [number]
713
+ - Conditions tested: [list]
714
+ - Variables measured: biomass/substrate/product
715
+
716
  2. **BEST MODELS BY EXPERIMENT - QUICK SUMMARY**
717
+
718
+ 📊 **EXPERIMENT 1 [Name/Condition]:**
719
+ - Biomass: [Model] (R²=[value])
720
+ - Substrate: [Model] (R²=[value])
721
+ - Product: [Model] (R²=[value])
722
+
723
+ 📊 **EXPERIMENT 2 [Name/Condition]:**
724
+ - Biomass: [Model] (R²=[value])
725
+ - Substrate: [Model] (R²=[value])
726
+ - Product: [Model] (R²=[value])
727
+
728
+ [Continue for all experiments...]
729
+
730
  3. **OVERALL WINNERS ACROSS ALL EXPERIMENTS**
731
+ 🏆 **Best Models Overall:**
732
+ - **Biomass**: [Model] - Best in [X]/[Y] experiments
733
+ - **Substrate**: [Model] - Average R²=[value]
734
+ - **Product**: [Model] - Most consistent performance
735
+
736
  4. **QUICK COMPARISON TABLE**
737
+ | Experiment | Best Biomass | Best Substrate | Best Product | Overall R² |
738
+ |------------|--------------|----------------|--------------|------------|
739
+ | Exp1 | [Model] | [Model] | [Model] | [avg] |
740
+ | Exp2 | [Model] | [Model] | [Model] | [avg] |
741
+
742
  5. **KEY FINDINGS**
743
+ - Parameter ranges across experiments: μmax=[min-max], Ks=[min-max]
744
+ - Best conditions identified: [specific values]
745
+ - Most robust models: [list with reasons]
746
+
747
  6. **PRACTICAL RECOMMENDATIONS**
748
+ - For biomass prediction: Use [Model]
749
+ - For substrate monitoring: Use [Model]
750
+ - For product estimation: Use [Model]
751
+ - Critical parameters: [list with values]
752
+
753
  Keep it concise but include ALL experiments and model names with their key metrics.
754
  """
755
 
756
  try:
757
+ # CAMBIO: Llamada a la API y acceso a la respuesta
758
  response = self.client.chat.completions.create(
759
+ model=nebius_model,
760
  temperature=0.6,
761
  top_p=0.95,
762
  max_tokens=4000,
 
766
  }]
767
  )
768
 
 
 
 
769
  # Análisis adicional para generar código con valores numéricos reales
770
  code_prompt = f"""
771
  {lang_prefix}
 
798
  Format: Complete, executable Python code with actual data values embedded.
799
  """
800
 
801
+ # CAMBIO: Llamada a la API y acceso a la respuesta
802
  code_response = self.client.chat.completions.create(
803
+ model=nebius_model,
804
  temperature=0.6,
805
  top_p=0.95,
806
  max_tokens=3000,
 
810
  }]
811
  )
812
 
 
 
 
813
  return {
814
  "tipo": "Comparative Analysis of Mathematical Models",
815
+ "analisis_completo": response.choices[0].message.content,
816
+ "codigo_implementacion": code_response.choices[0].message.content,
817
  "resumen_datos": {
818
  "n_modelos": len(data),
819
  "columnas": list(data.columns),
 
828
  except Exception as e:
829
  return {"error": str(e)}
830
 
831
+ def process_files(files, model_name: str, detail_level: str = "detailed",
832
+ language: str = "en", additional_specs: str = "") -> Tuple[str, str]:
833
  """Procesa múltiples archivos con soporte de idioma y especificaciones adicionales"""
834
  processor = FileProcessor()
835
  analyzer = AIAnalyzer(client, model_registry)
836
  results = []
837
  all_code = []
838
+
839
  for file in files:
840
  if file is None:
841
  continue
 
875
  all_code.append(result["codigo_implementacion"])
876
 
877
  results.append("\n---\n")
878
+
879
  analysis_text = "\n".join(results)
880
  code_text = "\n\n# " + "="*50 + "\n\n".join(all_code) if all_code else generate_implementation_code(analysis_text)
881
+
882
  return analysis_text, code_text
883
 
884
  def generate_implementation_code(analysis_results: str) -> str:
885
+ """Genera código de implementación con análisis por experimento"""
886
+ code = """
887
+ import numpy as np
888
+ import pandas as pd
889
+ import matplotlib.pyplot as plt
890
+ from scipy.integrate import odeint
891
+ from scipy.optimize import curve_fit, differential_evolution
892
+ from sklearn.metrics import r2_score, mean_squared_error
893
+ import seaborn as sns
894
+ from typing import Dict, List, Tuple, Optional
895
+
896
+ # Visualization configuration
897
+ plt.style.use('seaborn-v0_8-darkgrid')
898
+ sns.set_palette("husl")
899
+
900
+ class ExperimentalModelAnalyzer:
901
+ \"\"\"
902
+ Class for comparative analysis of biotechnological models across multiple experiments.
903
+ Analyzes biomass, substrate and product models separately for each experimental condition.
904
+ \"\"\"
905
+
906
+ def __init__(self):
907
+ self.results_df = None
908
+ self.experiments = {}
909
+ self.best_models_by_experiment = {}
910
+ self.overall_best_models = {
911
+ 'biomass': None,
912
+ 'substrate': None,
913
+ 'product': None
914
+ }
915
+
916
+ def load_results(self, file_path: str = None, data_dict: dict = None) -> pd.DataFrame:
917
+ \"\"\"Load fitting results from CSV/Excel file or dictionary\"\"\"
918
+ if data_dict:
919
+ self.results_df = pd.DataFrame(data_dict)
920
+ elif file_path:
921
+ if file_path.endswith('.csv'):
922
+ self.results_df = pd.read_csv(file_path)
923
+ else:
924
+ self.results_df = pd.read_excel(file_path)
925
+
926
+ print(f"✅ Data loaded: {len(self.results_df)} models")
927
+ print(f"📊 Available columns: {list(self.results_df.columns)}")
928
+
929
+ # Identify experiments
930
+ if 'Experiment' in self.results_df.columns:
931
+ self.experiments = self.results_df.groupby('Experiment').groups
932
+ print(f"🧪 Experiments found: {list(self.experiments.keys())}")
933
+
934
+ return self.results_df
935
+
936
+ def analyze_by_experiment(self,
937
+ experiment_col: str = 'Experiment',
938
+ model_col: str = 'Model',
939
+ type_col: str = 'Type',
940
+ r2_col: str = 'R2',
941
+ rmse_col: str = 'RMSE') -> Dict:
942
+ \"\"\"
943
+ Analyze models by experiment and variable type.
944
+ Identifies best models for biomass, substrate, and product in each experiment.
945
+ \"\"\"
946
+ if self.results_df is None:
947
+ raise ValueError("First load data with load_results()")
948
+
949
+ results_by_exp = {}
950
+
951
+ # Get unique experiments
952
+ if experiment_col in self.results_df.columns:
953
+ experiments = self.results_df[experiment_col].unique()
954
+ else:
955
+ experiments = ['All_Data']
956
+ self.results_df[experiment_col] = 'All_Data'
957
+
958
+ print("\\n" + "="*80)
959
+ print("📊 ANALYSIS BY EXPERIMENT AND VARIABLE TYPE")
960
+ print("="*80)
961
+
962
+ for exp in experiments:
963
+ print(f"\\n🧪 EXPERIMENT: {exp}")
964
+ print("-"*50)
965
+
966
+ exp_data = self.results_df[self.results_df[experiment_col] == exp]
967
+ results_by_exp[exp] = {}
968
+
969
+ # Analyze by variable type if available
970
+ if type_col in exp_data.columns:
971
+ var_types = exp_data[type_col].unique()
972
+
973
+ for var_type in var_types:
974
+ var_data = exp_data[exp_data[type_col] == var_type]
975
+
976
+ if not var_data.empty:
977
+ # Find best model for this variable type
978
+ best_idx = var_data[r2_col].idxmax()
979
+ best_model = var_data.loc[best_idx]
980
+
981
+ results_by_exp[exp][var_type] = {
982
+ 'best_model': best_model[model_col],
983
+ 'r2': best_model[r2_col],
984
+ 'rmse': best_model[rmse_col],
985
+ 'all_models': var_data[[model_col, r2_col, rmse_col]].to_dict('records')
986
+ }
987
+
988
+ print(f"\\n 📈 {var_type.upper()}:")
989
+ print(f" Best Model: {best_model[model_col]}")
990
+ print(f" R² = {best_model[r2_col]:.4f}")
991
+ print(f" RMSE = {best_model[rmse_col]:.4f}")
992
+
993
+ # Show all models for this variable
994
+ print(f"\\n All {var_type} models tested:")
995
+ for _, row in var_data.iterrows():
996
+ print(f" - {row[model_col]}: R²={row[r2_col]:.4f}, RMSE={row[rmse_col]:.4f}")
997
+ else:
998
+ # If no type column, analyze all models together
999
+ best_idx = exp_data[r2_col].idxmax()
1000
+ best_model = exp_data.loc[best_idx]
1001
+
1002
+ results_by_exp[exp]['all'] = {
1003
+ 'best_model': best_model[model_col],
1004
+ 'r2': best_model[r2_col],
1005
+ 'rmse': best_model[rmse_col],
1006
+ 'all_models': exp_data[[model_col, r2_col, rmse_col]].to_dict('records')
1007
+ }
1008
+
1009
+ self.best_models_by_experiment = results_by_exp
1010
+
1011
+ # Determine overall best models
1012
+ self._determine_overall_best_models()
1013
+
1014
+ return results_by_exp
1015
+
1016
+ def _determine_overall_best_models(self):
1017
+ \"\"\"Determine the best models across all experiments\"\"\"
1018
+ print("\\n" + "="*80)
1019
+ print("🏆 OVERALL BEST MODELS ACROSS ALL EXPERIMENTS")
1020
+ print("="*80)
1021
+
1022
+ # Aggregate performance by model and type
1023
+ model_performance = {}
1024
+
1025
+ for exp, exp_results in self.best_models_by_experiment.items():
1026
+ for var_type, var_results in exp_results.items():
1027
+ if var_type not in model_performance:
1028
+ model_performance[var_type] = {}
1029
+
1030
+ for model_data in var_results['all_models']:
1031
+ model_name = model_data['Model']
1032
+ if model_name not in model_performance[var_type]:
1033
+ model_performance[var_type][model_name] = {
1034
+ 'r2_values': [],
1035
+ 'rmse_values': [],
1036
+ 'experiments': []
1037
+ }
1038
+
1039
+ model_performance[var_type][model_name]['r2_values'].append(model_data['R2'])
1040
+ model_performance[var_type][model_name]['rmse_values'].append(model_data['RMSE'])
1041
+ model_performance[var_type][model_name]['experiments'].append(exp)
1042
+
1043
+ # Calculate average performance and select best
1044
+ for var_type, models in model_performance.items():
1045
+ best_avg_r2 = -1
1046
+ best_model = None
1047
+
1048
+ print(f"\\n📊 {var_type.upper()} MODELS:")
1049
+ for model_name, perf_data in models.items():
1050
+ avg_r2 = np.mean(perf_data['r2_values'])
1051
+ avg_rmse = np.mean(perf_data['rmse_values'])
1052
+ n_exp = len(perf_data['experiments'])
1053
+
1054
+ print(f" {model_name}:")
1055
+ print(f" Average R² = {avg_r2:.4f}")
1056
+ print(f" Average RMSE = {avg_rmse:.4f}")
1057
+ print(f" Tested in {n_exp} experiments")
1058
+
1059
+ if avg_r2 > best_avg_r2:
1060
+ best_avg_r2 = avg_r2
1061
+ best_model = {
1062
+ 'name': model_name,
1063
+ 'avg_r2': avg_r2,
1064
+ 'avg_rmse': avg_rmse,
1065
+ 'n_experiments': n_exp
1066
+ }
1067
+
1068
+ if var_type.lower() in ['biomass', 'substrate', 'product']:
1069
+ self.overall_best_models[var_type.lower()] = best_model
1070
+ print(f"\\n 🏆 BEST {var_type.upper()} MODEL: {best_model['name']} (Avg R²={best_model['avg_r2']:.4f})")
1071
+
1072
+ def create_comparison_visualizations(self):
1073
+ \"\"\"Create visualizations comparing models across experiments\"\"\"
1074
+ if not self.best_models_by_experiment:
1075
+ raise ValueError("First run analyze_by_experiment()")
1076
+
1077
+ # Prepare data for visualization
1078
+ experiments = []
1079
+ biomass_r2 = []
1080
+ substrate_r2 = []
1081
+ product_r2 = []
1082
+
1083
+ for exp, results in self.best_models_by_experiment.items():
1084
+ experiments.append(exp)
1085
+ biomass_r2.append(results.get('Biomass', {}).get('r2', 0))
1086
+ substrate_r2.append(results.get('Substrate', {}).get('r2', 0))
1087
+ product_r2.append(results.get('Product', {}).get('r2', 0))
1088
+
1089
+ # Create figure with subplots
1090
+ fig, axes = plt.subplots(2, 2, figsize=(15, 12))
1091
+ fig.suptitle('Model Performance Comparison Across Experiments', fontsize=16)
1092
+
1093
+ # 1. R² comparison by experiment and variable type
1094
+ ax1 = axes[0, 0]
1095
+ x = np.arange(len(experiments))
1096
+ width = 0.25
1097
+
1098
+ ax1.bar(x - width, biomass_r2, width, label='Biomass', color='green', alpha=0.8)
1099
+ ax1.bar(x, substrate_r2, width, label='Substrate', color='blue', alpha=0.8)
1100
+ ax1.bar(x + width, product_r2, width, label='Product', color='red', alpha=0.8)
1101
+
1102
+ ax1.set_xlabel('Experiment')
1103
+ ax1.set_ylabel('R²')
1104
+ ax1.set_title('Best Model R² by Experiment and Variable Type')
1105
+ ax1.set_xticks(x)
1106
+ ax1.set_xticklabels(experiments, rotation=45, ha='right')
1107
+ ax1.legend()
1108
+ ax1.grid(True, alpha=0.3)
1109
+
1110
+ # Add value labels
1111
+ for i, (b, s, p) in enumerate(zip(biomass_r2, substrate_r2, product_r2)):
1112
+ if b > 0: ax1.text(i - width, b + 0.01, f'{b:.3f}', ha='center', va='bottom', fontsize=8)
1113
+ if s > 0: ax1.text(i, s + 0.01, f'{s:.3f}', ha='center', va='bottom', fontsize=8)
1114
+ if p > 0: ax1.text(i + width, p + 0.01, f'{p:.3f}', ha='center', va='bottom', fontsize=8)
1115
+
1116
+ # 2. Model frequency heatmap
1117
+ ax2 = axes[0, 1]
1118
+ # This would show which models appear most frequently as best
1119
+ # Implementation depends on actual data structure
1120
+ ax2.text(0.5, 0.5, 'Model Frequency Analysis\\n(Most Used Models)',
1121
+ ha='center', va='center', transform=ax2.transAxes)
1122
+ ax2.set_title('Most Frequently Selected Models')
1123
+
1124
+ # 3. Parameter evolution across experiments
1125
+ ax3 = axes[1, 0]
1126
+ ax3.text(0.5, 0.5, 'Parameter Evolution\\nAcross Experiments',
1127
+ ha='center', va='center', transform=ax3.transAxes)
1128
+ ax3.set_title('Parameter Trends')
1129
+
1130
+ # 4. Overall best models summary
1131
+ ax4 = axes[1, 1]
1132
+ ax4.axis('off')
1133
+
1134
+ summary_text = "🏆 OVERALL BEST MODELS\\n\\n"
1135
+ for var_type, model_info in self.overall_best_models.items():
1136
+ if model_info:
1137
+ summary_text += f"{var_type.upper()}:\\n"
1138
+ summary_text += f" Model: {model_info['name']}\\n"
1139
+ summary_text += f" Avg R²: {model_info['avg_r2']:.4f}\\n"
1140
+ summary_text += f" Tested in: {model_info['n_experiments']} experiments\\n\\n"
1141
+
1142
+ ax4.text(0.1, 0.9, summary_text, transform=ax4.transAxes,
1143
+ fontsize=12, verticalalignment='top', fontfamily='monospace')
1144
+ ax4.set_title('Overall Best Models Summary')
1145
+
1146
+ plt.tight_layout()
1147
+ plt.show()
1148
+
1149
+ def generate_summary_table(self) -> pd.DataFrame:
1150
+ \"\"\"Generate a summary table of best models by experiment and type\"\"\"
1151
+ summary_data = []
1152
+
1153
+ for exp, results in self.best_models_by_experiment.items():
1154
+ for var_type, var_results in results.items():
1155
+ summary_data.append({
1156
+ 'Experiment': exp,
1157
+ 'Variable_Type': var_type,
1158
+ 'Best_Model': var_results['best_model'],
1159
+ 'R2': var_results['r2'],
1160
+ 'RMSE': var_results['rmse']
1161
+ })
1162
+
1163
+ summary_df = pd.DataFrame(summary_data)
1164
+
1165
+ print("\\n📋 SUMMARY TABLE: BEST MODELS BY EXPERIMENT AND VARIABLE TYPE")
1166
+ print("="*80)
1167
+ print(summary_df.to_string(index=False))
1168
+
1169
+ return summary_df
1170
 
1171
+ # Example usage
1172
+ if __name__ == "__main__":
1173
+ print("🧬 Experimental Model Comparison System")
1174
+ print("="*60)
1175
+
1176
+ # Example data structure with experiments
1177
+ example_data = {
1178
+ 'Experiment': ['pH_7.0', 'pH_7.0', 'pH_7.0', 'pH_7.5', 'pH_7.5', 'pH_7.5',
1179
+ 'pH_7.0', 'pH_7.0', 'pH_7.5', 'pH_7.5',
1180
+ 'pH_7.0', 'pH_7.0', 'pH_7.5', 'pH_7.5'],
1181
+ 'Model': ['Monod', 'Logistic', 'Gompertz', 'Monod', 'Logistic', 'Gompertz',
1182
+ 'First_Order', 'Monod_Substrate', 'First_Order', 'Monod_Substrate',
1183
+ 'Luedeking_Piret', 'Linear', 'Luedeking_Piret', 'Linear'],
1184
+ 'Type': ['Biomass', 'Biomass', 'Biomass', 'Biomass', 'Biomass', 'Biomass',
1185
+ 'Substrate', 'Substrate', 'Substrate', 'Substrate',
1186
+ 'Product', 'Product', 'Product', 'Product'],
1187
+ 'R2': [0.9845, 0.9912, 0.9956, 0.9789, 0.9834, 0.9901,
1188
+ 0.9723, 0.9856, 0.9698, 0.9812,
1189
+ 0.9634, 0.9512, 0.9687, 0.9423],
1190
+ 'RMSE': [0.0234, 0.0189, 0.0145, 0.0267, 0.0223, 0.0178,
1191
+ 0.0312, 0.0245, 0.0334, 0.0289,
1192
+ 0.0412, 0.0523, 0.0389, 0.0567],
1193
+ 'mu_max': [0.45, 0.48, 0.52, 0.42, 0.44, 0.49,
1194
+ None, None, None, None, None, None, None, None],
1195
+ 'Ks': [None, None, None, None, None, None,
1196
+ 2.1, 1.8, 2.3, 1.9, None, None, None, None]
1197
+ }
1198
+
1199
+ # Create analyzer
1200
+ analyzer = ExperimentalModelAnalyzer()
1201
+
1202
+ # Load data
1203
+ analyzer.load_results(data_dict=example_data)
1204
+
1205
+ # Analyze by experiment
1206
+ results = analyzer.analyze_by_experiment()
1207
+
1208
+ # Create visualizations
1209
+ analyzer.create_comparison_visualizations()
1210
+
1211
+ # Generate summary table
1212
+ summary = analyzer.generate_summary_table()
1213
+
1214
+ print("\\n✨ Analysis complete! Best models identified for each experiment and variable type.")
1215
+ """
1216
+
1217
+ return code
1218
 
1219
  # Estado global para almacenar resultados
1220
  class AppState:
 
1236
  'pt': "Nenhuma análise disponível para exportar"
1237
  }
1238
  return error_msg.get(language, error_msg['en']), ""
1239
+
1240
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
1241
+
1242
  try:
1243
  if export_format == "DOCX":
1244
  filename = f"biotech_analysis_report_{timestamp}.docx"
 
1257
  # Estado inicial
1258
  current_theme = "light"
1259
  current_language = "en"
1260
+
1261
  def update_interface_language(language):
1262
  """Actualiza el idioma de la interfaz"""
1263
  app_state.current_language = language
1264
  t = TRANSLATIONS[language]
1265
 
1266
  return [
1267
+ gr.update(value=f"# {t['title']}"), # title_text
1268
+ gr.update(value=t['subtitle']), # subtitle_text
1269
+ gr.update(label=t['upload_files']), # files_input
1270
+ gr.update(label=t['select_model']), # model_selector
1271
+ gr.update(label=t['select_language']), # language_selector
1272
+ gr.update(label=t['select_theme']), # theme_selector
1273
+ gr.update(label=t['detail_level']), # detail_level
1274
+ gr.update(label=t['additional_specs'], placeholder=t['additional_specs_placeholder']), # additional_specs
1275
+ gr.update(value=t['analyze_button']), # analyze_btn
1276
+ gr.update(label=t['export_format']), # export_format
1277
+ gr.update(value=t['export_button']), # export_btn
1278
+ gr.update(label=t['comparative_analysis']), # analysis_output
1279
+ gr.update(label=t['implementation_code']), # code_output
1280
+ gr.update(label=t['data_format']) # data_format_accordion
1281
  ]
1282
+
1283
  def process_and_store(files, model, detail, language, additional_specs):
1284
  """Procesa archivos y almacena resultados"""
1285
  if not files:
 
1290
  app_state.current_analysis = analysis
1291
  app_state.current_code = code
1292
  return analysis, code
1293
+
1294
  with gr.Blocks(theme=THEMES[current_theme]) as demo:
1295
+ # Componentes de UI
1296
  with gr.Row():
1297
  with gr.Column(scale=3):
1298
  title_text = gr.Markdown(f"# {TRANSLATIONS[current_language]['title']}")
 
1322
  type="filepath"
1323
  )
1324
 
1325
+ # CAMBIO: Usar el diccionario de modelos de Nebius
1326
+ default_model = "Qwen/Qwen3-14B"
1327
  model_selector = gr.Dropdown(
1328
  choices=list(NEBIUS_MODELS.keys()),
1329
+ value=default_model,
1330
  label=TRANSLATIONS[current_language]['select_model'],
1331
+ info=f"{TRANSLATIONS[current_language]['best_for']}: {NEBIUS_MODELS[default_model]['best_for']}"
1332
  )
1333
 
1334
  detail_level = gr.Radio(
 
1340
  label=TRANSLATIONS[current_language]['detail_level']
1341
  )
1342
 
1343
+ # Nueva entrada para especificaciones adicionales
1344
  additional_specs = gr.Textbox(
1345
  label=TRANSLATIONS[current_language]['additional_specs'],
1346
  placeholder=TRANSLATIONS[current_language]['additional_specs_placeholder'],
 
1404
  |------------|-------|------|-----|------|-----|-----|--------|-------|------------|
1405
  | pH_7.0 | Monod | Biomass | 0.985 | 0.023 | -45.2 | -42.1 | 0.45 | 2.1 | {...} |
1406
  | pH_7.0 | Logistic | Biomass | 0.976 | 0.031 | -42.1 | -39.5 | 0.42 | - | {...} |
1407
+ | pH_7.0 | First_Order | Substrate | 0.992 | 0.018 | -48.5 | -45.2 | - | 1.8 | {...} |
1408
+ | pH_7.5 | Monod | Biomass | 0.978 | 0.027 | -44.1 | -41.2 | 0.43 | 2.2 | {...} |
1409
 
1410
  **Important columns:**
1411
  - **Experiment**: Experimental condition identifier
 
1415
  - **Parameters**: Model-specific parameters
1416
  """)
1417
 
1418
+ # CAMBIO: Actualizar el modelo en los ejemplos
1419
  examples = gr.Examples(
1420
  examples=[
1421
  [["examples/biomass_models_comparison.csv"], "Qwen/Qwen3-14B", "detailed", ""],
 
1425
  label=TRANSLATIONS[current_language]['examples']
1426
  )
1427
 
1428
+ # Eventos
1429
  language_selector.change(
1430
  update_interface_language,
1431
  inputs=[language_selector],
 
1438
  )
1439
 
1440
  def change_theme(theme_name):
1441
+ """Cambia el tema de la interfaz"""
1442
  return gr.Info("Theme will be applied on next page load")
1443
 
1444
  theme_selector.change(
 
1465
  inputs=[export_format, language_selector],
1466
  outputs=[export_status, export_file]
1467
  )
1468
+
1469
  return demo
1470
 
1471
  # Función principal
1472
  def main():
1473
+ # CAMBIO: Comprobar la nueva variable de entorno
1474
  if not os.getenv("NEBIUS_API_KEY"):
1475
  print("⚠️ Configure NEBIUS_API_KEY in HuggingFace Space secrets")
 
 
1476
  return gr.Interface(
1477
+ fn=lambda x: TRANSLATIONS['en']['error_no_api'],
1478
  inputs=gr.Textbox(),
1479
  outputs=gr.Textbox(),
1480
  title="Configuration Error"
1481
  )
1482
+
1483
  return create_interface()
1484
 
1485
  # Para ejecución local
1486
  if __name__ == "__main__":
 
 
 
1487
  demo = main()
1488
  if demo:
1489
+ # Para crear los archivos de ejemplo si no existen
1490
+ if not os.path.exists("examples"):
1491
+ os.makedirs("examples")
1492
+ if not os.path.exists("examples/biomass_models_comparison.csv"):
1493
+ pd.DataFrame({
1494
+ 'Experiment': ['pH_7.0', 'pH_7.0', 'pH_7.0', 'pH_7.5', 'pH_7.5', 'pH_7.5'],
1495
+ 'Model': ['Monod', 'Logistic', 'Gompertz', 'Monod', 'Logistic', 'Gompertz'],
1496
+ 'Type': ['Biomass', 'Biomass', 'Biomass', 'Biomass', 'Biomass', 'Biomass'],
1497
+ 'R2': [0.98, 0.99, 0.995, 0.97, 0.98, 0.99],
1498
+ 'RMSE': [0.02, 0.01, 0.005, 0.03, 0.02, 0.01]
1499
+ }).to_csv("examples/biomass_models_comparison.csv", index=False)
1500
+ if not os.path.exists("examples/substrate_kinetics_results.xlsx"):
1501
+ pd.DataFrame({
1502
+ 'Experiment': ['Temp_30C', 'Temp_30C', 'Temp_37C', 'Temp_37C'],
1503
+ 'Model': ['First_Order', 'Monod_Substrate', 'First_Order', 'Monod_Substrate'],
1504
+ 'Type': ['Substrate', 'Substrate', 'Substrate', 'Substrate'],
1505
+ 'R2': [0.97, 0.98, 0.96, 0.985],
1506
+ 'RMSE': [0.03, 0.02, 0.04, 0.015]
1507
+ }).to_excel("examples/substrate_kinetics_results.xlsx", index=False)
1508
+
1509
  demo.launch(
1510
  server_name="0.0.0.0",
1511
  server_port=7860,
1512
+ share=False
1513
  )