mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-05 12:42:30 +00:00
fix(fallback): attach credential pool after provider switch
When automatic fallback activates a provider that differs from the primary, try_activate_fallback() cleared the primary's pool (to avoid cross-provider base_url contamination, #33163) but never loaded the fallback provider's own pool. The fallback then ran with no pool, so rate_limit/billing/auth recovery couldn't rotate its credentials. After clearing a mismatched pool, load_pool(fb_provider) and attach it when it has credentials, so provider-specific rotation continues to work on the fallback target.
This commit is contained in:
parent
635841d210
commit
3fe16e3cd5
2 changed files with 76 additions and 13 deletions
|
|
@ -11,8 +11,8 @@ _swap_credential continue operating on the PRIMARY's credential pool during
|
|||
fallback calls, contaminating primary state with fallback-provider errors.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from unittest.mock import MagicMock
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
||||
|
||||
|
|
@ -81,10 +81,8 @@ class TestFallbackCredentialIsolation:
|
|||
|
||||
def test_fallback_clears_primary_pool(self):
|
||||
"""When switching from openai-codex to openrouter, the codex pool is cleared."""
|
||||
# Import the real method
|
||||
sys.path.insert(0, "/mnt/g/knowledge/project/hermes-agent")
|
||||
# We test the isolation logic directly, not the full _try_activate_fallback
|
||||
# which has many dependencies. Instead we verify the pool-clearing guard.
|
||||
# We test the isolation logic directly here as a minimal guard; the
|
||||
# integration-style test below calls the real fallback activator.
|
||||
|
||||
agent = _make_agent(provider="openai-codex", base_url="https://chatgpt.com/backend-api/codex")
|
||||
agent._fallback_activated = True
|
||||
|
|
@ -122,6 +120,53 @@ class TestFallbackCredentialIsolation:
|
|||
"Pool should be preserved when fallback provider matches pool provider"
|
||||
)
|
||||
|
||||
def test_fallback_attaches_matching_pool_after_clear(self):
|
||||
"""Provider-switch fallback should attach the fallback provider's pool."""
|
||||
from agent.chat_completion_helpers import try_activate_fallback
|
||||
|
||||
agent = _make_agent(
|
||||
provider="ollama-cloud",
|
||||
model="glm-5.2",
|
||||
base_url="https://ollama.com/v1",
|
||||
api_mode="chat_completions",
|
||||
)
|
||||
agent._fallback_chain = [{"provider": "openai-codex", "model": "gpt-5.5"}]
|
||||
agent._credential_pool = _make_pool("ollama-cloud")
|
||||
agent._buffer_status = MagicMock()
|
||||
agent._is_azure_openai_url.return_value = False
|
||||
agent._is_direct_openai_url.return_value = False
|
||||
agent._provider_model_requires_responses_api.return_value = False
|
||||
agent._anthropic_prompt_cache_policy.return_value = (False, False)
|
||||
agent._ensure_lmstudio_runtime_loaded = MagicMock()
|
||||
agent._replace_primary_openai_client = MagicMock()
|
||||
agent.context_compressor = None
|
||||
|
||||
fallback_client = SimpleNamespace(
|
||||
api_key="codex-key",
|
||||
base_url="https://chatgpt.com/backend-api/codex",
|
||||
_custom_headers={},
|
||||
)
|
||||
fallback_pool = _make_pool("openai-codex")
|
||||
|
||||
with patch(
|
||||
"agent.auxiliary_client.resolve_provider_client",
|
||||
return_value=(fallback_client, "gpt-5.5"),
|
||||
) as resolve_provider_client, patch(
|
||||
"agent.credential_pool.load_pool",
|
||||
return_value=fallback_pool,
|
||||
) as load_pool:
|
||||
assert try_activate_fallback(agent) is True
|
||||
|
||||
resolve_provider_client.assert_called_once()
|
||||
load_pool.assert_called_once_with("openai-codex")
|
||||
assert agent.provider == "openai-codex"
|
||||
assert agent.model == "gpt-5.5"
|
||||
assert agent.base_url == "https://chatgpt.com/backend-api/codex"
|
||||
assert agent.api_mode == "codex_responses"
|
||||
assert agent._credential_pool is fallback_pool
|
||||
assert agent._credential_pool.provider == "openai-codex"
|
||||
assert agent._transport_cache == {}
|
||||
|
||||
|
||||
# ── Test: _recover_with_credential_pool rejects mismatched pool ──────
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue