mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-10-20 05:15:31 +00:00
fix(auxiliary): reach the main agent model when a sibling aux model fails on the same provider
Widen okalentiev's failed_model narrowing (#59561) to _try_main_agent_model_fallback. The safety-net layer still skipped on a provider-label match alone, so single-provider users whose aux compression model and main model share one custom endpoint had ZERO fallbacks: the aux model timing out exhausted the chain in one hop and compression aborted. Real incident (0.19.0 debug dump): aux zai-org/glm-5.2 hung 324s and timed out while main mindai/macaron-v1-venti on the SAME endpoint was serving 448K-token turns — the label-only skip discarded the one viable summarizer, the session wedged over threshold, and the anti-thrash breaker tripped. Same convention as the chain fix: model-specific failures (timeout, connection, rate limit) pass failed_model so only the exact failed model is skipped; provider-wide failures (auth 401 / payment 402) pass None and keep the whole-provider skip. Both sync and async call_llm sites pass it. Sabotage-verified: the new regression test fails on the provider-only skip.
This commit is contained in:
parent
83f20e07e0
commit
0a2c245cd6
3 changed files with 70 additions and 6 deletions
|
|
@ -3167,6 +3167,47 @@ class TestTryMainAgentModelFallback:
|
|||
client, model, label = _try_main_agent_model_fallback("glm", task="vision")
|
||||
assert client is None
|
||||
|
||||
def test_same_provider_different_model_falls_back_when_failed_model_given(self):
|
||||
"""Self-hosted shape: aux model and main model share one custom
|
||||
provider label. A timeout on the aux model must still reach the main
|
||||
model — same provider, DIFFERENT model (real incident: aux glm-5.2
|
||||
timed out while main macaron-v1-venti on the same endpoint was
|
||||
healthy; the provider-label skip discarded the working fallback)."""
|
||||
from agent.auxiliary_client import _try_main_agent_model_fallback
|
||||
fake_client = MagicMock()
|
||||
with patch("agent.auxiliary_client._read_main_provider", return_value="custom"), \
|
||||
patch("agent.auxiliary_client._read_main_model", return_value="mindai/macaron-v1-venti"), \
|
||||
patch("agent.auxiliary_client._is_provider_unhealthy", return_value=False), \
|
||||
patch("agent.auxiliary_client.resolve_provider_client",
|
||||
return_value=(fake_client, "mindai/macaron-v1-venti")):
|
||||
client, model, label = _try_main_agent_model_fallback(
|
||||
"custom", task="compression", failed_model="zai-org/glm-5.2")
|
||||
assert client is fake_client
|
||||
assert model == "mindai/macaron-v1-venti"
|
||||
assert label == "main-agent(custom)"
|
||||
|
||||
def test_same_provider_same_model_still_skips_with_failed_model(self):
|
||||
"""When the model that failed IS the main model, there is nothing to
|
||||
fall back to — the narrowed skip must not regress into a self-retry."""
|
||||
from agent.auxiliary_client import _try_main_agent_model_fallback
|
||||
with patch("agent.auxiliary_client._read_main_provider", return_value="custom"), \
|
||||
patch("agent.auxiliary_client._read_main_model", return_value="mindai/macaron-v1-venti"):
|
||||
client, model, label = _try_main_agent_model_fallback(
|
||||
"custom", task="compression",
|
||||
failed_model="MindAI/Macaron-V1-Venti") # case-insensitive match
|
||||
assert client is None and model is None and label == ""
|
||||
|
||||
def test_same_provider_no_failed_model_keeps_provider_wide_skip(self):
|
||||
"""Legacy / provider-wide callers (auth 401, payment 402) pass no
|
||||
failed_model: the whole-provider skip must be preserved so broken
|
||||
shared credentials don't trigger a doomed main-model attempt."""
|
||||
from agent.auxiliary_client import _try_main_agent_model_fallback
|
||||
with patch("agent.auxiliary_client._read_main_provider", return_value="custom"), \
|
||||
patch("agent.auxiliary_client._read_main_model", return_value="mindai/macaron-v1-venti"):
|
||||
client, model, label = _try_main_agent_model_fallback(
|
||||
"custom", task="compression")
|
||||
assert client is None and model is None and label == ""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Gate: _resolve_api_key_provider must skip anthropic when not configured
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue