atlas-iac/services/hermes/scripts/routing_catalog.py

139 lines
5.4 KiB
Python

#!/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/<provider>/<selector>/<effort> 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]}"