from __future__ import annotations from dataclasses import dataclass from pathlib import Path from typing import Any import json try: import yaml except Exception: # pragma: no cover yaml = None @dataclass(frozen=True) class ModelEntry: model_id: str license: str usage_notes: str runtime: str = 'heuristic' backend: str | None = None local_fallback: str | None = None def _load_raw(path: Path) -> Any: text = path.read_text(encoding='utf-8') if path.suffix.lower() == '.json': return json.loads(text) if yaml is not None: return yaml.safe_load(text) return json.loads(text) def load_model_registry(path: str | Path) -> dict[str, Any]: path = Path(path) raw = _load_raw(path) if not isinstance(raw, dict): raise ValueError(f'model registry must be a mapping, got {type(raw)!r}') return raw def get_entry(registry: dict[str, Any], project_key: str, component: str) -> ModelEntry: section = registry.get(project_key, {}) if project_key == 'shared': section = registry.get('shared', {}) else: section = registry.get('projects', {}).get(project_key, {}) if component not in section: raise KeyError(f'missing registry entry for {project_key}.{component}') item = section[component] return ModelEntry( model_id=item['model_id'], license=item['license'], usage_notes=item['usage_notes'], runtime=item.get('runtime', 'heuristic'), backend=item.get('backend'), local_fallback=item.get('local_fallback'), )