erica92 commited on
Commit
55738d2
·
verified ·
1 Parent(s): 9256eec

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +83 -49
app.py CHANGED
@@ -1,24 +1,9 @@
1
  import os
 
2
  import gradio as gr
3
  from huggingface_hub import InferenceClient
4
  from difflib import SequenceMatcher
5
 
6
- # =======================
7
- # METRICHE (evaluate)
8
- # =======================
9
-
10
- try:
11
- import evaluate
12
- ROUGE = evaluate.load("rouge")
13
- BLEU = evaluate.load("bleu")
14
- CHRF = evaluate.load("chrf")
15
- METRICS_AVAILABLE = True
16
- print("✅ Metriche ROUGE / BLEU / chrF caricate correttamente.")
17
- except Exception as e:
18
- print("⚠️ Impossibile caricare le metriche da evaluate:", repr(e))
19
- ROUGE = BLEU = CHRF = None
20
- METRICS_AVAILABLE = False
21
-
22
  # =======================
23
  # MODELLI DISPONIBILI
24
  # =======================
@@ -190,45 +175,99 @@ def get_system_prompt(mostra_ragionamento: bool):
190
  )
191
 
192
  # =======================
193
- # FUNZIONE ADMIN: METRICHE DI QUALITÀ (LOG ONLY)
194
  # =======================
195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  def log_quality_metrics(question: str, voci_glossario, answer_text: str, modello_scelto: str):
197
  """
198
- Calcola ROUGE / BLEU / chrF confrontando la risposta del modello
199
- con le definizioni del glossario (come riferimento).
200
- I risultati vengono SOLO stampati nei log del server (non mostrati all'utente).
201
  """
202
- if not METRICS_AVAILABLE:
203
- print("⚠️ Metriche non disponibili (evaluate non caricato). Salto la valutazione.")
204
- return
205
-
206
  if not voci_glossario:
207
  print("ℹ️ Nessuna voce di glossario per questa domanda. Salto il calcolo delle metriche.")
208
  return
209
 
210
- # Riferimento: concatenazione delle definizioni delle voci selezionate
211
  reference = " ".join(e["definition"] for e in voci_glossario)
212
  prediction = answer_text.strip()
213
 
214
- try:
215
- rouge_res = ROUGE.compute(predictions=[prediction], references=[reference])
216
- bleu_res = BLEU.compute(predictions=[prediction], references=[reference])
217
- chrf_res = CHRF.compute(predictions=[prediction], references=[reference])
218
- except Exception as e:
219
- print("⚠️ Errore nel calcolo delle metriche:", repr(e))
220
- return
221
 
222
- print("\n====== VALUTAZIONE QUALITÀ RISPOSTA ======")
223
  print(f"Modello: {modello_scelto}")
224
  print(f"Domanda: {question}")
225
  print(f"Glossario usato per riferimento: {[e['term'] for e in voci_glossario]}")
226
- print("--- Metriche ---")
227
- print(f"ROUGE-1 F1: {rouge_res.get('rouge1', 0.0):.4f}")
228
- print(f"ROUGE-L F1: {rouge_res.get('rougeL', 0.0):.4f}")
229
- print(f"BLEU: {bleu_res.get('bleu', 0.0):.44f}")
230
- print(f"chrF++: {chrf_res.get('chrf', 0.0):.4f}")
231
- print("=========================================\n")
232
 
233
  # =======================
234
  # FUNZIONE DI RISPOSTA (STREAMING + SELF-REFLECTION IMPLICITA)
@@ -241,7 +280,7 @@ def answer_with_self_reflection(question: str, modello_scelto: str, mostra_ragio
241
  - usa il glossario come fonte principale
242
  - chiede al modello di correggersi mentalmente prima di rispondere
243
 
244
- Alla fine, in background, calcola anche le metriche di qualità rispetto al glossario
245
  e le scrive nei log (solo per amministratore).
246
  """
247
  question = question.strip()
@@ -249,19 +288,17 @@ def answer_with_self_reflection(question: str, modello_scelto: str, mostra_ragio
249
  yield "Scrivi un termine o concetto di informatica che vuoi capire meglio 😊"
250
  return
251
 
252
- # Ricava il vero MODEL_ID a partire dall'etichetta scelta nel menu
253
  model_id = AVAILABLE_MODELS.get(modello_scelto)
254
  if model_id is None:
255
  yield "Si è verificato un errore: modello non valido."
256
  return
257
 
258
- # Client specifico per il modello scelto
259
  client = InferenceClient(model=model_id, token=HF_TOKEN)
260
 
261
  # 🔍 MINI-RAG: trova voci rilevanti nel glossario TOON
262
  voci = trova_voci_rilevanti(question, k=3)
263
 
264
- # Genera il prompt di sistema appropriato
265
  base_prompt = get_system_prompt(mostra_ragionamento)
266
 
267
  if voci:
@@ -298,11 +335,10 @@ def answer_with_self_reflection(question: str, modello_scelto: str, mostra_ragio
298
  ]
299
 
300
  if mostra_ragionamento:
301
- max_tokens = 800 # serve più spazio per "Come ci arrivo" + spiegazione
302
  else:
303
- max_tokens = 400 # per sola spiegazione bastano meno token
304
 
305
- # Badge con numero di voci trovate
306
  if n_voci > 0:
307
  prefix = f"🔎 Ho trovato **{n_voci}** voci nel glossario rilevanti per la tua domanda.\n\n"
308
  else:
@@ -329,14 +365,12 @@ def answer_with_self_reflection(question: str, modello_scelto: str, mostra_ragio
329
  yield f"Si è verificato un errore: {e}"
330
  return
331
 
332
- # A questo punto `partial` contiene la risposta completa mostrata all'utente
333
- # Per le metriche togliamo il prefisso "🔎 ..." se presente
334
  if n_voci > 0:
335
  answer_for_metrics = partial[len(prefix):]
336
  else:
337
  answer_for_metrics = partial
338
 
339
- # 🔐 Valutazione "nascosta": solo log admin
340
  log_quality_metrics(question, voci, answer_for_metrics, modello_scelto)
341
 
342
  # =======================
 
1
  import os
2
+ import re
3
  import gradio as gr
4
  from huggingface_hub import InferenceClient
5
  from difflib import SequenceMatcher
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  # =======================
8
  # MODELLI DISPONIBILI
9
  # =======================
 
175
  )
176
 
177
  # =======================
178
+ # METRICHE CUSTOM (approx ROUGE / BLEU / chrF)
179
  # =======================
180
 
181
+ def _tokenize(text: str):
182
+ # tokenizzazione molto semplice: parole alfanumeriche
183
+ return [t for t in re.findall(r"\w+", text.lower()) if t]
184
+
185
+ def _char_list(text: str):
186
+ return [c for c in text.lower() if not c.isspace()]
187
+
188
+ def rouge1_f1(pred: str, ref: str) -> float:
189
+ pred_tokens = _tokenize(pred)
190
+ ref_tokens = _tokenize(ref)
191
+ if not pred_tokens or not ref_tokens:
192
+ return 0.0
193
+ pred_set = set(pred_tokens)
194
+ ref_set = set(ref_tokens)
195
+ overlap = len(pred_set & ref_set)
196
+ if overlap == 0:
197
+ return 0.0
198
+ precision = overlap / len(pred_set)
199
+ recall = overlap / len(ref_set)
200
+ if precision + recall == 0:
201
+ return 0.0
202
+ return 2 * precision * recall / (precision + recall)
203
+
204
+ def bleu1(pred: str, ref: str) -> float:
205
+ pred_tokens = _tokenize(pred)
206
+ ref_tokens = _tokenize(ref)
207
+ if not pred_tokens or not ref_tokens:
208
+ return 0.0
209
+ overlap = 0
210
+ ref_counts = {}
211
+ for t in ref_tokens:
212
+ ref_counts[t] = ref_counts.get(t, 0) + 1
213
+ for t in pred_tokens:
214
+ if ref_counts.get(t, 0) > 0:
215
+ overlap += 1
216
+ ref_counts[t] -= 1
217
+ precision = overlap / len(pred_tokens)
218
+ # brevity penalty
219
+ from math import exp
220
+ len_p = len(pred_tokens)
221
+ len_r = len(ref_tokens)
222
+ if len_p == 0:
223
+ return 0.0
224
+ if len_p > len_r:
225
+ bp = 1.0
226
+ else:
227
+ bp = exp(1 - len_r / len_p)
228
+ return bp * precision
229
+
230
+ def chrf_simple(pred: str, ref: str) -> float:
231
+ pred_chars = _char_list(pred)
232
+ ref_chars = _char_list(ref)
233
+ if not pred_chars or not ref_chars:
234
+ return 0.0
235
+ pred_set = set(pred_chars)
236
+ ref_set = set(ref_chars)
237
+ overlap = len(pred_set & ref_set)
238
+ if overlap == 0:
239
+ return 0.0
240
+ precision = overlap / len(pred_set)
241
+ recall = overlap / len(ref_set)
242
+ if precision + recall == 0:
243
+ return 0.0
244
+ return 2 * precision * recall / (precision + recall)
245
+
246
  def log_quality_metrics(question: str, voci_glossario, answer_text: str, modello_scelto: str):
247
  """
248
+ Calcola metriche naive (ROUGE1-F1, BLEU1, chrF-like) confrontando la risposta del modello
249
+ con le definizioni del glossario. Solo log su console (admin).
 
250
  """
 
 
 
 
251
  if not voci_glossario:
252
  print("ℹ️ Nessuna voce di glossario per questa domanda. Salto il calcolo delle metriche.")
253
  return
254
 
 
255
  reference = " ".join(e["definition"] for e in voci_glossario)
256
  prediction = answer_text.strip()
257
 
258
+ r1 = rouge1_f1(prediction, reference)
259
+ b1 = bleu1(prediction, reference)
260
+ cf = chrf_simple(prediction, reference)
 
 
 
 
261
 
262
+ print("\n====== VALUTAZIONE QUALITÀ RISPOSTA (NAIVE METRICS) ======")
263
  print(f"Modello: {modello_scelto}")
264
  print(f"Domanda: {question}")
265
  print(f"Glossario usato per riferimento: {[e['term'] for e in voci_glossario]}")
266
+ print("--- Metriche approx ---")
267
+ print(f"ROUGE-1 F1 (uno-grammi): {r1:.4f}")
268
+ print(f"BLEU-1 (uno-grammi): {b1:.4f}")
269
+ print(f"chrF semplice (char F1): {cf:.4f}")
270
+ print("=========================================================\n")
 
271
 
272
  # =======================
273
  # FUNZIONE DI RISPOSTA (STREAMING + SELF-REFLECTION IMPLICITA)
 
280
  - usa il glossario come fonte principale
281
  - chiede al modello di correggersi mentalmente prima di rispondere
282
 
283
+ Alla fine, in background, calcola anche metriche di qualità rispetto al glossario
284
  e le scrive nei log (solo per amministratore).
285
  """
286
  question = question.strip()
 
288
  yield "Scrivi un termine o concetto di informatica che vuoi capire meglio 😊"
289
  return
290
 
 
291
  model_id = AVAILABLE_MODELS.get(modello_scelto)
292
  if model_id is None:
293
  yield "Si è verificato un errore: modello non valido."
294
  return
295
 
 
296
  client = InferenceClient(model=model_id, token=HF_TOKEN)
297
 
298
  # 🔍 MINI-RAG: trova voci rilevanti nel glossario TOON
299
  voci = trova_voci_rilevanti(question, k=3)
300
 
301
+ # Prompt di sistema
302
  base_prompt = get_system_prompt(mostra_ragionamento)
303
 
304
  if voci:
 
335
  ]
336
 
337
  if mostra_ragionamento:
338
+ max_tokens = 800
339
  else:
340
+ max_tokens = 400
341
 
 
342
  if n_voci > 0:
343
  prefix = f"🔎 Ho trovato **{n_voci}** voci nel glossario rilevanti per la tua domanda.\n\n"
344
  else:
 
365
  yield f"Si è verificato un errore: {e}"
366
  return
367
 
368
+ # Risposta completa (senza prefisso) per le metriche admin
 
369
  if n_voci > 0:
370
  answer_for_metrics = partial[len(prefix):]
371
  else:
372
  answer_for_metrics = partial
373
 
 
374
  log_quality_metrics(question, voci, answer_for_metrics, modello_scelto)
375
 
376
  # =======================