C2MV's picture
Update app.py
33e8773 verified
import gradio as gr
from gradio_client import Client, handle_file
import pandas as pd
import json
import tempfile
import os
from datetime import datetime
import plotly.graph_objects as go
import plotly.express as px
import numpy as np
from smolagents import CodeAgent, tool, InferenceClientModel
import logging
# Configuración de logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Configuración de clientes
biotech_client = Client("C2MV/BiotechU4")
analysis_client = Client("C2MV/Project-HF-2025")
# Configuración del motor de Hugging Face (opcional)
try:
hf_engine = InferenceClientModel(model_id="mistralai/Mistral-7B-Instruct-v0.2")
except Exception:
logger.warning("No se pudo inicializar el modelo de HF. Agentes usarán lógica simple.")
hf_engine = None
# ============================================================================
# 🤖 SISTEMA DE AGENTES (CON DOCSTRINGS RESTAURADOS)
# ============================================================================
# ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
# CORRECCIÓN: Se han restaurado los docstrings en las funciones decoradas con @tool.
# Esto es obligatorio para que la librería smolagents pueda generar el esquema de la herramienta.
# ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
class BiotechAgentTools:
"""Herramientas personalizadas para los agentes biotecnológicos"""
@tool
def analyze_data_characteristics(data_info: str) -> dict:
"""
Analiza las características de los datos biotecnológicos subidos.
Args:
data_info: Información sobre el archivo de datos (formato, tamaño, columnas).
Returns:
dict: Características identificadas y recomendaciones.
"""
try:
characteristics = {"experiment_type": "unknown", "recommended_models": [], "suggested_params": {}, "data_quality": "good"}
data_lower = data_info.lower()
models_from_docs = ['logistic', 'gompertz', 'moser', 'baranyi', 'monod', 'contois', 'andrews', 'tessier', 'richards', 'stannard', 'huang']
growth_models = [m for m in ['logistic', 'gompertz', 'baranyi', 'richards'] if m in models_from_docs]
fermentation_models = [m for m in ['monod', 'contois', 'andrews', 'moser'] if m in models_from_docs]
if "biomass" in data_lower or "growth" in data_lower:
characteristics.update({"experiment_type": "growth_kinetics", "recommended_models": growth_models, "suggested_params": {"component": "biomass", "use_de": True, "maxfev": 75000}})
elif "ferment" in data_lower or "substrate" in data_lower:
characteristics.update({"experiment_type": "fermentation", "recommended_models": fermentation_models,"suggested_params": {"component": "all", "use_de": False, "maxfev": 50000}})
else:
characteristics.update({"experiment_type": "general_biotech", "recommended_models": growth_models, "suggested_params": {"component": "all", "use_de": False, "maxfev": 50000}})
logger.info(f"Análisis completado: {characteristics['experiment_type']}")
return characteristics
except Exception as e:
logger.error(f"Error en análisis de datos: {str(e)}")
return {"experiment_type": "error", "recommended_models": ['logistic', 'gompertz'], "suggested_params": {"component": "all", "use_de": False, "maxfev": 50000}, "data_quality": "unknown"}
@tool
def evaluate_analysis_quality(results_info: str) -> dict:
"""
Evalúa la calidad de los resultados del análisis biotecnológico.
Args:
results_info: Un string con información sobre los resultados obtenidos para evaluar.
Returns:
dict: Una evaluación de la calidad y recomendaciones.
"""
try:
evaluation = {"quality_score": 0.8, "is_satisfactory": True, "recommendations": [], "needs_retry": False}
results_lower = results_info.lower()
if "error" in results_lower or "failed" in results_lower: evaluation.update({"quality_score": 0.2, "is_satisfactory": False, "needs_retry": True, "recommendations": ["Retry with different parameters"]})
elif "r2" in results_lower or "rmse" in results_lower: evaluation.update({"quality_score": 0.9, "is_satisfactory": True, "recommendations": ["Results look good"]})
logger.info(f"Evaluación de calidad: {evaluation['quality_score']}")
return evaluation
except Exception as e:
logger.error(f"Error en evaluación: {str(e)}")
return {"quality_score": 0.5, "is_satisfactory": True, "recommendations": ["Continue with analysis"], "needs_retry": False}
@tool
def prepare_claude_context(data_summary: str) -> str:
"""
Prepara el contexto específico para el análisis de Claude.
Args:
data_summary: Un resumen de los datos y los resultados del análisis.
Returns:
str: El contexto mejorado y formateado para enviar a Claude.
"""
try:
enhanced_context = f"""CONTEXTO BIOTECNOLÓGICO ESPECÍFICO:
Datos analizados: {data_summary}
Por favor, enfócate en:
1. Interpretación biológica de los parámetros ajustados
2. Comparación de la bondad de ajuste entre modelos
3. Implicaciones prácticas para el proceso biotecnológico
4. Recomendaciones para optimización del proceso
5. Identificación de posibles limitaciones del modelo
Incluye análisis estadístico riguroso y recomendaciones prácticas."""
logger.info("Contexto preparado para Claude")
return enhanced_context
except Exception as e:
logger.error(f"Error preparando contexto: {str(e)}")
return data_summary
class CoordinatorAgent:
def __init__(self):
self.agent = CodeAgent(tools=[BiotechAgentTools.analyze_data_characteristics, BiotechAgentTools.evaluate_analysis_quality, BiotechAgentTools.prepare_claude_context], model=hf_engine) if hf_engine else None
self.tools = BiotechAgentTools()
def analyze_and_optimize(self, file_info: str, current_config: dict) -> dict:
try:
logger.info("🤖 Agente Coordinador iniciando análisis...")
characteristics = self.tools.analyze_data_characteristics(file_info)
optimized_config = current_config.copy()
if characteristics["experiment_type"] != "error":
optimized_config.update({"models": characteristics["recommended_models"], "component": characteristics["suggested_params"]["component"], "use_de": characteristics["suggested_params"]["use_de"], "maxfev": characteristics["suggested_params"]["maxfev"]})
if characteristics["experiment_type"] == "growth_kinetics": optimized_config["additional_specs"] = "Enfócate en el análisis de cinética de crecimiento: interpretación de μmax, lag time, etc."
elif characteristics["experiment_type"] == "fermentation": optimized_config["additional_specs"] = "Enfócate en el análisis de fermentación: eficiencia de conversión, productividad, etc."
logger.info(f"✅ Configuración optimizada para: {characteristics['experiment_type']}")
return {"config": optimized_config, "analysis": characteristics, "recommendations": f"Configuración optimizada para {characteristics['experiment_type']}"}
except Exception as e:
logger.error(f"❌ Error en Agente Coordinador: {str(e)}")
return {"config": current_config, "analysis": {"experiment_type": "error"}, "recommendations": f"Error en optimización: {str(e)}"}
class RecoveryAgent:
def __init__(self):
self.agent = CodeAgent(tools=[BiotechAgentTools.analyze_data_characteristics], model=hf_engine) if hf_engine else None
self.retry_strategies = [{"use_de": False, "maxfev": 25000, "models_subset": 2}, {"use_de": True, "maxfev": 100000, "models_subset": 1}, {"component": "biomass", "use_de": False, "maxfev": 50000}]
def attempt_recovery(self, original_config: dict, error_info: str, attempt: int = 0) -> dict:
if attempt >= len(self.retry_strategies): return {"success": False, "message": "Todas las estrategias fallaron"}
strategy = self.retry_strategies[attempt]
recovery_config = original_config.copy()
if "models_subset" in strategy: recovery_config["models"] = recovery_config.get("models", ["logistic"])[:strategy["models_subset"]]
for key, value in strategy.items():
if key != "models_subset": recovery_config[key] = value
logger.info(f"🔧 Aplicando estrategia de recuperación {attempt + 1}")
return {"success": True, "config": recovery_config, "strategy": strategy}
class QualityAgent:
def __init__(self):
self.agent = CodeAgent(tools=[BiotechAgentTools.evaluate_analysis_quality], model=hf_engine) if hf_engine else None
self.tools = BiotechAgentTools()
def evaluate_results(self, results_summary: str) -> dict:
try:
evaluation = self.tools.evaluate_analysis_quality(results_summary)
quality_feedback = {"quality_score": evaluation["quality_score"], "is_acceptable": evaluation["is_satisfactory"], "feedback": evaluation["recommendations"], "needs_improvement": evaluation["needs_retry"]}
logger.info(f"✅ Evaluación de calidad: {quality_feedback['quality_score']:.2f}")
return quality_feedback
except Exception as e:
logger.error(f"❌ Error en evaluación de calidad: {str(e)}")
return {"quality_score": 0.7, "is_acceptable": True, "feedback": ["Evaluación completada con advertencias"], "needs_improvement": False}
class ContextAgent:
def __init__(self):
self.agent = CodeAgent(tools=[BiotechAgentTools.prepare_claude_context], model=hf_engine) if hf_engine else None
self.tools = BiotechAgentTools()
def enhance_analysis_context(self, data_summary: str, experiment_type: str) -> str:
try:
enhanced_context = self.tools.prepare_claude_context(f"Tipo de experimento: {experiment_type}. Datos: {data_summary}")
logger.info("📊 Contexto mejorado para Claude")
return enhanced_context
except Exception as e:
logger.error(f"❌ Error mejorando contexto: {str(e)}")
return data_summary
class BiotechAgentSystem:
def __init__(self):
self.coordinator = CoordinatorAgent()
self.recovery = RecoveryAgent()
self.quality = QualityAgent()
self.context = ContextAgent()
logger.info("🚀 Sistema de agentes inicializado")
def process_with_agents(self, file_info: str, user_config: dict) -> dict:
try:
coordination_result = self.coordinator.analyze_and_optimize(file_info, user_config)
optimized_config = coordination_result["config"]
experiment_type = coordination_result["analysis"]["experiment_type"]
quality_result = self.quality.evaluate_results("Initial configuration optimized")
enhanced_specs = self.context.enhance_analysis_context(file_info, experiment_type)
optimized_config["additional_specs"] = enhanced_specs
return {"success": True, "optimized_config": optimized_config, "experiment_type": experiment_type, "recommendations": coordination_result["recommendations"], "quality_score": quality_result["quality_score"]}
except Exception as e:
logger.error(f"❌ Error en sistema de agentes: {str(e)}")
return {"success": False, "optimized_config": user_config, "experiment_type": "error", "recommendations": f"Error: {str(e)}", "quality_score": 0.5}
# El resto del código se mantiene igual...
agent_system = BiotechAgentSystem()
def process_biotech_data(file, models, component, use_de, maxfev, exp_names):
try:
file_path = file.name if hasattr(file, 'name') else file
return biotech_client.predict(file=handle_file(file_path), models=models, component=component, use_de=use_de, maxfev=maxfev, exp_names=exp_names, theme=False, api_name="/run_analysis_wrapper")
except Exception as e:
logger.error(f"Error en proceso biotecnológico: {str(e)}")
return None, None, f"Error en el análisis: {str(e)}"
def create_dummy_plot():
fig = go.Figure(go.Scatter(x=np.linspace(0, 10, 100), y=np.exp(-np.linspace(0, 10, 100)/5) * np.cos(2*np.pi*np.linspace(0, 10, 100)), mode='lines', name='Datos de ejemplo'))
fig.update_layout(title="Visualización de Resultados", template="plotly_white", height=500)
return fig
def parse_plot_data(plot_dict):
if not plot_dict: return create_dummy_plot()
try:
if isinstance(plot_dict, str): return go.Figure(json.loads(plot_dict))
if isinstance(plot_dict, dict): return go.Figure(plot_dict)
except Exception as e:
logger.error(f"Error parsing plot: {str(e)}")
return create_dummy_plot()
def download_results_as_csv(df_data):
try:
return biotech_client.predict(df=df_data, api_name="/download_results_excel")
except Exception as e:
logger.warning(f"Error con API de descarga: {str(e)}")
if df_data and 'data' in df_data and 'headers' in df_data:
try:
df = pd.DataFrame(df_data['data'], columns=df_data['headers'])
with tempfile.NamedTemporaryFile(mode='w+', suffix='.csv', delete=False) as temp_file:
df.to_csv(temp_file.name, index=False)
return temp_file.name
except Exception as e_local:
logger.error(f"Error creando CSV local: {e_local}")
return None
def generate_claude_report(csv_file_path, model, detail_level, language, additional_specs):
try:
return analysis_client.predict(files=[handle_file(csv_file_path)], model=model, detail=detail_level, language=language, additional_specs=additional_specs, api_name="/process_and_store")
except Exception as e:
logger.error(f"Error generando reporte Claude: {str(e)}")
return f"Error en el análisis: {str(e)}", ""
def export_report(format_type, language):
try:
status, file_path = analysis_client.predict(format=format_type, language=language, api_name="/handle_export")
return file_path, status
except Exception as e:
logger.error(f"Error en exportación: {str(e)}")
return None, f"Error al exportar: {str(e)}"
def process_complete_pipeline_with_agents(file, models, component, use_de, maxfev, exp_names, claude_model, detail_level, language, additional_specs, export_format, progress=gr.Progress()):
progress(0, desc="🚀 Iniciando Pipeline...")
if not file: return create_dummy_plot(), None, None, None, None, "❌ Por favor, sube un archivo."
if not models: return create_dummy_plot(), None, None, None, None, "❌ Por favor, selecciona al menos un modelo."
progress_updates = []
progress(0.1, desc="🤖 Activando sistema de agentes...")
file_info = f"Archivo: {os.path.basename(file.name)}, Modelos: {models}"
user_config = {"models": models, "component": component, "use_de": use_de, "maxfev": maxfev, "claude_model": claude_model, "detail_level": detail_level, "language": language, "additional_specs": additional_specs, "export_format": export_format}
agent_result = agent_system.process_with_agents(file_info, user_config)
if agent_result["success"]:
optimized_config = agent_result["optimized_config"]
progress_updates.extend([f"✅ Agentes detectaron: {agent_result['experiment_type']}", f"🎯 {agent_result['recommendations']}", f"📊 Calidad esperada: {agent_result['quality_score']:.1%}"])
models, component, use_de, maxfev, additional_specs = (optimized_config.get("models", models), optimized_config.get("component", component), optimized_config.get("use_de", use_de), optimized_config.get("maxfev", maxfev), optimized_config.get("additional_specs", additional_specs))
else:
progress_updates.append("⚠️ Agentes no pudieron optimizar, usando config original.")
progress(0.2, desc="🔄 Procesando datos biotecnológicos...")
plot_data, df_data, status = process_biotech_data(file, models, component, use_de, maxfev, exp_names)
if not plot_data or not df_data or "Error" in str(status): return create_dummy_plot(), None, None, None, None, "\n".join(progress_updates) + f"\n❌ Error en análisis: {status}"
progress_updates.append("✅ Análisis biotecnológico completado")
progress(0.4, desc="📥 Descargando resultados...")
csv_file_path = download_results_as_csv(df_data)
if not csv_file_path: return parse_plot_data(plot_data), df_data, None, None, None, "\n".join(progress_updates) + "\n❌ Error al descargar resultados para análisis."
progress_updates.append("✅ Resultados descargados")
progress(0.5, desc=f"🤖 Generando análisis con {claude_model}...")
analysis, code = generate_claude_report(csv_file_path, claude_model, detail_level, language, additional_specs)
if os.path.exists(csv_file_path): os.remove(csv_file_path)
if "Error" in analysis: return parse_plot_data(plot_data), df_data, analysis, code, None, "\n".join(progress_updates) + f"\n❌ {analysis}"
progress_updates.append("✅ Análisis con Claude completado")
progress(0.9, desc=f"📄 Exportando informe en {export_format}...")
report_file, export_status = export_report(export_format, language)
if report_file:
progress_updates.append("✅ Informe exportado.")
else:
progress_updates.append(f"❌ Error al exportar: {export_status}")
progress(1, desc="🎉 Pipeline Completado")
return parse_plot_data(plot_data), df_data, analysis, code, report_file, "\n".join(progress_updates)
BIOTECH_MODELS = ['logistic', 'gompertz', 'moser', 'baranyi', 'monod', 'contois', 'andrews', 'tessier', 'richards', 'stannard', 'huang']
DEFAULT_BIOTECH_SELECTION = [model for model in ['logistic', 'gompertz', 'moser', 'baranyi'] if model in BIOTECH_MODELS]
CLAUDE_MODELS = ['claude-opus-4-20250514', 'claude-sonnet-4-20250514', 'claude-3-5-haiku-20241022', 'claude-3-7-sonnet-20250219', 'claude-3-5-sonnet-20241022']
DEFAULT_CLAUDE_MODEL = 'claude-3-5-sonnet-20241022'
theme = gr.themes.Soft(primary_hue="blue", secondary_hue="indigo", neutral_hue="slate")
custom_css = ".file-upload { border: 2px dashed #3b82f6; } button.primary { background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%); }"
with gr.Blocks(theme=theme, title="BioTech Analysis & Report Generator", css=custom_css) as demo:
gr.Markdown("# 🧬 BioTech Analysis & Report Generator\n## 🤖 **Potenciado por Agentes IA**")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("## 📊 Configuración")
file_input = gr.File(label="📁 Archivo de datos (CSV/Excel)", file_types=[".csv", ".xlsx", ".xls"], elem_classes=["file-upload"])
with gr.Accordion("🔬 Parámetros de Análisis (Optimizados por IA)", open=False):
models_input = gr.CheckboxGroup(choices=BIOTECH_MODELS, value=DEFAULT_BIOTECH_SELECTION, label="📊 Modelos")
component_input = gr.Dropdown(['all', 'biomass', 'substrate', 'product'], value='all', label="📈 Componente")
exp_names_input = gr.Textbox(label="🏷️ Nombres de experimentos", value="Análisis Biotecnológico")
use_de_input = gr.Checkbox(label="🧮 Usar Evolución Diferencial", value=False)
maxfev_input = gr.Number(label="🔄 Iteraciones máximas", value=50000, step=1000)
with gr.Group():
claude_model_input = gr.Dropdown(choices=CLAUDE_MODELS, value=DEFAULT_CLAUDE_MODEL, label="🤖 Modelo Claude")
detail_level_input = gr.Radio(['detailed', 'summarized'], value='detailed', label="📋 Detalle")
language_input = gr.Dropdown(['en', 'es', 'fr', 'de', 'pt'], value='es', label="🌐 Idioma")
export_format_input = gr.Radio(['PDF', 'DOCX'], value='PDF', label="📄 Formato")
additional_specs_input = gr.Textbox(label="📝 Especificaciones Adicionales", placeholder="Los agentes IA personalizarán esto...", lines=3, value="Análisis detallado de modelos, métricas y recomendaciones.")
process_btn = gr.Button("🚀 Ejecutar Pipeline con Agentes IA", variant="primary", size="lg")
with gr.Column(scale=2):
gr.Markdown("## 📈 Resultados")
status_output = gr.Textbox(label="📊 Estado del Proceso (con Agentes IA)", lines=5, interactive=False)
with gr.Tabs():
with gr.TabItem("📊 Visualización"):
plot_output = gr.Plot()
with gr.TabItem("📋 Tabla"):
table_output = gr.Dataframe()
with gr.TabItem("📝 Análisis"):
analysis_output = gr.Markdown()
with gr.TabItem("💻 Código"):
code_output = gr.Code(language="python")
report_output = gr.File(label="📥 Descargar Informe", interactive=False)
process_btn.click(fn=process_complete_pipeline_with_agents, inputs=[file_input, models_input, component_input, use_de_input, maxfev_input, exp_names_input, claude_model_input, detail_level_input, language_input, additional_specs_input, export_format_input], outputs=[plot_output, table_output, analysis_output, code_output, report_output, status_output])
if __name__ == "__main__":
demo.launch(show_error=True)