"""Provider-neutral fallback contracts for Hermes automatic routing.""" from __future__ import annotations import tomllib from testing.tests.test_hermes_chat_support import HERMES, _documents def _config() -> dict: manifest = _documents(HERMES / "switchyard-configmap.yaml")[0] return tomllib.loads(manifest["data"]["routes.toml"]) def _provider(target_id: str) -> str | None: parts = target_id.split("/") if len(parts) >= 3 and parts[0] in {"route", "worker"}: return parts[1] return None def test_every_classifier_selects_from_both_hosted_providers(): """No automatic classifier may narrow its candidate set to one provider.""" config = _config() targets = config["targets"] classifiers = { name: route for name, route in config["routes"].items() if route["type"] == "llm_classifier" } assert classifiers for route in classifiers.values(): providers = { _provider(targets[name]["id"]) for name in route["targets"] } assert {"codex", "claude"} <= providers def test_classifier_failure_uses_an_even_cross_provider_pool(): """The required Switchyard default must name a neutral pool, not a provider.""" config = _config() targets = config["targets"] routes = config["routes"] routes_by_id = {route["id"]: route for route in routes.values()} for route in routes.values(): if route["type"] != "llm_classifier": continue fallback_target_name = route["default_target"] assert fallback_target_name in route["targets"] assert fallback_target_name.startswith("neutral_") fallback_target = targets[fallback_target_name] assert fallback_target["llm_client"] == "neutral_pool" pool = routes_by_id[fallback_target["id"]] assert pool["type"] == "random" assert pool["weights"] == [1.0, 1.0] pool_providers = [ _provider(targets[name]["id"]) for name in pool["targets"] ] assert sorted(pool_providers) == ["claude", "codex"] neutral_client = config["llm_clients"]["neutral_pool"] assert neutral_client == { "format": "openai_chat", "base_url": "http://127.0.0.1:9005/v1", "max_retries": 0, }