import json import os import pandas as pd from datetime import datetime, timezone from typing import Optional, Any from src.display.formatting import styled_error, styled_message, styled_warning from src.envs import API, EVAL_REQUESTS_PATH, EVAL_RESULTS_PATH, TOKEN, QUEUE_REPO, RESULTS_REPO from src.submission.check_validity import already_submitted_models, check_model_card, get_model_size, is_model_on_hub from src.submission.do_exam import run_exam from src.populate import get_leaderboard_df from src.display.utils import BENCHMARK_COLS, COLS REQUESTED_MODELS = None USERS_TO_SUBMISSION_DATES = None def add_new_eval( model: str, base_model: str, revision: str, precision: str, weight_type: str, model_type: str, submit_type: str = "huggingface", openrouter_key: str = None, exam_theme: str = None, progress: Optional[Any] = None, leaderboard_component = None, # Componente Gradio para actualizar el leaderboard ): """ Añade una nueva evaluación al sistema. Args: model: Nombre del modelo (HF Hub) o identificador (OpenRouter) base_model: Modelo base para pesos delta/adapter revision: Revisión del modelo (solo para HF Hub) precision: Precisión del modelo weight_type: Tipo de pesos (Original/Delta/Adapter) model_type: Tipo de modelo (pretrained/finetuned/etc) submit_type: "huggingface" u "openrouter" openrouter_key: API key para OpenRouter (requerido si submit_type es "openrouter") exam_theme: Tema específico para la evaluación (solo para OpenRouter) progress: Función para actualizar el progreso (opcional) leaderboard_component: Componente del leaderboard para actualizar (opcional) """ global REQUESTED_MODELS global USERS_TO_SUBMISSION_DATES if not REQUESTED_MODELS: REQUESTED_MODELS, USERS_TO_SUBMISSION_DATES = already_submitted_models(EVAL_REQUESTS_PATH) current_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") if model_type is None or model_type == "": return styled_error("Por favor selecciona un tipo de modelo.") if submit_type == "huggingface": # Validar modelo de Hugging Face if revision == "": revision = "main" if weight_type in ["Delta", "Adapter"]: base_model_on_hub, error, _ = is_model_on_hub( model_name=base_model, revision=revision, token=TOKEN, test_tokenizer=True ) if not base_model_on_hub: return styled_error(f'El modelo base "{base_model}" {error}') if weight_type != "Adapter": model_on_hub, error, _ = is_model_on_hub( model_name=model, revision=revision, token=TOKEN, test_tokenizer=True ) if not model_on_hub: return styled_error(f'El modelo "{model}" {error}') try: model_info = API.model_info(repo_id=model, revision=revision) license = model_info.cardData.get("license", "Unknown") model_size = get_model_size(model_info=model_info, precision=precision) likes = model_info.likes except Exception: return styled_error("No se pudo obtener la información del modelo. Por favor verifica que esté correctamente configurado.") # Verificar model card y licencia modelcard_OK, error_msg = check_model_card(model) if not modelcard_OK: return styled_error(error_msg) elif submit_type == "openrouter": if not openrouter_key: return styled_error("Se requiere una API key de OpenRouter.") revision = "openrouter" license = "API Service" model_size = 0 # No podemos determinarlo likes = 0 # Parece correcto, crear la evaluación print("Añadiendo nueva evaluación con OpenRouter") # Mostrar progreso inicial si está disponible if progress is not None: progress(0.01, "Iniciando evaluación con OpenRouter...") # Crear configuración para guardar en los resultados eval_entry = { "model": model, "base_model": base_model, "revision": revision, "precision": precision, "weight_type": weight_type, "status": "RUNNING", # Inicialmente running mientras evaluamos "submitted_time": current_time, "model_type": model_type, "submit_type": submit_type, "likes": likes, "params": model_size, "license": license, "private": False, } try: # Ejecutar evaluación con OpenRouter directamente print(f"Ejecutando examen para {model} con OpenRouter" + (f" (tema: {exam_theme})" if exam_theme else " (todos los temas)")) results = run_exam( model_name=model, openai_api_key=openrouter_key, openrouter_base_url="https://openrouter.ai/api/v1", exam_theme=exam_theme, progress=progress ) # Actualizar estado a FINISHED tras completar la evaluación eval_entry["status"] = "FINISHED" # Guardar los resultados obtenidos if progress is not None: progress(0.91, "Guardando resultados...") model_path = model.replace("/", "_") if "/" in model else model # Crear archivo de resultados results_file = f"{EVAL_RESULTS_PATH}/results_{model_path}_{precision}.json" results_data = { "config": eval_entry, "results": results } # Guardar resultados localmente with open(results_file, "w") as f: json.dump(results_data, f) # Subir resultados al dataset con el tema en el mensaje de commit si está disponible if progress is not None: progress(0.95, "Subiendo resultados al repositorio...") print("Subiendo resultados de evaluación") commit_message = f"Add {model} evaluation results" if exam_theme: commit_message += f" (tema: {exam_theme})" API.upload_file( path_or_fileobj=results_file, path_in_repo=f"results_{model_path}_{precision}.json", repo_id=RESULTS_REPO, repo_type="dataset", commit_message=commit_message ) # Actualizar el leaderboard con los nuevos resultados if progress is not None: progress(0.98, "Actualizando leaderboard...") if leaderboard_component is not None: try: # Obtener los datos actualizados del leaderboard updated_df = get_leaderboard_df(EVAL_RESULTS_PATH, EVAL_REQUESTS_PATH, COLS, BENCHMARK_COLS) # Actualizar el componente del leaderboard leaderboard_component.update(value=updated_df) except Exception as e: print(f"Error al actualizar el leaderboard: {e}") # Finalizar progreso si está disponible if progress is not None: progress(1.0, "¡Evaluación completada con éxito!") return styled_message( f"¡Evaluación de {model} completada con éxito! " + f"Resultados: Puntuación global: {results['overall']['accuracy']:.2f} " + f"({results['overall']['total_questions']} preguntas evaluadas)" ) except Exception as e: # Marcar progreso como error si está disponible if progress is not None: progress(1.0, f"Error: {str(e)}") return styled_error(f"Error al evaluar con OpenRouter: {str(e)}") else: return styled_error("Tipo de submit no válido") # Solo llegamos aquí para modelos de Hugging Face (no OpenRouter) # Parece correcto, crear la evaluación print("Añadiendo nueva evaluación") eval_entry = { "model": model, "base_model": base_model, "revision": revision, "precision": precision, "weight_type": weight_type, "status": "PENDING", "submitted_time": current_time, "model_type": model_type, "submit_type": submit_type, "likes": likes, "params": model_size, "license": license, "private": False, } # Verificar duplicados if f"{model}_{revision}_{precision}" in REQUESTED_MODELS: return styled_warning("Este modelo ya ha sido enviado anteriormente.") # Guardar resultados print("Creando archivo de evaluación") user_name = model.split("/")[0] if "/" in model else "openrouter" model_path = model.split("/")[1] if "/" in model else model OUT_DIR = f"{EVAL_REQUESTS_PATH}/{user_name}" os.makedirs(OUT_DIR, exist_ok=True) request_file = f"{OUT_DIR}/{model_path}_eval_request_False_{precision}_{weight_type}.json" with open(request_file, "w") as f: json.dump(eval_entry, f) print("Subiendo archivo de evaluación") API.upload_file( path_or_fileobj=request_file, path_in_repo=f"{user_name}/{model_path}_eval_request_False_{precision}_{weight_type}.json", repo_id=QUEUE_REPO, repo_type="dataset", commit_message=f"Add {model} to eval queue", ) # Eliminar archivo local os.remove(request_file) return styled_message( "¡Tu solicitud ha sido enviada! Por favor espera hasta una hora para que el modelo aparezca en la lista PENDING." )