from __future__ import annotations from collections.abc import Mapping from dataclasses import dataclass from pathlib import Path import yaml class AgentRegistryError(ValueError): pass @dataclass(frozen=True) class AgentDefinition: agent_id: str label: str class AgentRegistry: def __init__(self, agents: list[AgentDefinition]) -> None: self.agents = agents self._by_id = {agent.agent_id: agent for agent in agents} def get(self, agent_id: str) -> AgentDefinition: try: return self._by_id[agent_id] except KeyError as exc: raise AgentRegistryError(f"unknown agent id: {agent_id}") from exc def _load_registry_data(path: str | Path) -> dict[str, object]: try: raw = yaml.safe_load(Path(path).read_text(encoding="utf-8")) except yaml.YAMLError as exc: raise AgentRegistryError("invalid agent registry YAML") from exc if raw is None: return {} if not isinstance(raw, Mapping): raise AgentRegistryError("agent registry must be a mapping with an agents list") return dict(raw) def load_agent_registry(path: str | Path) -> AgentRegistry: raw = _load_registry_data(path) entries = raw.get("agents") if not isinstance(entries, list) or not entries: raise AgentRegistryError("agents registry must contain a non-empty agents list") agents: list[AgentDefinition] = [] seen: set[str] = set() for entry in entries: if not isinstance(entry, Mapping): raise AgentRegistryError("each agent entry requires id and label") agent_id = str(entry.get("id", "")).strip() label = str(entry.get("label", "")).strip() if not agent_id or not label: raise AgentRegistryError("each agent entry requires id and label") if agent_id in seen: raise AgentRegistryError(f"duplicate agent id: {agent_id}") seen.add(agent_id) agents.append(AgentDefinition(agent_id=agent_id, label=label)) return AgentRegistry(agents)