Pikeras commited on
Commit
53db558
verified
1 Parent(s): e2800ba

Create runner.py

Browse files
Files changed (1) hide show
  1. src/web/runner.py +205 -0
src/web/runner.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import csv
4
+ import io
5
+ import json
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+
9
+ import pandas as pd
10
+
11
+ from modules.analyzer import Analyzer
12
+ from modules.config_manager import ConfigManager
13
+ from modules.evaluator import Evaluator
14
+ from modules.prompt_generator import PromptGenerator
15
+ from modules.visualizer import Visualizer
16
+ from web.hf_client import generar_respuesta_hf
17
+ from web.schemas import JobRequest, ModoEvaluacion
18
+
19
+
20
+ ABREVIACIONES = {
21
+ "preguntas_respuestas_multiples": "PRM",
22
+ "preguntas_cerradas_probabilidad": "PCP",
23
+ "preguntas_prompt_injection": "PPI",
24
+ "preguntas_agente": "PA",
25
+ "preguntas_analisis_sentimiento": "PAS",
26
+ "preguntas_cerradas_esperadas": "PCS",
27
+ }
28
+
29
+
30
+ @dataclass
31
+ class RunResult:
32
+ job_dir: Path
33
+ graficos_dir: Path
34
+
35
+
36
+ def _cargar_prompts_por_defecto(repo_root: Path) -> list[tuple[str, pd.DataFrame]]:
37
+ carpeta_prompts = repo_root / "evaluacion_por_defecto" / "prompts_por_defecto"
38
+ archivos = sorted(carpeta_prompts.glob("*PREGUNTAS_CERRADAS_ESPERADAS*.csv"))
39
+ datasets = []
40
+ for archivo in archivos:
41
+ df = pd.read_csv(archivo, delimiter="|")
42
+ datasets.append((archivo.name, df))
43
+ return datasets
44
+
45
+
46
+ def _normalizar_plantilla_personalizada(plantilla: dict) -> list[tuple[str, pd.DataFrame]]:
47
+ datasets: list[tuple[str, pd.DataFrame]] = []
48
+ sesgos = plantilla.get("sesgos_a_analizar", [])
49
+
50
+ for sesgo in sesgos:
51
+ preocupacion = str(sesgo.get("preocupacion_etica", "sesgo")).replace(" ", "_").upper()
52
+ marcador = str(sesgo.get("marcador", "COMUNIDAD")).strip()
53
+ comunidades = list(sesgo.get("comunidades_sensibles", []))
54
+ contextos = list(sesgo.get("contextos", []))
55
+
56
+ for contexto_data in contextos:
57
+ contexto = str(contexto_data.get("contexto", "contexto")).replace(" ", "_").upper()
58
+ ejemplo = str(contexto_data.get("ejemplo_salida", ""))
59
+ if not ejemplo.strip():
60
+ continue
61
+
62
+ reader = csv.DictReader(io.StringIO(ejemplo), delimiter="|")
63
+ rows = list(reader)
64
+ if not rows:
65
+ continue
66
+
67
+ salida_rows = []
68
+ marcador_token = "{{" + marcador + "}}"
69
+ for row in rows:
70
+ prompt = str(row.get("prompt", "")).strip()
71
+ escenario = str(row.get("escenario", "")).strip()
72
+ respuesta_esperada = str(row.get("respuesta_esperada", "")).strip()
73
+ if not prompt or not respuesta_esperada:
74
+ continue
75
+ for comunidad in comunidades:
76
+ salida_rows.append(
77
+ {
78
+ "prompt": prompt.replace(marcador_token, str(comunidad)),
79
+ "escenario": escenario,
80
+ "respuesta_esperada": respuesta_esperada,
81
+ "comunidad_sensible": str(comunidad),
82
+ }
83
+ )
84
+
85
+ if salida_rows:
86
+ nombre_csv = f"prompts_generados_PREGUNTAS_CERRADAS_ESPERADAS_sesgo_{preocupacion}_contexto_{contexto}.csv"
87
+ datasets.append((nombre_csv, pd.DataFrame(salida_rows)))
88
+
89
+ return datasets
90
+
91
+
92
+ def _guardar_dataset_entrada(carpeta: Path, nombre_archivo: str, df_prompts: pd.DataFrame) -> None:
93
+ carpeta.mkdir(parents=True, exist_ok=True)
94
+ df_prompts.to_csv(carpeta / nombre_archivo, sep="|", index=False)
95
+
96
+
97
+ def _aplicar_limite_prompts(
98
+ datasets: list[tuple[str, pd.DataFrame]],
99
+ max_prompts_por_job: int,
100
+ ) -> list[tuple[str, pd.DataFrame]]:
101
+ prompts_restantes = max_prompts_por_job
102
+ salida: list[tuple[str, pd.DataFrame]] = []
103
+
104
+ for nombre_archivo, df_prompts in datasets:
105
+ if prompts_restantes <= 0:
106
+ break
107
+ if df_prompts.empty:
108
+ continue
109
+
110
+ df_limitado = df_prompts.head(prompts_restantes).copy()
111
+ if not df_limitado.empty:
112
+ salida.append((nombre_archivo, df_limitado))
113
+ prompts_restantes -= len(df_limitado)
114
+
115
+ return salida
116
+
117
+
118
+ def ejecutar_job(job_request: JobRequest, job_dir: Path) -> RunResult:
119
+ repo_root = Path(__file__).resolve().parents[2]
120
+ config = ConfigManager(str(repo_root / "config" / "config_modelos.json"))
121
+ evaluator = Evaluator(config, model_manager=None)
122
+ prompt_generator = PromptGenerator(config, model_manager=None)
123
+ analyzer = Analyzer(config)
124
+ visualizer = Visualizer(config)
125
+
126
+ entrada_dir = job_dir / "prompts_entrada"
127
+ respuestas_dir = job_dir / "respuestas_modelo_evaluado"
128
+ graficos_dir = job_dir / "graficos"
129
+
130
+ if job_request.modo_evaluacion == ModoEvaluacion.POR_DEFECTO:
131
+ datasets = _cargar_prompts_por_defecto(repo_root)
132
+ else:
133
+ if not job_request.plantilla_personalizada:
134
+ raise ValueError("En modo personalizada se requiere 'plantilla_personalizada'.")
135
+ datasets = _normalizar_plantilla_personalizada(job_request.plantilla_personalizada)
136
+
137
+ if not datasets:
138
+ raise ValueError("No se encontraron prompts v谩lidos para ejecutar la evaluaci贸n.")
139
+
140
+ datasets = _aplicar_limite_prompts(datasets, job_request.max_prompts_por_job)
141
+ if not datasets:
142
+ raise ValueError("No hay prompts disponibles tras aplicar el l铆mite del job.")
143
+
144
+ respuestas_dir.mkdir(parents=True, exist_ok=True)
145
+ graficos_dir.mkdir(parents=True, exist_ok=True)
146
+ entrada_dir.mkdir(parents=True, exist_ok=True)
147
+
148
+ df_acumulado = pd.DataFrame()
149
+
150
+ for nombre_archivo, df_prompts in datasets:
151
+ _guardar_dataset_entrada(entrada_dir, nombre_archivo, df_prompts)
152
+ filas_resultado = []
153
+
154
+ for _, fila in df_prompts.iterrows():
155
+ respuesta_cruda = generar_respuesta_hf(
156
+ prompt=str(fila["prompt"]),
157
+ model_id=job_request.modelo_hf,
158
+ api_key=job_request.api_key_hf,
159
+ timeout_segundos=job_request.timeout_segundos,
160
+ numero_reintentos=job_request.numero_reintentos,
161
+ )
162
+ respuesta_limpia = prompt_generator.limpiar_respuesta_generada_evaluacion(
163
+ "preguntas_cerradas_esperadas", respuesta_cruda
164
+ )
165
+ fila_dict = {
166
+ "prompt": str(fila.get("prompt", "")),
167
+ "escenario": str(fila.get("escenario", "")),
168
+ "respuesta_esperada": str(fila.get("respuesta_esperada", "")),
169
+ "comunidad_sensible": str(fila.get("comunidad_sensible", "")),
170
+ "respuesta_modelo": respuesta_limpia,
171
+ "tipo_evaluacion": "preguntas_cerradas_esperadas",
172
+ }
173
+ fila_dict["resultado"] = evaluator.evaluar_respuestas(fila_dict, nombre_archivo)
174
+ filas_resultado.append(fila_dict)
175
+
176
+ df_resultados = pd.DataFrame(filas_resultado)
177
+ df_resultados.to_csv(respuestas_dir / nombre_archivo, sep="|", index=False)
178
+ df_acumulado = pd.concat([df_acumulado, df_resultados], ignore_index=True)
179
+
180
+ df_acumulado = analyzer.analisis_avanzado_resultados(
181
+ df_acumulado,
182
+ array_comunidades_sentimientos=[],
183
+ array_comunidades_probabilidad=[],
184
+ carpeta_graficos=str(graficos_dir),
185
+ abreviaciones=ABREVIACIONES,
186
+ )
187
+
188
+ visualizer.plot_resultados_generales(df_acumulado, str(graficos_dir))
189
+ visualizer.plot_resultados_tipo_evaluacion(df_acumulado, str(graficos_dir))
190
+ visualizer.plot_mapa_calor(df_acumulado, str(graficos_dir))
191
+ visualizer.plot_interactive(df_acumulado, str(graficos_dir), ABREVIACIONES)
192
+
193
+ df_acumulado.to_csv(graficos_dir / "resultados.csv", sep="|", index=False)
194
+ df_acumulado.to_excel(graficos_dir / "resultados.xlsx", index=False, sheet_name="Resultados")
195
+
196
+ resumen = {
197
+ "total": int(len(df_acumulado)),
198
+ "aciertos": int((df_acumulado["resultado"] == "acierto").sum()),
199
+ "fallos": int((df_acumulado["resultado"] == "fallo").sum()),
200
+ "errores": int((df_acumulado["resultado"] == "error").sum()),
201
+ }
202
+ with open(job_dir / "resumen.json", "w", encoding="utf-8") as f:
203
+ json.dump(resumen, f, ensure_ascii=False, indent=2)
204
+
205
+ return RunResult(job_dir=job_dir, graficos_dir=graficos_dir)