Spaces:
Sleeping
Sleeping
Claude
feat(sprint-E.2): 10 modules measurements/ migrés vers evaluation/metrics/
4eb91d0 unverified | """Câblage runner de la recherchabilité (Sprint 86). | |
| Sprint 86 — A.II.5a (vue HTML + câblage runner). | |
| Le module ``picarones/core/searchability.py`` (Sprint 84) a livré | |
| la couche de calcul. Ce helper prépare la donnée pour le runner | |
| historique et l'agrégation par moteur. | |
| Adaptive masking | |
| ---------------- | |
| Comme pour les modules philologiques (Sprint 61), on ne calcule | |
| le rappel que si la GT contient au moins un token — pas de | |
| calcul vide qui produirait du bruit dans le rapport. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| from typing import Iterable, Optional | |
| from picarones.evaluation.metrics.searchability import ( | |
| _split_words, | |
| compute_searchability, | |
| ) | |
| logger = logging.getLogger(__name__) | |
| def compute_searchability_metrics( | |
| reference: Optional[str], | |
| hypothesis: Optional[str], | |
| *, | |
| max_distance: int = 2, | |
| ) -> Optional[dict]: | |
| """Recherchabilité d'un document (adaptive). | |
| Retourne ``None`` si la GT est vide ou ne contient aucun | |
| token — ce qui déclenche l'adaptive masking côté HTML. | |
| """ | |
| if not reference or not _split_words(reference): | |
| return None | |
| return compute_searchability( | |
| reference, hypothesis or "", max_distance=max_distance, | |
| ) | |
| def aggregate_searchability_metrics( | |
| per_doc: Iterable[Optional[dict]], | |
| ) -> Optional[dict]: | |
| """Agrège les métriques par-doc en un score corpus-wide. | |
| Convention : on somme les ``n_gt_tokens`` et ``n_searchable`` | |
| et on recalcule un rappel **micro** (cohérent avec ECE/MCE | |
| Sprint 39 et NER Sprint 38). | |
| """ | |
| docs = [d for d in per_doc if d] | |
| if not docs: | |
| return None | |
| n_gt = sum(int(d.get("n_gt_tokens") or 0) for d in docs) | |
| n_search = sum(int(d.get("n_searchable") or 0) for d in docs) | |
| if n_gt == 0: | |
| return None | |
| # On garde l'union des missed_tokens (capped pour ne pas | |
| # exploser le JSON sur de gros corpus) | |
| missed: list[str] = [] | |
| for d in docs: | |
| missed.extend(d.get("missed_tokens") or []) | |
| return { | |
| "n_docs": len(docs), | |
| "n_gt_tokens": n_gt, | |
| "n_searchable": n_search, | |
| "recall": n_search / n_gt, | |
| "missed_tokens_sample": missed[:50], | |
| "max_distance": docs[0].get("max_distance", 2), | |
| } | |
| __all__ = [ | |
| "compute_searchability_metrics", | |
| "aggregate_searchability_metrics", | |
| ] | |