Spaces:
Sleeping
Sleeping
File size: 24,850 Bytes
43203c1 b9fbc5e 43203c1 d1eb779 d1ce30b f45845d 631a418 f45845d d1ce30b b9fbc5e 9b58814 d1ce30b d7a5a15 b9fbc5e d1ce30b 14d782a b9fbc5e 636020c 7993e54 8a21a90 5c5c390 11c99e4 5c5c390 636020c 1e200b9 7993e54 b9fbc5e 7993e54 6673668 1e200b9 636020c 7993e54 da38434 1e200b9 72fd6ef 7993e54 72fd6ef 14d782a 636020c b9fbc5e 1e200b9 be9c158 7993e54 11c99e4 0ee0f76 d873e9a 0ee0f76 6673668 b9fbc5e d873e9a 11c99e4 b9fbc5e d873e9a f10d43c d873e9a b9fbc5e d873e9a 0ee0f76 72fd6ef d873e9a 11c99e4 72fd6ef f10d43c 0ee0f76 f10d43c d873e9a f10d43c b9fbc5e 72fd6ef f10d43c 938da61 be9c158 f10d43c b9fbc5e f10d43c b9fbc5e be9c158 636020c b9fbc5e d1ce30b b9fbc5e d1ce30b 8a21a90 1e200b9 d1ce30b 1e200b9 7993e54 cb5c52b b9fbc5e 7993e54 11c99e4 72fd6ef 11c99e4 72fd6ef 11c99e4 72fd6ef 1e200b9 b9fbc5e 938da61 7993e54 1e200b9 72fd6ef 7993e54 b9fbc5e 7993e54 2328ff4 b9fbc5e 72fd6ef 7993e54 b9fbc5e 72fd6ef be9c158 7993e54 b9fbc5e 7993e54 b9fbc5e 7993e54 72fd6ef 7993e54 b9fbc5e d1ce30b 72fd6ef b9fbc5e 72fd6ef b9fbc5e 72fd6ef d1ce30b 7993e54 d1ce30b f4c235c dc41149 740148a b9fbc5e 220eb0b 1e200b9 2328ff4 d1ce30b cfa82b8 7993e54 72fd6ef f4c235c a668668 1e200b9 740148a 7993e54 72fd6ef cb5c52b b9fbc5e d7a5a15 6673668 d7a5a15 72fd6ef 1e200b9 a668668 7993e54 a668668 7993e54 a668668 636020c f4c235c 8a21a90 636020c 43203c1 d7a5a15 7993e54 b9fbc5e 7993e54 d873e9a 494ef15 7993e54 b9fbc5e 6673668 b9fbc5e 636020c 7993e54 2328ff4 7993e54 b9fbc5e be9c158 2328ff4 b9fbc5e 2328ff4 494ef15 7993e54 b9fbc5e 494ef15 636020c 6673668 b9fbc5e 636020c b9fbc5e 740148a 1e200b9 7993e54 72fd6ef 7993e54 b9fbc5e 7993e54 a668668 7993e54 740148a 43ab0e1 740148a f83e05d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
"""
CÓDIGO COMPLETO Y CORREGIDO - VERSIÓN 7.8 (Agente de Instrucciones de Lenguaje Natural)
- MEJORA MAYOR: Se ha creado un nuevo `InstructionParsingAgent` que interpreta las instrucciones
en lenguaje natural del usuario desde el cuadro de "Especificaciones Adicionales".
- FUNCIONALIDAD AVANZADA: El usuario puede especificar qué métricas usar (R2, RMSE, o ambas) y
cuántos "Top N" modelos seleccionar, simplemente escribiéndolo.
- MODELSELECTIONAGENT MEJORADO: La lógica del "Plan B" ahora es dinámica y se basa en las
instrucciones parseadas, calculando un score combinado si se especifican múltiples métricas.
- UI SIMPLIFICADA: Se han eliminado los controles estáticos de ranking, reemplazados por el
cuadro de texto de instrucciones, haciendo la interfaz más limpia y potente.
"""
import gradio as gr
from gradio_client import Client, handle_file
import pandas as pd
import json
import tempfile
import os
import re
from datetime import datetime
import plotly.graph_objects as go
import logging
import numpy as np
from smolagents import CodeAgent, InferenceClientModel
# --- CONFIGURACIÓN Y CLIENTES ---
logging.basicConfig(level=logging.INFO); logger = logging.getLogger(__name__)
# --- INICIALIZACIÓN DE MODELO PARA AGENTES ---
try:
hf_engine = InferenceClientModel(model_id="deepseek-ai/DeepSeek-V2-Lite-Instruct")
logger.info("✅ Modelo de lenguaje (DeepSeek-V2-Lite) inicializado para agentes.")
except Exception: hf_engine = None; logger.error("❌ No se pudo inicializar el modelo de lenguaje para agentes.")
try: biotech_client = Client("C2MV/BiotechU4"); logger.info("✅ Cliente BiotechU4 inicializado.")
except: biotech_client = None
try: analysis_client = Client("C2MV/Project-HF-2025-2"); logger.info("✅ Cliente Project-HF-2025-2 inicializado.")
except: analysis_client = None
# ============================================================================
# 🤖 SISTEMA DE AGENTES (LÓGICA ACTUALIZADA)
# ============================================================================
class LoggingAgent:
def __init__(self): self.log_entries, self.start_time = [], datetime.now(); logger.info("🕵️ LoggingAgent activado.")
def register(self, agent_name, action, details=""):
entry = f"**{datetime.now().strftime('%H:%M:%S')} | {agent_name}:** {action}"; self.log_entries.append(entry + (f"\n> *Detalles: {details}*" if details else ""))
def get_report(self):
if not self.log_entries: return "### 🕵️ Informe de Actividad\n\nNo se registraron actividades."
return "### 🕵️ Informe de Actividad de Agentes\n\n---\n\n" + "\n\n---\n\n".join(self.log_entries) + f"\n\n---\n\n**Tiempo total: {(datetime.now() - self.start_time).total_seconds():.2f} s.**"
def clear(self): self.log_entries, self.start_time = [], datetime.now()
class StructureValidationAgent:
def __init__(self, log_agent: LoggingAgent): self.log_agent = log_agent
def validate(self, file_obj):
try:
if file_obj.name.endswith(('.xls', '.xlsx')): df = pd.read_excel(file_obj.name, header=1)
else: df = pd.read_csv(file_obj.name)
if df.empty: return False, "Validación fallida: El archivo está vacío."
except Exception as e: return False, f"Error crítico al leer el archivo: {e}"
self.log_agent.register("StructureValidationAgent", "Validación de formato de archivo superada.")
return True, "Formato de archivo básico validado."
class InstructionParsingAgent:
"""Agente que convierte el lenguaje natural del usuario en un plan de acción estructurado."""
def __init__(self, log_agent: LoggingAgent, llm_engine):
self.log_agent = log_agent
self.agent = CodeAgent(tools=[], model=llm_engine) if llm_engine else None
def parse(self, text: str):
default_instructions = {'metrics': ['R2'], 'top_n': 3}
if not self.agent:
self.log_agent.register("InstructionParsingAgent", "LLM no disponible, usando defaults.")
return default_instructions
prompt = f"""
Analyze the user's instruction for a data analysis task. Extract the metrics they want to use for ranking and the number of top models to select.
The possible metrics are "R2", "RMSE".
Your output MUST be ONLY a valid JSON object with two keys: "metrics" (a list of strings) and "top_n" (an integer).
- If the user mentions "R2" or "R-cuadrado", include "R2" in the metrics list.
- If the user mentions "RMSE", include "RMSE" in the metrics list.
- If the user mentions a number like "top 3", "los 2 mejores", or just a digit, set "top_n" to that number.
- If no metrics are mentioned, default to ["R2"].
- If no number is mentioned, default to 3.
Example 1: "Usa el promedio de R2 y RMSE y elige el top 2" -> {{"metrics": ["R2", "RMSE"], "top_n": 2}}
Example 2: "dame los 3 mejores modelos segun el menor RMSE" -> {{"metrics": ["RMSE"], "top_n": 3}}
Example 3: "el mejor R2" -> {{"metrics": ["R2"], "top_n": 1}}
Example 4: "analizar los datos" -> {{"metrics": ["R2"], "top_n": 3}}
User instruction: "{text}"
JSON Output:
"""
try:
response_str = self.agent.run(prompt)
json_str = response_str[response_str.find('{'):response_str.rfind('}')+1]
instructions = json.loads(json_str)
# Validar que el formato es correcto
if 'metrics' not in instructions or 'top_n' not in instructions:
raise ValueError("JSON de salida no contiene las claves esperadas.")
self.log_agent.register("InstructionParsingAgent", "Instrucciones del usuario parseadas con éxito.")
return instructions
except Exception as e:
self.log_agent.register("InstructionParsingAgent", "Error parseando instrucciones, usando defaults.", f"Error: {e}")
return default_instructions
class ModelSelectionAgent:
"""Agente 3 (CON FALLBACK CONFIGURABLE): Identifica los mejores modelos, con un plan B personalizable."""
def __init__(self, log_agent: LoggingAgent): self.log_agent = log_agent
def _find_column(self, df_columns, possible_names):
for name in possible_names:
for col in df_columns:
if str(col).lower() == name.lower(): return col
return None
def identify_best_models(self, results_df, component, r2_threshold, rmse_threshold, instructions: dict):
self.log_agent.register("ModelSelectionAgent", f"Iniciando identificación para: '{component}'.")
# 1. Normalizar columna del Modelo
model_col = self._find_column(results_df.columns, ['model', 'modelo'])
if not model_col: return [], "Error: No se encontró la columna de nombres de modelos ('Model')."
df_norm = results_df.rename(columns={model_col: 'Model'})
# 2. Identificar columnas de métricas
r2_target_col, rmse_target_col = None, None
if component != 'all':
r2_target_col = self._find_column(df_norm.columns, [f'r2_{component}'])
rmse_target_col = self._find_column(df_norm.columns, [f'rmse_{component}'])
else:
metric_cols_r2 = [c for c in df_norm.columns if 'r2_' in str(c).lower()]
metric_cols_rmse = [c for c in df_norm.columns if 'rmse_' in str(c).lower()]
if metric_cols_r2 and metric_cols_rmse:
r2_target_col, rmse_target_col = 'R2_Avg', 'RMSE_Avg'
df_norm[r2_target_col] = df_norm[metric_cols_r2].mean(axis=1, skipna=True)
df_norm[rmse_target_col] = df_norm[metric_cols_rmse].mean(axis=1, skipna=True)
if not r2_target_col or not rmse_target_col or r2_target_col not in df_norm.columns or rmse_target_col not in df_norm.columns:
return [], f"Error: No se encontraron las métricas para el componente '{component}'."
# 3. Agrupar por modelo y calcular métrica de rendimiento promedio
model_performance = df_norm.groupby('Model').agg({r2_target_col: 'mean', rmse_target_col: 'mean'}).reset_index()
# 4. Intento 1: Filtrado Estricto
good_models_df = model_performance[(model_performance[r2_target_col] >= r2_threshold) & (model_performance[rmse_target_col] <= rmse_threshold)]
if not good_models_df.empty:
best_models_list = sorted([str(model).lower() for model in good_models_df['Model'].tolist()])
reasoning = f"Agente identificó **{len(best_models_list)}** modelo(s) que cumplen tus criterios: `{', '.join(best_models_list)}`."
return best_models_list, reasoning
else:
# 5. Intento 2: Plan B - Ranking Estratégico basado en instrucciones
self.log_agent.register("ModelSelectionAgent", "Filtro primario falló. Activando fallback: 'Ranking por Instrucciones'.", f"Plan: {instructions}")
use_r2 = 'R2' in instructions['metrics']
use_rmse = 'RMSE' in instructions['metrics']
top_n = instructions['top_n']
# Calcular el score de rendimiento
if use_r2 and use_rmse:
model_performance['Score'] = model_performance[r2_target_col] / (model_performance[rmse_target_col] + 1e-9)
sort_col, ascending, metric_name = 'Score', False, "R²/RMSE combinado"
elif use_rmse:
sort_col, ascending, metric_name = rmse_target_col, True, "RMSE"
else: # Por defecto R2
sort_col, ascending, metric_name = r2_target_col, False, "R²"
sorted_performance = model_performance.sort_values(by=sort_col, ascending=ascending)
top_n_df = sorted_performance.head(top_n)
best_models_list = sorted([str(model).lower() for model in top_n_df['Model'].tolist()])
reasoning = (f"**Advertencia:** Ningún modelo cumplió con los criterios iniciales.\n\n"
f"Como plan B, el agente ha seleccionado los **Top {len(best_models_list)}** modelos con el mejor **{metric_name} promedio**: `{', '.join(best_models_list)}`.")
return best_models_list, reasoning
# --- INICIALIZACIÓN DE AGENTES GLOBALES ---
log_agent = LoggingAgent(); validation_agent = StructureValidationAgent(log_agent)
instruction_parser_agent = InstructionParsingAgent(log_agent, hf_engine)
model_selection_agent = ModelSelectionAgent(log_agent)
# --- FUNCIONES DEL PIPELINE ---
def create_dummy_plot(title="Esperando resultados..."):
fig = go.Figure(go.Scatter(x=[], y=[])); fig.update_layout(title=title, template="plotly_white", height=500, annotations=[dict(text="Sube un archivo y ejecuta", showarrow=False)])
return fig
def detect_experiments(file_obj):
if not file_obj: return gr.update(choices=[], value=[])
try:
df_first_row = pd.read_excel(file_obj.name, header=None, nrows=1)
exp_names = [str(name).strip() for name in df_first_row.iloc[0].dropna().tolist()]
return gr.update(choices=exp_names, value=exp_names, interactive=True)
except Exception as e: return gr.update(choices=[], value=[], interactive=False, placeholder=f"Error: {e}")
# ... (ETAPA 1: run_base_analysis - sin cambios) ...
def run_base_analysis(file, models, exp_names_selected, component, use_de, maxfev, progress=gr.Progress()):
log_agent.clear(); progress(0, desc="🚀 Iniciando Análisis Base...")
if not file or not models or not exp_names_selected:
return create_dummy_plot(), None, "❌ Por favor, sube un archivo y selecciona modelos/experimentos.", gr.update(interactive=False), {}, None, None, log_agent.get_report()
log_agent.register("Pipeline (Etapa 1)", "Iniciando Análisis Base."); progress(0.2, desc="Validando archivo...")
is_valid, msg = validation_agent.validate(file)
if not is_valid: return create_dummy_plot(), None, msg, gr.update(interactive=False), {}, None, None, log_agent.get_report()
progress(0.5, desc="Ejecutando análisis biotecnológico...");
if not biotech_client: return create_dummy_plot(), None, "❌ Cliente BiotechU4 no disponible.", gr.update(interactive=False), {}, None, None, log_agent.get_report()
try:
exp_names_str = ",".join(exp_names_selected); models_lower = [str(m).lower() for m in models]
plot_info, df_data, status = biotech_client.predict(file=handle_file(file.name), models=models_lower, component=component, use_de=use_de, maxfev=maxfev, exp_names=exp_names_str, api_name="/run_analysis_wrapper")
if "Error" in status: raise Exception(status)
except Exception as e:
return create_dummy_plot(), None, f"❌ Error en Análisis Base: {e}", gr.update(interactive=False), {}, None, None, log_agent.get_report()
progress(1, desc="🎉 Análisis Base Completado")
final_status = "✅ Análisis Base completado. \n➡️ Ahora puedes aplicar el filtro de IA y generar el informe final."
results_df_obj = {'data': df_data['data'], 'headers': df_data['headers']}
fig = go.Figure(json.loads(plot_info['plot'])) if plot_info and 'plot' in plot_info else create_dummy_plot()
original_params = {'exp_names': exp_names_selected, 'component': component, 'use_de': use_de, 'maxfev': maxfev}
return fig, df_data, final_status, gr.update(interactive=True), results_df_obj, file.name, original_params, log_agent.get_report()
# --- ETAPA 2: REFINAMIENTO Y REPORTE IA (ACTUALIZADA) ---
def refine_and_generate_report(baseline_results, file_path, original_params, r2_threshold, rmse_threshold, instructions_text, ia_model, detail_level, language, max_output_tokens, use_personal_key, personal_api_key, progress=gr.Progress()):
progress(0, desc="🚀 Iniciando Refinamiento con IA..."); log_agent.register("Pipeline (Etapa 2)", "Iniciando Refinamiento.")
if not baseline_results or not file_path or not original_params:
return gr.update(), None, None, None, "❌ No hay resultados base para refinar.", None, log_agent.get_report()
progress(0.1, desc="Agente de Parseo interpretando instrucciones...")
instructions = instruction_parser_agent.parse(instructions_text)
log_agent.register("InstructionParsingAgent", "Instrucciones interpretadas.", f"Plan: {instructions}")
progress(0.2, desc="Agente de Selección identificando mejores modelos...")
results_df = pd.DataFrame(baseline_results['data'], columns=baseline_results['headers'])
best_models, reasoning = model_selection_agent.identify_best_models(results_df, original_params['component'], r2_threshold, rmse_threshold, instructions)
if not best_models:
return gr.update(), baseline_results, None, None, f"🤖 Análisis del Agente:\n{reasoning}", None, log_agent.get_report()
progress(0.4, desc="Re-ejecutando análisis con los mejores modelos...");
try:
exp_names_str = ",".join(original_params['exp_names'])
final_plot_info, final_df_data, final_status = biotech_client.predict(file=handle_file(file_path), models=best_models, component=original_params['component'], use_de=original_params['use_de'], maxfev=original_params['maxfev'], exp_names=exp_names_str, api_name="/run_analysis_wrapper")
if "Error" in final_status: raise Exception(final_status)
except Exception as e:
return gr.update(), None, None, None, f"❌ Error en el re-análisis final: {e}", None, log_agent.get_report()
progress(0.6, desc="Generando informe IA..."); temp_csv_file = None
try:
final_results_df = pd.DataFrame(final_df_data['data'], columns=final_df_data['headers'])
with tempfile.NamedTemporaryFile(mode='w+', suffix='.csv', delete=False, encoding='utf-8') as temp_f:
final_results_df.to_csv(temp_f.name, index=False); temp_csv_file = temp_f.name
current_analysis_client = analysis_client
if use_personal_key and personal_api_key: current_analysis_client = Client("C2MV/Project-HF-2025-2", hf_token=personal_api_key)
chunk_update_dict = current_analysis_client.predict(files=[handle_file(temp_csv_file)], api_name="/update_chunk_column_selector")
selected_chunk_column = chunk_update_dict['choices'][0][0]
result = current_analysis_client.predict(files=[handle_file(temp_csv_file)], chunk_column=selected_chunk_column, qwen_model=ia_model, detail_level=detail_level, language=language, additional_specs="", max_output_tokens=max_output_tokens, api_name="/process_files_and_analyze")
_, analysis_report, implementation_code, token_usage = result
except Exception as e:
return gr.update(), final_df_data, None, None, f"❌ Error generando informe IA: {e}", None, log_agent.get_report()
finally:
if temp_csv_file and os.path.exists(temp_csv_file): os.remove(temp_csv_file)
progress(0.9, desc="Finalizando..."); final_report_path = None
if analysis_report:
export_dir = "exported_reports"; os.makedirs(export_dir, exist_ok=True)
final_report_path = os.path.join(export_dir, f"report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md")
with open(final_report_path, 'w', encoding='utf-8') as f: f.write(analysis_report)
final_status = f"✅ Refinamiento y reporte completados.\n{reasoning}\nInforme IA generado con {token_usage}."
final_fig = go.Figure(json.loads(final_plot_info['plot'])) if final_plot_info and 'plot' in final_plot_info else create_dummy_plot()
return final_fig, final_df_data, analysis_report, implementation_code, final_status, final_report_path, log_agent.get_report()
# ... (create_dummy_excel_file y constantes sin cambios) ...
def create_dummy_excel_file():
examples_dir = "examples"; os.makedirs(examples_dir, exist_ok=True); file_path = os.path.join(examples_dir, "archivo.xlsx")
if not os.path.exists(file_path):
exp_names = ['CN 20_1', 'CN 20_2', 'CN 30_1', 'CN 40_1']; writer = pd.ExcelWriter(file_path, engine='xlsxwriter'); worksheet = writer.book.add_worksheet('Datos'); writer.sheets['Datos'] = worksheet
for i, name in enumerate(exp_names): worksheet.write(0, i * 4, name)
start_col = 0
for _ in exp_names:
time = np.arange(0, 11, 2); biomass = 0.2 + (np.random.rand() * 20) / (1 + np.exp(4 - 0.5 * time)) + np.random.rand(len(time)) * 0.2
substrate = 10 * np.exp(-0.2 * time) + np.random.rand(len(time)) * 0.3; product = 1 * (1 - np.exp(-0.3 * time)) + np.random.rand(len(time)) * 0.1
df = pd.DataFrame({'Tiempo': time, 'Biomasa': biomass, 'Sustrato': substrate, 'Producto': product})
df.to_excel(writer, sheet_name='Datos', startrow=1, startcol=start_col, index=False); start_col += 4
writer.close()
BIOTECH_MODELS = ['logistic', 'gompertz', 'moser', 'baranyi', 'monod', 'contois', 'andrews', 'tessier', 'richards', 'stannard', 'huang']
IA_MODELS = ["deepseek-ai/DeepSeek-V3-0324"]
theme = gr.themes.Soft(primary_hue="blue", secondary_hue="indigo", neutral_hue="slate")
if __name__ == "__main__":
create_dummy_excel_file()
with gr.Blocks(theme=theme, title="BioTech Analysis & Report Generator") as demo:
gr.Markdown("# 🧬 BioTech Analysis & Report Generator v7.8")
gr.Markdown("### Un pipeline inteligente de dos etapas: Análisis Base y Refinamiento con IA.")
baseline_results_state = gr.State(value=None); file_path_state = gr.State(value=None); original_params_state = gr.State(value=None)
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 1. Carga y Configuración del Análisis Base")
file_input = gr.File(label="📁 Archivo de Datos", file_types=[".xlsx", ".xls"]); gr.Examples(examples=["examples/archivo.xlsx"], inputs=[file_input])
exp_names_input = gr.CheckboxGroup(label="🔬 Experimentos a Analizar", interactive=False)
models_input = gr.CheckboxGroup(choices=BIOTECH_MODELS, value=BIOTECH_MODELS, label="📊 Modelos a Evaluar")
component_input = gr.Dropdown(['all', 'biomass', 'substrate', 'product'], value='all', label="📈 Componente a Analizar/Filtrar")
with gr.Accordion("Parámetros Avanzados", open=False):
use_de_input = gr.Checkbox(label="🧮 Usar Evolución Diferencial", value=False)
maxfev_input = gr.Slider(label="🔄 Máx. Iteraciones", minimum=10000, maximum=100000, value=50000, step=1000)
run_base_analysis_btn = gr.Button("1. Ejecutar Análisis Base", variant="secondary")
with gr.Group():
gr.Markdown("### 2. Refinamiento con IA")
gr.Markdown("#### Criterios de Selección Primarios (Filtro Estricto)")
r2_threshold_slider = gr.Slider(minimum=0.0, maximum=0.99, value=0.9, step=0.01, label="R² Mínimo")
rmse_threshold_input = gr.Number(value=0.5, label="RMSE Máximo")
# --- NUEVO INPUT DE INSTRUCCIONES ---
additional_specs_input = gr.Textbox(label="📝 Instrucciones para Selección Avanzada (Plan B)",
placeholder="Ej: Usa R2 y RMSE y dame el top 2",
info="Si ningún modelo cumple los criterios de arriba, el agente seguirá estas instrucciones.")
with gr.Accordion("Parámetros del Informe de IA Final", open=False):
ia_model_input = gr.Dropdown(choices=IA_MODELS, value=IA_MODELS[0], label="🤖 Modelo de IA para Informe")
detail_level_input = gr.Radio(['detailed', 'summarized'], value='detailed', label="📋 Nivel de Detalle")
language_input = gr.Dropdown(['es', 'en'], value='es', label="🌐 Idioma")
max_output_tokens_input = gr.Slider(minimum=1000, maximum=32000, value=8000, step=100, label="🔢 Máx. Tokens")
use_personal_key_input = gr.Checkbox(label="Usar Token HF Personal", value=False)
personal_api_key_input = gr.Textbox(label="Token HF", type="password", visible=False)
refine_with_ia_btn = gr.Button("2. 🤖 Aplicar Filtro y Generar Informe IA", variant="primary", interactive=False)
with gr.Column(scale=2):
gr.Markdown("### 3. Resultados")
status_output = gr.Textbox(label="📊 Registro de Estado", lines=5, interactive=False)
with gr.Tabs():
with gr.TabItem("📊 Visualización"): plot_output = gr.Plot()
with gr.TabItem("📋 Tabla de Modelado"): table_output = gr.Dataframe()
with gr.TabItem("📝 Informe IA"): analysis_output = gr.Markdown("El informe aparecerá aquí.")
with gr.TabItem("💻 Código"): code_output = gr.Code(language="python")
with gr.TabItem("🕵️ Registro de Agentes"): agent_log_output = gr.Markdown()
download_link_markdown = gr.Markdown("*El enlace de descarga aparecerá aquí.*")
report_output = gr.File(label="📥 Descargar Informe", interactive=False)
report_path_state = gr.State(value=None)
file_input.upload(fn=detect_experiments, inputs=file_input, outputs=exp_names_input)
use_personal_key_input.change(lambda x: gr.update(visible=x), inputs=use_personal_key_input, outputs=personal_api_key_input)
run_base_analysis_btn.click(
fn=run_base_analysis,
inputs=[file_input, models_input, exp_names_input, component_input, use_de_input, maxfev_input],
outputs=[plot_output, table_output, status_output, refine_with_ia_btn, baseline_results_state, file_path_state, original_params_state, agent_log_output]
)
refine_with_ia_btn.click(
fn=refine_and_generate_report,
inputs=[baseline_results_state, file_path_state, original_params_state, r2_threshold_slider, rmse_threshold_input, additional_specs_input, ia_model_input, detail_level_input, language_input, max_output_tokens_input, use_personal_key_input, personal_api_key_input],
outputs=[plot_output, table_output, analysis_output, code_output, status_output, report_path_state, agent_log_output]
)
def update_dl_link(path):
if path and os.path.exists(path): return f"**¡Informe listo!** 👉 [**Descargar '{os.path.basename(path)}'**](/file={path})"
return "*No se generó ningún archivo para descargar.*"
report_path_state.change(fn=update_dl_link, inputs=report_path_state, outputs=download_link_markdown)
demo.launch(show_error=True, debug=True) |