Source code for personalitylinmult.inference.registry
"""Resolve a model *name* to its checkpoint and load a predictor.
Deployment surfaces (a Gradio dropdown, a CLI ``--model`` flag) select a model by
a short name — ``"AVT"`` or ``"A"`` — rather than a checkpoint path. This module
holds that name-to-checkpoint mapping and hands back a ready
:class:`~personalitylinmult.inference.predictor.FiPredictor`, caching each so a
repeated selection does not reload from disk.
"""
from __future__ import annotations
import logging
from dataclasses import dataclass
from pathlib import Path
from personalitylinmult.inference.predictor import FiPredictor
logger = logging.getLogger(__name__)
"""Module-level logger."""
[docs]
@dataclass(frozen=True)
class ModelSpec:
"""One registered model.
Args:
name (str): Short selector, e.g. ``"AVT"`` or ``"A"``.
checkpoint (Path): Path to its ``.ckpt``.
description (str): One-line human summary shown in a UI.
"""
name: str
checkpoint: Path
description: str = ""
[docs]
class ModelRegistry:
"""A name-keyed set of models, loaded on demand and cached.
Args:
specs (list[ModelSpec]): The registered models.
Raises:
ValueError: If two specs share a name.
"""
def __init__(self, specs: list[ModelSpec]):
names = [s.name for s in specs]
dupes = {n for n in names if names.count(n) > 1}
if dupes:
raise ValueError(f"Duplicate model names in registry: {sorted(dupes)}.")
self._specs = {s.name: s for s in specs}
self._cache: dict[str, FiPredictor] = {}
@property
def names(self) -> list[str]:
"""The registered model names, in insertion order."""
return list(self._specs)
[docs]
def spec(self, name: str) -> ModelSpec:
"""Return the spec for a name.
Args:
name (str): A registered model name.
Returns:
ModelSpec: Its spec.
Raises:
KeyError: If the name is not registered.
"""
if name not in self._specs:
raise KeyError(f"No model named {name!r}. Registered: {self.names}.")
return self._specs[name]
[docs]
def load(self, name: str) -> FiPredictor:
"""Load (or return the cached) predictor for a name.
Args:
name (str): A registered model name.
Returns:
FiPredictor: The predictor.
Raises:
KeyError: If the name is not registered.
FileNotFoundError: If its checkpoint is missing.
"""
if name not in self._cache:
spec = self.spec(name)
self._cache[name] = FiPredictor.from_checkpoint(spec.checkpoint)
return self._cache[name]
[docs]
@classmethod
def from_checkpoints(cls, checkpoints: dict[str, Path | str]) -> ModelRegistry:
"""Build a registry from a ``{name: checkpoint_path}`` mapping.
Args:
checkpoints (dict[str, Path | str]): Name-to-path mapping.
Returns:
ModelRegistry: The registry (paths are not opened until
:meth:`load`, so a missing file surfaces at load time, not here).
"""
specs = [ModelSpec(name=name, checkpoint=Path(path)) for name, path in checkpoints.items()]
return cls(specs)