Spaces:
Sleeping
Sleeping
Claude
docs(sprint-H.8): cleanup obsolete legacy/shim language in production docstrings
e407ec0 unverified | """``MetricRegistry`` β Sprint A14-S5. | |
| Container mutable qui associe chaque ``MetricSpec`` Γ son callable | |
| de calcul. **InstanciΓ© explicitement** par un service au dΓ©marrage | |
| de l'application (cf. ``picarones/app/services/registry_service.py`` | |
| au S20) β pas de singleton global, pas de side-effect d'import, | |
| pas de dΓ©corateur magique. | |
| DiffΓ©rence avec ``picarones.evaluation.metric_registry`` | |
| -------------------------------------------------------- | |
| L'autre registre utilise un dict module-level ``_METRIC_REGISTRY`` | |
| rempli par un dΓ©corateur ``@register_metric`` appliquΓ© au top-level | |
| d'autres modules. ConsΓ©quence : un ``import picarones`` charge | |
| ~50 sous-modules pour amorcer le registre. | |
| Ici, ``MetricRegistry`` est une classe instanciable : | |
| .. code-block:: python | |
| from picarones.domain import ArtifactType | |
| from picarones.domain.evaluation_spec import MetricSpec | |
| from picarones.evaluation.registry import MetricRegistry | |
| reg = MetricRegistry() | |
| reg.register( | |
| MetricSpec(name="cer", input_types=( | |
| ArtifactType.RAW_TEXT, ArtifactType.RAW_TEXT, | |
| )), | |
| compute_cer, # callable | |
| ) | |
| selected = reg.select( | |
| ArtifactType.RAW_TEXT, ArtifactType.RAW_TEXT, | |
| ) | |
| Anti-sur-ingΓ©nierie | |
| ------------------- | |
| Pas de gestion de versions de mΓ©trique, pas de namespace, pas de | |
| recherche par tag. Si un caller a besoin de ces features, il les | |
| implΓ©mentera quand le besoin sera concret (probablement S15+). | |
| """ | |
| from __future__ import annotations | |
| from typing import Any, Callable | |
| from picarones.domain.artifacts import ArtifactType | |
| from picarones.domain.errors import PicaronesError | |
| from picarones.domain.evaluation_spec import MetricSpec | |
| class MetricRegistrationError(PicaronesError): | |
| """Tentative d'enregistrement invalide d'une mΓ©trique.""" | |
| class MetricNotFoundError(PicaronesError): | |
| """La mΓ©trique demandΓ©e n'est pas enregistrΓ©e.""" | |
| class MetricRegistry: | |
| """Container mutable de ``MetricSpec`` + callables. | |
| Thread-safe en lecture après initialisation ; la séquence | |
| d'enregistrement attendue est : un seul service, au dΓ©marrage, | |
| enregistre toutes les mΓ©triques en une fois, puis l'instance | |
| est figΓ©e par convention (lecture seule depuis les services | |
| consommateurs). | |
| Pas de mΓ©canisme de freeze technique pour l'instant β si un | |
| caller modifie le registre après le bootstrap, c'est de sa | |
| responsabilitΓ©. | |
| """ | |
| def __init__(self) -> None: | |
| self._specs: dict[str, MetricSpec] = {} | |
| self._callables: dict[str, Callable[..., Any]] = {} | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Enregistrement | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def register(self, spec: MetricSpec, func: Callable[..., Any]) -> None: | |
| """Enregistre une mΓ©trique. | |
| Raises | |
| ------ | |
| MetricRegistrationError | |
| Si une mΓ©trique du mΓͺme nom est dΓ©jΓ enregistrΓ©e | |
| (sauf re-enregistrement strict du mΓͺme couple | |
| ``(spec, func)``, tolΓ©rΓ© pour les tests qui re-instancient). | |
| """ | |
| if not callable(func): | |
| raise MetricRegistrationError( | |
| f"register({spec.name!r}) : func n'est pas callable." | |
| ) | |
| if spec.name in self._specs: | |
| existing_spec = self._specs[spec.name] | |
| existing_func = self._callables[spec.name] | |
| if existing_spec == spec and existing_func is func: | |
| return # idempotent | |
| raise MetricRegistrationError( | |
| f"MΓ©trique {spec.name!r} dΓ©jΓ enregistrΓ©e avec une " | |
| "autre spec ou un autre callable." | |
| ) | |
| self._specs[spec.name] = spec | |
| self._callables[spec.name] = func | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Lecture | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def __contains__(self, name: str) -> bool: | |
| return name in self._specs | |
| def __len__(self) -> int: | |
| return len(self._specs) | |
| def names(self) -> list[str]: | |
| """Liste des noms enregistrΓ©s (ordre d'enregistrement).""" | |
| return list(self._specs.keys()) | |
| def get_spec(self, name: str) -> MetricSpec: | |
| if name not in self._specs: | |
| raise MetricNotFoundError( | |
| f"MΓ©trique {name!r} non enregistrΓ©e. " | |
| f"Disponibles : {sorted(self._specs)}." | |
| ) | |
| return self._specs[name] | |
| def get_callable(self, name: str) -> Callable[..., Any]: | |
| if name not in self._callables: | |
| raise MetricNotFoundError( | |
| f"Callable de mΓ©trique {name!r} non enregistrΓ©." | |
| ) | |
| return self._callables[name] | |
| def select( | |
| self, | |
| reference_type: ArtifactType, | |
| hypothesis_type: ArtifactType, | |
| ) -> list[MetricSpec]: | |
| """MΓ©triques applicables Γ une jonction donnΓ©e (signature exacte).""" | |
| target = (reference_type, hypothesis_type) | |
| return [s for s in self._specs.values() if s.input_types == target] | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Calcul | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def compute( | |
| self, | |
| name: str, | |
| reference: Any, | |
| hypothesis: Any, | |
| ) -> Any: | |
| """Calcule la métrique nommée sur la paire (référence, hypothèse). | |
| Aucune capture d'exception : si la métrique lève, l'exception | |
| remonte au caller (qui est typiquement un | |
| ``EvaluationViewExecutor`` qui dΓ©cide quoi en faire dans son | |
| ``ProjectionReport``). | |
| """ | |
| func = self.get_callable(name) | |
| return func(reference, hypothesis) | |
| def compute_at_junction( | |
| self, | |
| reference: Any, | |
| hypothesis: Any, | |
| reference_type: ArtifactType, | |
| hypothesis_type: ArtifactType, | |
| ) -> dict[str, Any]: | |
| """Calcule **toutes** les mΓ©triques applicables Γ la jonction. | |
| Retourne ``{metric_name: value}``. Une métrique qui lève | |
| est absente du dict (warning loggΓ© au niveau caller via | |
| l'EvaluationViewExecutor β ici on remonte l'exception pour | |
| que les tests dΓ©tectent les bugs). | |
| """ | |
| results: dict[str, Any] = {} | |
| for spec in self.select(reference_type, hypothesis_type): | |
| results[spec.name] = self.compute(spec.name, reference, hypothesis) | |
| return results | |
| __all__ = [ | |
| "MetricRegistry", | |
| "MetricRegistrationError", | |
| "MetricNotFoundError", | |
| ] | |