fix(runtime): read the OpenRouter fallback keys from .env after an exhausted pool entry
This commit is contained in:
@@ -157,18 +157,23 @@ def _resolve_openrouter_runtime(
|
||||
)
|
||||
)
|
||||
if is_openrouter_context:
|
||||
# Same read order as the credential pool and _resolve_api_key_provider_secret
|
||||
# (.env, then scope-aware os.environ, raw op:// references yielding to the resolved
|
||||
# scoped value). get_secret_str sees os.environ only, so a key living solely in
|
||||
# ~/.hermes/.env was lost once the pool entry went exhausted/benched (#117667).
|
||||
# OPENAI_API_KEY is a legacy home for an OpenRouter key. When OPENAI_BASE_URL binds it, it
|
||||
# goes only to that origin: another scheme or port on the same host is another endpoint.
|
||||
# Unbound, openrouter.ai gets it only when it is OpenRouter-shaped (sk-or-), so a real OpenAI
|
||||
# key never reaches a third party.
|
||||
openai_key = get_secret_str("OPENAI_API_KEY")
|
||||
from hermes_cli.config import get_env_value_prefer_dotenv
|
||||
openai_key = get_env_value_prefer_dotenv("OPENAI_API_KEY") or ""
|
||||
openai_base_url = get_secret_str("OPENAI_BASE_URL", "").strip()
|
||||
if base_url_hostname(openai_base_url):
|
||||
openai_origin = base_url_origin(openai_base_url) # empty on a bad port: bound, matches nothing
|
||||
openai_key_ok = bool(openai_origin[1]) and openai_origin == base_url_origin(base_url)
|
||||
else:
|
||||
openai_key_ok = not is_openrouter_url or rp.looks_like_openrouter_key(openai_key)
|
||||
candidates = [explicit_api_key, get_secret_str("OPENROUTER_API_KEY"),
|
||||
candidates = [explicit_api_key, get_env_value_prefer_dotenv("OPENROUTER_API_KEY"),
|
||||
openai_key if openai_key_ok else ""]
|
||||
else:
|
||||
# ``model.api_key`` and ``model.key_env`` back a trusted config base_url only; the key_env
|
||||
|
||||
@@ -726,6 +726,34 @@ def test_openai_key_bound_to_another_host_never_reaches_openrouter(monkeypatch,
|
||||
assert resolved["api_key"] == expected_key
|
||||
|
||||
|
||||
def test_openrouter_env_file_key_survives_exhausted_pool(monkeypatch):
|
||||
"""A key living only in ~/.hermes/.env must survive an exhausted pool entry.
|
||||
|
||||
Regression test for #117667: with the pool's OPENROUTER_API_KEY entry benched
|
||||
(select() -> None), the terminal resolver read os.environ only, so the documented
|
||||
.env location was skipped and `hermes chat --provider openrouter` failed with
|
||||
"No API key found" until the key was exported in the shell.
|
||||
"""
|
||||
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "openrouter")
|
||||
monkeypatch.setattr(rp, "_get_model_config", lambda: {})
|
||||
monkeypatch.setattr(
|
||||
rp, "load_pool",
|
||||
lambda _provider: SimpleNamespace(has_credentials=lambda: True, select=lambda **_kw: None),
|
||||
)
|
||||
monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
|
||||
monkeypatch.delenv("OPENROUTER_BASE_URL", raising=False)
|
||||
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
||||
or_env_name = "OPENROUTER_" + "API_KEY"
|
||||
monkeypatch.delenv(or_env_name, raising=False)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.config.load_env", lambda: {or_env_name: "dotenv-or-key"}
|
||||
)
|
||||
|
||||
resolved = rp.resolve_runtime_provider(requested="openrouter")
|
||||
|
||||
assert resolved["api_key"] == "dotenv-or-key"
|
||||
|
||||
|
||||
def test_custom_endpoint_uses_saved_config_base_url_when_env_missing(monkeypatch):
|
||||
"""Persisted custom endpoints in config.yaml must still resolve when
|
||||
OPENAI_BASE_URL is absent from the current environment.
|
||||
|
||||
Reference in New Issue
Block a user