From e033bbaa3c8cf5ffd89ca677f092433f08939477 Mon Sep 17 00:00:00 2001 From: fangliquanflq Date: Sun, 6 Sep 2026 21:05:28 +0800 Subject: [PATCH] fix(agent): quarantine bare custom aliases by endpoint --- agent/auxiliary_client.py | 24 +++++++------- tests/agent/test_auxiliary_client.py | 48 ++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/agent/auxiliary_client.py b/agent/auxiliary_client.py index 45cab3c9e7..4687801cf5 100644 --- a/agent/auxiliary_client.py +++ b/agent/auxiliary_client.py @@ -2903,8 +2903,8 @@ def _normalize_chain_label(provider: str) -> str: def _unhealthy_cache_key(provider: str, base_url: Optional[str] = None) -> Any: """Provider-wide key, or endpoint-specific key for an explicit custom endpoint.""" label = _normalize_chain_label(provider) - endpoint = str(base_url or "").strip().lower().rstrip("/") - if endpoint and (label == "local/custom" or label.startswith("custom:")): + endpoint = _custom_health_base_url(provider, base_url).lower().rstrip("/") + if endpoint: return "custom-endpoint", endpoint return label @@ -2912,18 +2912,18 @@ def _unhealthy_cache_key(provider: str, base_url: Optional[str] = None) -> Any: def _custom_health_base_url(provider: str, explicit_base_url: Optional[str] = None) -> str: """Return the concrete custom endpoint used to scope health and failed-route checks.""" explicit = str(explicit_base_url or "").strip() - if explicit: - return explicit label = _normalize_chain_label(provider) - if label.startswith("custom:"): - with contextlib.suppress(ImportError): - from hermes_cli.runtime_provider import _get_named_custom_provider - entry = _get_named_custom_provider(provider) - if entry: - return str(entry.get("base_url") or "").strip() - return "" if label == "local/custom": - return _current_custom_base_url() + return explicit or _current_custom_base_url() + if label.startswith("custom:") and explicit: + return explicit + with contextlib.suppress(ImportError): + from hermes_cli.runtime_provider import _get_named_custom_provider, _resolves_to_custom + if _resolves_to_custom(label): + return explicit or _current_custom_base_url() + entry = _get_named_custom_provider(provider) + if entry: + return explicit or str(entry.get("base_url") or "").strip() return "" diff --git a/tests/agent/test_auxiliary_client.py b/tests/agent/test_auxiliary_client.py index 1815b67f64..972a15d196 100644 --- a/tests/agent/test_auxiliary_client.py +++ b/tests/agent/test_auxiliary_client.py @@ -4286,6 +4286,54 @@ class TestAuxUnhealthyCache: assert label == "fallback_chain[1](custom)" resolver.assert_called_once_with(local_entry) + def test_bare_named_custom_aliases_share_endpoint_quarantine(self): + """Bare registry aliases cannot retry a quarantined custom endpoint.""" + from agent.auxiliary_client import _mark_provider_unhealthy, _try_configured_fallback_chain + + hosted_url = "https://hosted.example/v1" + local_url = "http://127.0.0.1:8080/v1" + hosted_entry = {"provider": "mirror", "model": "hosted-model"} + local_entry = {"provider": "custom", "model": "local-model", "base_url": local_url} + local_client = MagicMock() + _mark_provider_unhealthy("custom:primary", base_url=hosted_url) + + with patch( + "agent.auxiliary_client._get_auxiliary_task_config", + return_value={"fallback_chain": [hosted_entry, local_entry]}, + ), patch( + "hermes_cli.runtime_provider._get_named_custom_provider", + side_effect=lambda name: ( + {"name": "mirror", "base_url": hosted_url} if name == "mirror" else None + ), + ), patch( + "agent.auxiliary_client._resolve_fallback_entry", + return_value=(local_client, "local-model"), + ) as resolver: + client, model, label = _try_configured_fallback_chain( + "compression", "openrouter", reason="payment error" + ) + + assert (client, model, label) == ( + local_client, "local-model", "fallback_chain[1](custom)", + ) + resolver.assert_called_once_with(local_entry) + + def test_bare_named_custom_main_route_honors_endpoint_quarantine(self): + """A bare named custom main route is skipped before client resolution.""" + from agent.auxiliary_client import _mark_provider_unhealthy, _try_main_provider_route + + hosted_url = "https://hosted.example/v1" + _mark_provider_unhealthy("custom:primary", base_url=hosted_url) + + with patch( + "hermes_cli.runtime_provider._get_named_custom_provider", + return_value={"name": "hosted", "base_url": hosted_url}, + ), patch("agent.auxiliary_client.resolve_provider_client") as resolver: + routed = _try_main_provider_route("hosted", "hosted-model", "", "", "") + + assert routed is None + resolver.assert_not_called() + # ── auxiliary_max_tokens_param ──────────────────────────────────────────────