fix(cli): same-provider /model on a session-only custom endpoint stays on it (#74143)
A bare `custom`/`local` session whose base_url is not the trusted config `model.base_url` re-resolved credentials from config on a same-provider switch and fell through to the OpenRouter default: the next turn hit openrouter.ai with an empty (or the custom) key. Keep the session endpoint and key when resolution comes back empty or on OpenRouter; a config-backed custom URL still wins so key/endpoint rotation is not pinned to a stale session. Diagnosed by fangliquanflq in #74143 / #71693; this is the minimal form of that fix at the same-provider credential step.
This commit is contained in:
@@ -18,7 +18,7 @@ from hermes_cli.providers import (
|
||||
from hermes_cli.model_normalize import normalize_model_for_provider
|
||||
from agent.models_dev import (
|
||||
ModelCapabilities, ModelInfo, get_model_capabilities, get_model_info, list_provider_models)
|
||||
from utils import base_url_hostname, base_url_origin
|
||||
from utils import base_url_host_matches, base_url_hostname, base_url_origin
|
||||
# Re-exported: callers/tests patch hermes_cli.model_switch.<name>.
|
||||
from hermes_cli.model_switch_providers import list_authenticated_providers
|
||||
|
||||
@@ -1309,6 +1309,17 @@ def _creds_for_current_provider(st: _Switch) -> None:
|
||||
st.resolve_runtime(requested=st.current_provider)
|
||||
except Exception:
|
||||
pass
|
||||
# Bare ``custom``/``local`` sessions whose base_url is session-only (not a trusted config
|
||||
# ``model.base_url``) re-resolve to the OpenRouter DEFAULT — a host the user never picked
|
||||
# (#74143). Keep the session endpoint + key then; a config-backed custom URL still wins so
|
||||
# key/endpoint rotation is not pinned to a stale session.
|
||||
if (
|
||||
st.current_provider in {"custom", "local"} and st.current_base_url
|
||||
and (not st.base_url or base_url_host_matches(st.base_url, "openrouter.ai"))
|
||||
and not base_url_host_matches(st.current_base_url, "openrouter.ai")
|
||||
):
|
||||
st.base_url, st.api_key = st.current_base_url, st.current_api_key
|
||||
st.api_mode = determine_api_mode(st.current_provider, st.base_url)
|
||||
|
||||
|
||||
def _resolve_switch_credentials(st: _Switch) -> Optional[ModelSwitchResult]:
|
||||
|
||||
@@ -2220,3 +2220,23 @@ def test_legacy_sentinel_catalog_still_resolves_and_migrates(tmp_path, monkeypat
|
||||
assert list(saved["models"]) == _LOCAL_CATALOG
|
||||
assert "__discovered_model_catalog__" not in saved["models"]
|
||||
assert "__explicit_model_allowlist__" not in saved["models"]
|
||||
|
||||
|
||||
def test_same_provider_switch_on_session_only_custom_endpoint_keeps_endpoint(monkeypatch):
|
||||
"""#74143: a same-provider ``/model`` on a bare ``custom`` session whose base_url is NOT the
|
||||
trusted config ``model.base_url`` must stay on that endpoint with its key — re-resolving from
|
||||
config fell through to the OpenRouter default and moved the next turn to a host the user never
|
||||
picked."""
|
||||
for var in ("OPENROUTER_API_KEY", "OPENROUTER_BASE_URL", "CUSTOM_BASE_URL", "CUSTOM_API_KEY", "OPENAI_API_KEY"):
|
||||
monkeypatch.delenv(var, raising=False)
|
||||
monkeypatch.setattr("hermes_cli.model_switch.load_config", lambda: {"model": {"provider": "openrouter", "default": "x"}}, raising=False)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.models.probe_api_models",
|
||||
lambda api_key, base_url, **kw: {"models": ["m-a", "m-b"], "url": base_url + "/models", "base_url": base_url,
|
||||
"suggested_base_url": None, "used_fallback": False})
|
||||
|
||||
result = switch_model("m-b", "custom", "m-a", "http://10.0.0.5:8000/v1", "session-secret")
|
||||
|
||||
assert result.success
|
||||
assert result.base_url == "http://10.0.0.5:8000/v1"
|
||||
assert result.api_key == "session-secret"
|
||||
|
||||
Reference in New Issue
Block a user