cesparzaf commited on
Commit
00161b9
·
verified ·
1 Parent(s): 7b8dd49

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +114 -95
app.py CHANGED
@@ -5,12 +5,14 @@ import csv, os, time
5
  import gradio as gr
6
  import matplotlib.pyplot as plt
7
 
 
 
 
8
  DEFAULT_COLS = [
9
  "Código", "Indicador", "Score (0–4)",
10
  "Entailment medio", "Evidencias (hipótesis)", "Descripción"
11
  ]
12
 
13
- # --------- UI: meta y estilos ---------
14
  CUSTOM_CSS = """
15
  #app {max-width: 1200px; margin: 0 auto;}
16
  .badge {
@@ -26,7 +28,9 @@ CUSTOM_CSS = """
26
  .small {font-size: 12px; opacity: .9;}
27
  """
28
 
29
- # Nombres y descripciones cortas (IPMA ICB4 4.4.5.x)
 
 
30
  INDICATOR_META = {
31
  "4.4.5.1": ("Iniciativa y ayuda proactiva",
32
  "Inicia acciones sin que se lo pidan; ofrece ayuda, anticipa y equilibra riesgos."),
@@ -40,16 +44,30 @@ INDICATOR_META = {
40
  "Toma decisiones bajo incertidumbre; explica razones; revisa con nueva evidencia; comunica con claridad.")
41
  }
42
 
43
- # --- Carga perezosa de modelos (se inicializan al primer uso) ---
 
 
44
  _llm = None
45
  _llm_tok = None
46
  _gen = None
47
- _nli = None
48
-
49
- # === Modelos (ligeros + robustos para CPU Basic) ===
50
- LLM_ID = "Qwen/Qwen2.5-0.5B-Instruct" # multilingüe y compacto
51
- # NLI que puntúa bien en ES
52
- NLI_ID = "MoritzLaurer/mDeBERTa-v3-base-xnli-multilingual-nli-2mil7"
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  STAR_PROMPT = """Eres evaluador ICB4. Toma el texto del candidato y devuélvelo en formato STAR como JSON válido con claves:
55
  "situation" (<=3 frases), "task" (<=2 frases), "action" (lista de viñetas, verbos de acción), "result" (lista de viñetas, resultados/indicadores/aprendizajes).
@@ -91,18 +109,17 @@ HYP: Dict[str, List[str]] = {
91
  ]
92
  }
93
 
94
- # --------- Modelos ---------
 
 
95
  def lazy_load_llm():
96
- """Crea pipeline de generación sin flags que generen warnings en CPU."""
97
  global _llm, _llm_tok, _gen
 
98
  if _gen is not None:
99
  return _gen
100
- from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
101
  _llm_tok = AutoTokenizer.from_pretrained(LLM_ID)
102
- _llm = AutoModelForCausalLM.from_pretrained(
103
- LLM_ID,
104
- device_map="auto"
105
- )
106
  _gen = pipeline(
107
  "text-generation",
108
  model=_llm,
@@ -113,22 +130,24 @@ def lazy_load_llm():
113
  )
114
  return _gen
115
 
116
- def lazy_load_nli():
117
- """NLI robusto (scores completos) + truncation para textos largos."""
118
- global _nli
119
- if _nli is not None:
120
- return _nli
121
  from transformers import pipeline
122
- _nli = pipeline(
 
 
123
  "text-classification",
124
- model=NLI_ID,
125
- tokenizer=NLI_ID,
126
- return_all_scores=True, # lista completa de {label, score}
127
- truncation=True
128
  )
129
- return _nli
 
130
 
131
- # --------- Utilidades ---------
 
 
132
  def extract_json_block(text: str) -> str:
133
  start = text.find("{")
134
  end = text.rfind("}")
@@ -186,9 +205,17 @@ def extract_star(user_text: str) -> Dict:
186
  "result": [str(r).strip(" •-") for r in data["result"] if str(r).strip()],
187
  }
188
 
189
- def nli_entails(premise: str, hypothesis: str) -> float:
190
- """Devuelve probabilidad de ENTAILMENT (0..1)."""
191
- nli = lazy_load_nli()
 
 
 
 
 
 
 
 
192
 
193
  def _trim(s: str, limit=900):
194
  s = (s or "").strip()
@@ -221,40 +248,49 @@ def nli_entails(premise: str, hypothesis: str) -> float:
221
  return 0.0
222
  return 0.0
223
 
224
- def map_prob_to_score(p: float) -> int:
225
- if p >= 0.80: return 4
226
- if p >= 0.60: return 3
227
- if p >= 0.40: return 2
228
- if p >= 0.20: return 1
 
229
  return 0
230
 
231
- def score_indicator(premise: str, hyps: List[str]) -> Tuple[int, List[Tuple[str, float]], float]:
232
- probs = [(h, nli_entails(premise, h)) for h in hyps]
 
 
233
  avg = sum(p for _, p in probs) / max(1, len(probs))
234
- score = map_prob_to_score(avg)
235
  probs_sorted = sorted(probs, key=lambda x: x[1], reverse=True)[:2]
236
  return score, probs_sorted, avg
237
 
238
- def evaluate(texto: str):
239
- """Orquestación completa con salida: status_msg, fig_plot, table_dict."""
 
 
 
240
  try:
241
  if not texto or not texto.strip():
242
  return "Introduce un caso en formato STAR (o texto libre).", None, {"columns": [], "data": []}
243
 
 
 
 
 
 
 
244
  star = extract_star(texto)
245
 
246
- # Limita premisa a 6 acciones + 4 resultados (mejor señal para NLI)
247
  actions = (star.get("action", []) or [])[:6]
248
  results = (star.get("result", []) or [])[:4]
249
  premise = " ".join(actions) + " " + " ".join(results)
250
 
251
- # --- Scoring por indicador ---
252
- scores = []
253
- table_rows = []
254
- per_indicator_values = []
255
-
256
  for ind, hyps in HYP.items():
257
- s, ev, avg = score_indicator(premise, hyps)
258
  scores.append(s)
259
  per_indicator_values.append((ind, s))
260
  best_evid = " / ".join([h for h, _ in ev])
@@ -263,7 +299,7 @@ def evaluate(texto: str):
263
 
264
  overall = round(sum(scores) / max(1, len(scores)), 2)
265
 
266
- # --- Gráfica de barras (0-4) ---
267
  labels = [f"{k.split('.')[-1]}" for k, _ in per_indicator_values]
268
  values = [v for _, v in per_indicator_values]
269
  fig, ax = plt.subplots(figsize=(8.2, 4.0))
@@ -271,19 +307,16 @@ def evaluate(texto: str):
271
  ax.set_ylim(0, 4)
272
  ax.set_xlabel("Indicadores 4.4.5.x")
273
  ax.set_ylabel("Score (0–4)")
274
- fig.suptitle(f"ICB4 4.4.5 Leadership — Score global: {overall}", y=0.97)
275
  fig.subplots_adjust(top=0.86)
276
  for i, v in enumerate(values):
277
  ax.text(i, v + 0.08, f"{v}", ha="center", va="bottom")
278
  fig.tight_layout()
279
 
280
- table = {
281
- "columns": DEFAULT_COLS,
282
- "data": table_rows
283
- }
284
-
285
  msg = (
286
  f"Evaluación completada. Score global (0–4): {overall}\n"
 
287
  f"Sugerencia: revisa evidencias y ajusta umbrales según tu rúbrica."
288
  )
289
  return msg, fig, table
@@ -291,7 +324,9 @@ def evaluate(texto: str):
291
  except Exception as e:
292
  return f"⚠️ Error en evaluate(): {type(e).__name__}: {e}", None, {"columns": [], "data": []}
293
 
294
- # --------- Helper CSV ---------
 
 
295
  def make_csv_from_table(table: dict) -> str:
296
  cols = table.get("columns", [])
297
  rows = table.get("data", [])
@@ -304,7 +339,9 @@ def make_csv_from_table(table: dict) -> str:
304
  writer.writerow(r)
305
  return path if os.path.exists(path) else ""
306
 
307
- # --------- UI Gradio (estilo pro, 2 columnas, CSV) ---------
 
 
308
  with gr.Blocks(title="ICB4 4.4.5 Leadership — Evaluación STAR (FRAQX)", css=CUSTOM_CSS, elem_id="app") as demo:
309
  gr.Markdown(
310
  """
@@ -312,14 +349,22 @@ with gr.Blocks(title="ICB4 4.4.5 Leadership — Evaluación STAR (FRAQX)", css=C
312
  <img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" height="28">
313
  <h1 style="margin:0;">ICB4 • 4.4.5 Leadership — Evaluación STAR + NLI</h1>
314
  </div>
315
- <div class="small">Extracción STAR, scoring (4.4.5.1–4.4.5.5), gráfica y reporte descargable.</div>
316
  """
317
  )
318
 
319
  with gr.Row(equal_height=True):
320
- # -------- Columna izquierda (entrada) --------
321
  with gr.Column(scale=5):
322
  gr.Markdown("<div class='card'><b>Entrada</b></div>")
 
 
 
 
 
 
 
 
323
  texto = gr.Textbox(
324
  label="Caso (STAR o texto libre)",
325
  lines=16,
@@ -344,38 +389,28 @@ with gr.Blocks(title="ICB4 4.4.5 Leadership — Evaluación STAR (FRAQX)", css=C
344
  """,
345
  )
346
 
347
- # -------- Columna derecha (salida) --------
348
  with gr.Column(scale=7):
349
  gr.Markdown("<div class='card'><b>Resultados</b></div>")
350
  status = gr.Markdown(value="**Estado**: —", elem_id="status_md")
351
-
352
- # Cabecera con badge de score (se llena dinámicamente con Markdown)
353
  score_badge = gr.Markdown(value="<span class='badge'>Score global: —</span>")
354
-
355
- # Gráfica de barras
356
  plot = gr.Plot(label="Gráfica de evaluación (0–4)")
357
-
358
- # Tabla explicativa
359
  table = gr.Dataframe(
360
  headers=DEFAULT_COLS,
361
  datatype=["str", "str", "number", "str", "str", "str"],
362
  interactive=False,
363
  label="Detalle por indicador"
364
  )
365
-
366
- # Acciones de exportación
367
  with gr.Row():
368
  download_btn = gr.Button("Descargar CSV")
369
  csv_file = gr.File(label="Archivo CSV", visible=False)
370
 
371
- # -------- Lógica de interacción --------
372
- def run_eval(t: str):
373
- msg, fig, tbl = evaluate(t)
374
 
375
- # Estado en Markdown
376
  status_md = "**Estado** \n" + (msg or "").replace("\n", " \n")
377
 
378
- # Badge de score desde el texto
379
  badge_html = "<span class='badge'>Score global: —</span>"
380
  try:
381
  m = re.search(r"Score global \(0–4\):\s*([0-4](?:\.[0-9])?)", msg or "")
@@ -384,7 +419,6 @@ with gr.Blocks(title="ICB4 4.4.5 Leadership — Evaluación STAR (FRAQX)", css=C
384
  except Exception:
385
  pass
386
 
387
- # Tabla segura
388
  cols = (tbl or {}).get("columns") or DEFAULT_COLS
389
  data = (tbl or {}).get("data") or []
390
  safe_data = []
@@ -396,7 +430,6 @@ with gr.Blocks(title="ICB4 4.4.5 Leadership — Evaluación STAR (FRAQX)", css=C
396
  r = r[:len(cols)]
397
  safe_data.append(r)
398
 
399
- # Gráfica placeholder si hace falta
400
  if fig is None:
401
  fig, ax = plt.subplots(figsize=(6, 2))
402
  ax.axis("off")
@@ -404,29 +437,15 @@ with gr.Blocks(title="ICB4 4.4.5 Leadership — Evaluación STAR (FRAQX)", css=C
404
 
405
  return status_md, badge_html, fig, gr.update(value=safe_data, headers=cols)
406
 
407
- btn.click(
408
- fn=run_eval,
409
- inputs=[texto],
410
- outputs=[status, score_badge, plot, table]
411
- )
412
 
413
- # Botón de descarga CSV
414
- def export_csv_handler(t: str):
415
- _, _, tbl = evaluate(t)
416
  path = make_csv_from_table(tbl)
417
  return path, gr.update(visible=True)
418
 
419
- download_btn.click(
420
- fn=export_csv_handler,
421
- inputs=[texto],
422
- outputs=[csv_file, csv_file]
423
- )
424
 
425
- # --- Lanzamiento con cola (estable para CPU Basic) ---
426
  if __name__ == "__main__":
427
- demo.queue(
428
- max_size=16
429
- ).launch(
430
- ssr_mode=False,
431
- show_error=True
432
- )
 
5
  import gradio as gr
6
  import matplotlib.pyplot as plt
7
 
8
+ # ==========================
9
+ # Config & estilos
10
+ # ==========================
11
  DEFAULT_COLS = [
12
  "Código", "Indicador", "Score (0–4)",
13
  "Entailment medio", "Evidencias (hipótesis)", "Descripción"
14
  ]
15
 
 
16
  CUSTOM_CSS = """
17
  #app {max-width: 1200px; margin: 0 auto;}
18
  .badge {
 
28
  .small {font-size: 12px; opacity: .9;}
29
  """
30
 
31
+ # ==========================
32
+ # Metadatos IPMA ICB4 4.4.5.x
33
+ # ==========================
34
  INDICATOR_META = {
35
  "4.4.5.1": ("Iniciativa y ayuda proactiva",
36
  "Inicia acciones sin que se lo pidan; ofrece ayuda, anticipa y equilibra riesgos."),
 
44
  "Toma decisiones bajo incertidumbre; explica razones; revisa con nueva evidencia; comunica con claridad.")
45
  }
46
 
47
+ # ==========================
48
+ # Modelos (CPU Basic friendly)
49
+ # ==========================
50
  _llm = None
51
  _llm_tok = None
52
  _gen = None
53
+ _nli_cache: Dict[str, object] = {} # cache de pipelines NLI por model_id
54
+
55
+ LLM_ID = "Qwen/Qwen2.5-0.5B-Instruct" # LLM pequeño multilingüe para extraer STAR
56
+
57
+ # Selector de NLI con configuración asociada
58
+ MODEL_CHOICES = {
59
+ "Velocidad (MiniLM)": {
60
+ "id": "MoritzLaurer/multilingual-MiniLMv2-L12-mnli-xnli",
61
+ "calibrate": True,
62
+ "thresholds": (0.70, 0.50, 0.30, 0.15) # 4,3,2,1
63
+ },
64
+ "Precisión (DeBERTa)": {
65
+ "id": "MoritzLaurer/mDeBERTa-v3-base-xnli-multilingual-nli-2mil7",
66
+ "calibrate": False,
67
+ "thresholds": (0.80, 0.60, 0.40, 0.20)
68
+ }
69
+ }
70
+ DEFAULT_MODEL_KEY = "Velocidad (MiniLM)" # por defecto en Spaces gratis
71
 
72
  STAR_PROMPT = """Eres evaluador ICB4. Toma el texto del candidato y devuélvelo en formato STAR como JSON válido con claves:
73
  "situation" (<=3 frases), "task" (<=2 frases), "action" (lista de viñetas, verbos de acción), "result" (lista de viñetas, resultados/indicadores/aprendizajes).
 
109
  ]
110
  }
111
 
112
+ # ==========================
113
+ # Carga perezosa de modelos
114
+ # ==========================
115
  def lazy_load_llm():
116
+ """Pipeline de generación (Qwen 0.5B) para extraer STAR."""
117
  global _llm, _llm_tok, _gen
118
+ from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
119
  if _gen is not None:
120
  return _gen
 
121
  _llm_tok = AutoTokenizer.from_pretrained(LLM_ID)
122
+ _llm = AutoModelForCausalLM.from_pretrained(LLM_ID, device_map="auto")
 
 
 
123
  _gen = pipeline(
124
  "text-generation",
125
  model=_llm,
 
130
  )
131
  return _gen
132
 
133
+ def lazy_load_nli(model_id: str):
134
+ """NLI con salida completa y truncado seguro. Cachea por model_id."""
 
 
 
135
  from transformers import pipeline
136
+ if model_id in _nli_cache:
137
+ return _nli_cache[model_id]
138
+ nli = pipeline(
139
  "text-classification",
140
+ model=model_id,
141
+ tokenizer=model_id,
142
+ return_all_scores=True, # {label, score} para todas las clases
143
+ truncation=True # evita degradación por textos largos
144
  )
145
+ _nli_cache[model_id] = nli
146
+ return nli
147
 
148
+ # ==========================
149
+ # Utilidades extracción STAR
150
+ # ==========================
151
  def extract_json_block(text: str) -> str:
152
  start = text.find("{")
153
  end = text.rfind("}")
 
205
  "result": [str(r).strip(" •-") for r in data["result"] if str(r).strip()],
206
  }
207
 
208
+ # ==========================
209
+ # NLI + scoring (dinámico por modelo)
210
+ # ==========================
211
+ def calibrate_prob(p: float, use_calibration: bool) -> float:
212
+ """Calibración leve solo para MiniLM (p**0.9)."""
213
+ p = max(0.0, min(1.0, float(p)))
214
+ return (p ** 0.9) if use_calibration else p
215
+
216
+ def nli_entails(premise: str, hypothesis: str, model_id: str) -> float:
217
+ """Probabilidad de ENTAILMENT (0..1) robusta a variantes de salida."""
218
+ nli = lazy_load_nli(model_id)
219
 
220
  def _trim(s: str, limit=900):
221
  s = (s or "").strip()
 
248
  return 0.0
249
  return 0.0
250
 
251
+ def map_prob_to_score(p: float, thresholds: Tuple[float, float, float, float]) -> int:
252
+ t4, t3, t2, t1 = thresholds
253
+ if p >= t4: return 4
254
+ if p >= t3: return 3
255
+ if p >= t2: return 2
256
+ if p >= t1: return 1
257
  return 0
258
 
259
+ def score_indicator(premise: str, hyps: List[str], model_id: str, use_calibration: bool,
260
+ thresholds: Tuple[float, float, float, float]) -> Tuple[int, List[Tuple[str, float]], float]:
261
+ raw = [(h, nli_entails(premise, h, model_id)) for h in hyps]
262
+ probs = [(h, calibrate_prob(p, use_calibration)) for h, p in raw]
263
  avg = sum(p for _, p in probs) / max(1, len(probs))
264
+ score = map_prob_to_score(avg, thresholds)
265
  probs_sorted = sorted(probs, key=lambda x: x[1], reverse=True)[:2]
266
  return score, probs_sorted, avg
267
 
268
+ # ==========================
269
+ # Evaluación orquestada
270
+ # ==========================
271
+ def evaluate(texto: str, model_key: str):
272
+ """Devuelve: status_msg, matplotlib_fig, {"columns":[...], "data":[...] }."""
273
  try:
274
  if not texto or not texto.strip():
275
  return "Introduce un caso en formato STAR (o texto libre).", None, {"columns": [], "data": []}
276
 
277
+ # Config del modelo seleccionado
278
+ cfg = MODEL_CHOICES.get(model_key, MODEL_CHOICES[DEFAULT_MODEL_KEY])
279
+ model_id = cfg["id"]
280
+ use_calibration = cfg["calibrate"]
281
+ thresholds = cfg["thresholds"]
282
+
283
  star = extract_star(texto)
284
 
285
+ # Limita premisa para dar señal clara al NLI (6 A + 4 R)
286
  actions = (star.get("action", []) or [])[:6]
287
  results = (star.get("result", []) or [])[:4]
288
  premise = " ".join(actions) + " " + " ".join(results)
289
 
290
+ # Scoring por indicador
291
+ scores, table_rows, per_indicator_values = [], [], []
 
 
 
292
  for ind, hyps in HYP.items():
293
+ s, ev, avg = score_indicator(premise, hyps, model_id, use_calibration, thresholds)
294
  scores.append(s)
295
  per_indicator_values.append((ind, s))
296
  best_evid = " / ".join([h for h, _ in ev])
 
299
 
300
  overall = round(sum(scores) / max(1, len(scores)), 2)
301
 
302
+ # Gráfica
303
  labels = [f"{k.split('.')[-1]}" for k, _ in per_indicator_values]
304
  values = [v for _, v in per_indicator_values]
305
  fig, ax = plt.subplots(figsize=(8.2, 4.0))
 
307
  ax.set_ylim(0, 4)
308
  ax.set_xlabel("Indicadores 4.4.5.x")
309
  ax.set_ylabel("Score (0–4)")
310
+ fig.suptitle(f"ICB4 4.4.5 Leadership — Score global: {overall} | Modelo: {model_key}", y=0.97)
311
  fig.subplots_adjust(top=0.86)
312
  for i, v in enumerate(values):
313
  ax.text(i, v + 0.08, f"{v}", ha="center", va="bottom")
314
  fig.tight_layout()
315
 
316
+ table = {"columns": DEFAULT_COLS, "data": table_rows}
 
 
 
 
317
  msg = (
318
  f"Evaluación completada. Score global (0–4): {overall}\n"
319
+ f"Modelo: {model_key}\n"
320
  f"Sugerencia: revisa evidencias y ajusta umbrales según tu rúbrica."
321
  )
322
  return msg, fig, table
 
324
  except Exception as e:
325
  return f"⚠️ Error en evaluate(): {type(e).__name__}: {e}", None, {"columns": [], "data": []}
326
 
327
+ # ==========================
328
+ # CSV helper
329
+ # ==========================
330
  def make_csv_from_table(table: dict) -> str:
331
  cols = table.get("columns", [])
332
  rows = table.get("data", [])
 
339
  writer.writerow(r)
340
  return path if os.path.exists(path) else ""
341
 
342
+ # ==========================
343
+ # UI (2 columnas + selector modelo + CSV)
344
+ # ==========================
345
  with gr.Blocks(title="ICB4 4.4.5 Leadership — Evaluación STAR (FRAQX)", css=CUSTOM_CSS, elem_id="app") as demo:
346
  gr.Markdown(
347
  """
 
349
  <img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" height="28">
350
  <h1 style="margin:0;">ICB4 • 4.4.5 Leadership — Evaluación STAR + NLI</h1>
351
  </div>
352
+ <div class="small">Extracción STAR, scoring (4.4.5.1–4.4.5.5), gráfica y reporte descargable. Elige el modelo NLI según tu prioridad.</div>
353
  """
354
  )
355
 
356
  with gr.Row(equal_height=True):
357
+ # Entrada
358
  with gr.Column(scale=5):
359
  gr.Markdown("<div class='card'><b>Entrada</b></div>")
360
+
361
+ model_key = gr.Dropdown(
362
+ choices=list(MODEL_CHOICES.keys()),
363
+ value=DEFAULT_MODEL_KEY,
364
+ label="Modelo NLI",
365
+ info="Velocidad (MiniLM) = más rápido | Precisión (DeBERTa) = mejor calidad"
366
+ )
367
+
368
  texto = gr.Textbox(
369
  label="Caso (STAR o texto libre)",
370
  lines=16,
 
389
  """,
390
  )
391
 
392
+ # Salida
393
  with gr.Column(scale=7):
394
  gr.Markdown("<div class='card'><b>Resultados</b></div>")
395
  status = gr.Markdown(value="**Estado**: —", elem_id="status_md")
 
 
396
  score_badge = gr.Markdown(value="<span class='badge'>Score global: —</span>")
 
 
397
  plot = gr.Plot(label="Gráfica de evaluación (0–4)")
 
 
398
  table = gr.Dataframe(
399
  headers=DEFAULT_COLS,
400
  datatype=["str", "str", "number", "str", "str", "str"],
401
  interactive=False,
402
  label="Detalle por indicador"
403
  )
 
 
404
  with gr.Row():
405
  download_btn = gr.Button("Descargar CSV")
406
  csv_file = gr.File(label="Archivo CSV", visible=False)
407
 
408
+ # Lógica
409
+ def run_eval(t: str, mk: str):
410
+ msg, fig, tbl = evaluate(t, mk)
411
 
 
412
  status_md = "**Estado** \n" + (msg or "").replace("\n", " \n")
413
 
 
414
  badge_html = "<span class='badge'>Score global: —</span>"
415
  try:
416
  m = re.search(r"Score global \(0–4\):\s*([0-4](?:\.[0-9])?)", msg or "")
 
419
  except Exception:
420
  pass
421
 
 
422
  cols = (tbl or {}).get("columns") or DEFAULT_COLS
423
  data = (tbl or {}).get("data") or []
424
  safe_data = []
 
430
  r = r[:len(cols)]
431
  safe_data.append(r)
432
 
 
433
  if fig is None:
434
  fig, ax = plt.subplots(figsize=(6, 2))
435
  ax.axis("off")
 
437
 
438
  return status_md, badge_html, fig, gr.update(value=safe_data, headers=cols)
439
 
440
+ btn.click(fn=run_eval, inputs=[texto, model_key], outputs=[status, score_badge, plot, table])
 
 
 
 
441
 
442
+ def export_csv_handler(t: str, mk: str):
443
+ _, _, tbl = evaluate(t, mk)
 
444
  path = make_csv_from_table(tbl)
445
  return path, gr.update(visible=True)
446
 
447
+ download_btn.click(fn=export_csv_handler, inputs=[texto, model_key], outputs=[csv_file, csv_file])
 
 
 
 
448
 
449
+ # Lanzamiento
450
  if __name__ == "__main__":
451
+ demo.queue(max_size=16).launch(ssr_mode=False, show_error=True)