The gateway kept its own get_fallback_chain walk in _try_resolve_fallback_provider, a near copy of the shared resolution-time helper. _resolve_runtime_agent_kwargs now calls resolve_runtime_with_fallback(cfg) and the private loop is deleted; the rate-limited (429) vs auth-failed log distinction (is_rate_limited_auth_error) moves into the helper so every caller keeps it. Also: drop the test-only resolve= seam from the helper (tests monkeypatch hermes_cli.runtime_provider.resolve_runtime_provider instead), and log non-AuthError fallback-entry failures at warning so a mistyped fallback provider is not silently skipped at debug level. Tests: test_fallback_chain_runtime_uses_the_entry_model now drives gateway.run._resolve_runtime_agent_kwargs (red when the helper's chain walk is sabotaged); two gateway stubs accept the helper's keyword arguments.
50 lines
2.2 KiB
Python
50 lines
2.2 KiB
Python
"""Gateway provider-override credential resolution keys off the override's MODEL (#112600).
|
|
|
|
Channel overrides, persisted ``/model`` switches and API-server provider refreshes all resolve
|
|
credentials through ``gateway.run._resolve_runtime_agent_kwargs_for_provider``; without the model
|
|
the ladder keys off config's ``default`` and a ``*-free`` default decides the api_mode/base_url
|
|
for a Go-only model ("Model ... is not supported")."""
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture()
|
|
def _zen_free_default_home(monkeypatch, tmp_path):
|
|
home = tmp_path / "hermes"
|
|
home.mkdir()
|
|
(home / "config.yaml").write_text(
|
|
"model:\n default: mimo-v2.5-free\n provider: opencode\n base_url: https://opencode.ai/zen/v1\n")
|
|
monkeypatch.setenv("HERMES_HOME", str(home))
|
|
monkeypatch.setenv("OPENCODE_GO_API_KEY", "sk-test-go")
|
|
|
|
|
|
def test_provider_override_runtime_uses_the_override_model(_zen_free_default_home):
|
|
from gateway.run import _resolve_runtime_agent_kwargs_for_provider
|
|
|
|
runtime = _resolve_runtime_agent_kwargs_for_provider("opencode-go", target_model="mimo-v2.5")
|
|
assert runtime["provider"] == "opencode-go"
|
|
assert runtime["base_url"] == "https://opencode.ai/zen/go/v1"
|
|
|
|
|
|
def test_fallback_chain_runtime_uses_the_entry_model(_zen_free_default_home, monkeypatch):
|
|
"""The gateway's AuthError fallback goes through the shared ``resolve_runtime_with_fallback``
|
|
walker (no gateway-private loop) and keeps the entry's own model."""
|
|
import gateway.run as gateway_run
|
|
import hermes_cli.runtime_provider as rp
|
|
from hermes_cli.auth import AuthError
|
|
|
|
real_resolve = rp.resolve_runtime_provider
|
|
|
|
def primary_auth_fails(**kw):
|
|
if kw.get("requested") is None: # the primary, resolved from config.yaml
|
|
raise AuthError("primary quota exhausted (429)")
|
|
return real_resolve(**kw)
|
|
|
|
monkeypatch.setattr(rp, "resolve_runtime_provider", primary_auth_fails)
|
|
monkeypatch.setattr(gateway_run, "_load_gateway_config",
|
|
lambda: {"fallback_model": [{"provider": "opencode-go", "model": "mimo-v2.5"}]})
|
|
fb = gateway_run._resolve_runtime_agent_kwargs()
|
|
assert fb["model"] == "mimo-v2.5"
|
|
assert fb["provider"] == "opencode-go"
|
|
assert fb["base_url"] == "https://opencode.ai/zen/go/v1"
|