#!/usr/bin/env python3 """Resolve stable Switchyard selectors through the stewarded model catalog.""" from __future__ import annotations import json import os from pathlib import Path from typing import Any CATALOG_PATH = Path( os.environ.get("HERMES_ROUTING_CATALOG_PATH", "/routing-catalog/catalog.json") ) DEFAULTS = { "codex": { "low": "gpt-5.6-luna", "medium": "gpt-5.6-terra", "high": "gpt-5.6-sol", "xhigh": "gpt-5.6-sol", }, "claude": { "low": "claude-haiku-4-5-20251001", "medium": "claude-sonnet-5", "high": "claude-opus-5", "xhigh": "claude-opus-5", }, } PREFIXES = {"codex": "gpt-", "claude": "claude-"} CAPABILITY_ROLES = frozenset({"economy", "balanced", "advanced", "frontier"}) TIER_DEFAULTS = { "codex": { "luna": "gpt-5.6-luna", "terra": "gpt-5.6-terra", "sol": "gpt-5.6-sol", }, "claude": { "haiku": "claude-haiku-4-5-20251001", "fable": "claude-fable-5", "sonnet": "claude-sonnet-5", "opus": "claude-opus-5", }, } def load_catalog(path: Path = CATALOG_PATH) -> dict[str, Any]: """Load a valid routing catalog, falling back to an empty mapping.""" try: value = json.loads(path.read_text(encoding="utf-8")) except (OSError, ValueError, json.JSONDecodeError): return {} return value if isinstance(value, dict) else {} def catalog_contains(provider: str, model: str, catalog: dict[str, Any] | None = None) -> bool: """Return whether a provider's stewarded current catalog advertises a model.""" document = catalog if catalog is not None else load_catalog() providers = document.get("providers", {}) if isinstance(document, dict) else {} record = providers.get(provider, {}) if isinstance(providers, dict) else {} models = record.get("models", []) if isinstance(record, dict) else [] return isinstance(model, str) and model in models def resolve_model( provider: str, selector: str, effort: str, catalog: dict[str, Any] | None = None, ) -> str: """Resolve an automatic or capability-tier selector to an exact model ID.""" if provider not in DEFAULTS or effort not in DEFAULTS[provider]: raise ValueError("unsupported provider or effort") prefix = PREFIXES[provider] document = catalog if catalog is not None else load_catalog() if selector.startswith(prefix) or catalog_contains(provider, selector, document): if catalog_contains(provider, selector, document): return selector providers = document.get("providers", {}) if isinstance(document, dict) else {} record = providers.get(provider, {}) if isinstance(providers, dict) else {} if isinstance(record, dict) and record.get("live") is True: raise ValueError(f"model is not in the current {provider} catalog") return selector providers = document.get("providers", {}) if isinstance(document, dict) else {} record = providers.get(provider, {}) if isinstance(providers, dict) else {} if not isinstance(record, dict): record = {} capability = selector.removeprefix("auto-") if selector.startswith("auto-") else "" if capability and capability not in CAPABILITY_ROLES: raise ValueError(f"unsupported capability selector {selector!r}") if capability: mappings = record.get("capability_resolved", {}) mapping = mappings.get(capability, {}) if isinstance(mappings, dict) else {} candidate = mapping.get(effort, "") if isinstance(mapping, dict) else "" else: mapping_name = "resolved" if selector == "auto" else "tiers" mapping = record.get(mapping_name, {}) candidate = mapping.get(effort if selector == "auto" else selector, "") if isinstance(mapping, dict) else "" if ( selector != "auto" and not capability and isinstance(candidate, str) and candidate and selector not in candidate.lower() ): candidate = "" advertised = record.get("models", []) if isinstance(record, dict) else [] candidate_allowed = isinstance(candidate, str) and candidate and ( candidate in advertised or (not advertised and candidate.startswith(prefix)) ) if not candidate_allowed: # A fresh account list is authoritative. Do not turn a deliberately # unresolved/retired selector into a stale baseline model. if record.get("live") is True or capability: raise ValueError(f"no current {provider} model for selector {selector!r}") candidate = ( DEFAULTS[provider][effort] if selector == "auto" else TIER_DEFAULTS[provider].get(selector, DEFAULTS[provider][effort]) ) return candidate def resolve_route(route: str, catalog: dict[str, Any] | None = None) -> str: """Resolve a route/// target to a model ID.""" parts = route.split("/") if len(parts) != 4 or parts[0] != "route": return route return resolve_model(parts[1], parts[2], parts[3], catalog) def resolve_worker_route(route: str, catalog: dict[str, Any] | None = None) -> str: """Resolve a worker selector while preserving the worker route envelope.""" parts = route.split("/") if len(parts) != 4 or parts[0] != "worker": raise ValueError("unsupported worker route") model = resolve_model(parts[1], parts[2], parts[3], catalog) return f"worker/{parts[1]}/{model}/{parts[3]}"