mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
fix(agent): keep primary runtime restore on matching credential pool (#56374)
This commit is contained in:
parent
fb403a3a73
commit
820a052575
2 changed files with 61 additions and 1 deletions
|
|
@ -1184,11 +1184,23 @@ def restore_primary_runtime(agent) -> bool:
|
|||
if pool is not None and pool.has_available():
|
||||
entry = pool.select()
|
||||
if entry is not None:
|
||||
entry_provider = str(getattr(entry, "provider", "") or "").strip().lower()
|
||||
primary_provider = str(rt.get("provider") or "").strip().lower()
|
||||
entry_matches_primary = entry_provider == primary_provider
|
||||
if primary_provider == "custom" and entry_provider.startswith("custom:"):
|
||||
primary_base_url = str(rt.get("base_url") or "").strip().rstrip("/").lower()
|
||||
entry_base_url = str(
|
||||
getattr(entry, "runtime_base_url", None)
|
||||
or getattr(entry, "base_url", None)
|
||||
or ""
|
||||
).strip().rstrip("/").lower()
|
||||
entry_matches_primary = bool(primary_base_url and entry_base_url == primary_base_url)
|
||||
|
||||
entry_key = (
|
||||
getattr(entry, "runtime_api_key", None)
|
||||
or getattr(entry, "access_token", "")
|
||||
)
|
||||
if entry_key:
|
||||
if entry_key and entry_matches_primary:
|
||||
# ``_swap_credential`` rebuilds the OpenAI/Anthropic client,
|
||||
# reapplies base-url-scoped headers, and carries the
|
||||
# accumulated base_url / OAuth-detection fixes (#33163).
|
||||
|
|
@ -1198,6 +1210,14 @@ def restore_primary_runtime(agent) -> bool:
|
|||
getattr(entry, "id", "?"),
|
||||
getattr(entry, "label", "?"),
|
||||
)
|
||||
elif entry_key:
|
||||
logger.info(
|
||||
"Restore skipped pool entry %s (%s): provider %s does not match primary provider %s",
|
||||
getattr(entry, "id", "?"),
|
||||
getattr(entry, "label", "?"),
|
||||
entry_provider or "?",
|
||||
primary_provider or "?",
|
||||
)
|
||||
|
||||
# ── Reset fallback chain for the new turn ──
|
||||
agent._fallback_activated = False
|
||||
|
|
|
|||
|
|
@ -201,6 +201,46 @@ class TestRestorePrimaryRuntime:
|
|||
|
||||
assert agent._use_prompt_caching == original_caching
|
||||
|
||||
def test_restore_skips_cross_provider_pool_entry(self):
|
||||
"""Restore must not swap in a fallback provider credential for the primary runtime."""
|
||||
|
||||
class _Entry:
|
||||
provider = "openrouter"
|
||||
id = "fallback-entry"
|
||||
label = "fallback"
|
||||
runtime_api_key = "fallback-key"
|
||||
runtime_base_url = "https://openrouter.ai/api/v1"
|
||||
access_token = "fallback-key"
|
||||
|
||||
class _Pool:
|
||||
provider = "openrouter"
|
||||
|
||||
def has_available(self):
|
||||
return True
|
||||
|
||||
def select(self):
|
||||
return _Entry()
|
||||
|
||||
agent = _make_agent(
|
||||
provider="custom",
|
||||
base_url="https://primary.example.com/v1",
|
||||
fallback_model={"provider": "openrouter", "model": "anthropic/claude-sonnet-4"},
|
||||
)
|
||||
original_base_url = agent.base_url
|
||||
mock_client = _mock_resolve()
|
||||
with patch("agent.auxiliary_client.resolve_provider_client", return_value=(mock_client, None)):
|
||||
agent._try_activate_fallback()
|
||||
agent._credential_pool = _Pool()
|
||||
agent._swap_credential = MagicMock()
|
||||
|
||||
with patch("run_agent.OpenAI", return_value=MagicMock()):
|
||||
result = agent._restore_primary_runtime()
|
||||
|
||||
assert result is True
|
||||
assert agent.provider == "custom"
|
||||
assert agent.base_url == original_base_url
|
||||
agent._swap_credential.assert_not_called()
|
||||
|
||||
def test_restore_survives_exception(self):
|
||||
"""If client rebuild fails, the method returns False gracefully."""
|
||||
agent = _make_agent()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue