mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-09 08:21:50 +00:00
fix(auth): auto-detect OpenRouter credential from the pool, not just env (#42263)
resolve_provider() auto-detection only checked OPENROUTER_API_KEY/ OPENAI_API_KEY env vars, never the credential pool. A key added via `hermes auth add openrouter` (manual pool entry, no env var) was invisible: the provider failed to resolve or resolved with an empty api_key, so requests went out with no Authorization header and OpenRouter returned "HTTP 401: Missing Authentication header" while `hermes auth list` showed the credential. Closes #42130. - auth.py: check load_pool("openrouter").has_credentials() after the env check - dump.py: `debug share` shows 'openrouter set (auth pool)' instead of the misleading 'not set' when the key lives in the pool - add regression tests (pool credential auto-detects; empty pool still raises)
This commit is contained in:
parent
de80d28f38
commit
9c9d9113a8
3 changed files with 102 additions and 0 deletions
|
|
@ -1561,6 +1561,21 @@ def resolve_provider(
|
|||
if has_usable_secret(os.getenv("OPENAI_API_KEY")) or has_usable_secret(os.getenv("OPENROUTER_API_KEY")):
|
||||
return "openrouter"
|
||||
|
||||
# Auto-detect an OpenRouter credential added via `hermes auth add openrouter`
|
||||
# (manual pool entry, no env var). Without this, a key that only lives in
|
||||
# the credential pool is invisible to auto-detection — the user sees
|
||||
# `hermes auth list` showing the credential while requests go out with no
|
||||
# Authorization header ("HTTP 401: Missing Authentication header"). The
|
||||
# env-var check above only covers keys exported as OPENROUTER_API_KEY /
|
||||
# OPENAI_API_KEY. See issue #42130.
|
||||
try:
|
||||
from agent.credential_pool import load_pool as _load_pool
|
||||
|
||||
if _load_pool("openrouter").has_credentials():
|
||||
return "openrouter"
|
||||
except Exception as e:
|
||||
logger.debug("Could not check OpenRouter credential pool: %s", e)
|
||||
|
||||
# Auto-detect API-key providers by checking their env vars
|
||||
for pid, pconfig in PROVIDER_REGISTRY.items():
|
||||
if pconfig.auth_type != "api_key":
|
||||
|
|
|
|||
|
|
@ -318,6 +318,17 @@ def run_dump(args):
|
|||
display = _redact(val)
|
||||
else:
|
||||
display = "set" if val else "not set"
|
||||
# A credential added via `hermes auth add openrouter` lives in the
|
||||
# credential pool, not as an env var — surface it so the dump doesn't
|
||||
# misleadingly read "not set" while `hermes auth list` shows it (#42130).
|
||||
if not val and label == "openrouter":
|
||||
try:
|
||||
from agent.credential_pool import load_pool as _load_pool
|
||||
|
||||
if _load_pool("openrouter").has_credentials():
|
||||
display = "set (auth pool)"
|
||||
except Exception:
|
||||
pass
|
||||
lines.append(f" {label:<20} {display}")
|
||||
|
||||
# Features summary
|
||||
|
|
|
|||
76
tests/hermes_cli/test_resolve_provider_openrouter_pool.py
Normal file
76
tests/hermes_cli/test_resolve_provider_openrouter_pool.py
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
"""Regression tests for issue #42130.
|
||||
|
||||
A credential added via `hermes auth add openrouter` lives in the credential
|
||||
pool, NOT as an OPENROUTER_API_KEY env var. Before the fix, resolve_provider()
|
||||
auto-detection only checked env vars, so such a credential was invisible:
|
||||
the provider failed to resolve (AuthError) or resolved without a key, and
|
||||
requests went out with no Authorization header — OpenRouter's
|
||||
"HTTP 401: Missing Authentication header".
|
||||
|
||||
These tests lock in that auto-detection consults the OpenRouter pool.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _clean_inference_env(monkeypatch):
|
||||
"""Strip credential-shaped env vars so the pool is the only source."""
|
||||
for key in (
|
||||
"OPENROUTER_API_KEY",
|
||||
"OPENAI_API_KEY",
|
||||
"ANTHROPIC_API_KEY",
|
||||
"ANTHROPIC_TOKEN",
|
||||
"CLAUDE_CODE_OAUTH_TOKEN",
|
||||
"NOUS_API_KEY",
|
||||
"HERMES_INFERENCE_PROVIDER",
|
||||
):
|
||||
monkeypatch.delenv(key, raising=False)
|
||||
|
||||
|
||||
def _seed_openrouter_pool(token: str = "sk-or-FAKEKEY123") -> None:
|
||||
"""Mimic `hermes auth add openrouter <token>` — a manual pool entry."""
|
||||
from agent.credential_pool import (
|
||||
AUTH_TYPE_API_KEY,
|
||||
SOURCE_MANUAL,
|
||||
PooledCredential,
|
||||
load_pool,
|
||||
)
|
||||
|
||||
pool = load_pool("openrouter")
|
||||
pool.add_entry(
|
||||
PooledCredential(
|
||||
provider="openrouter",
|
||||
id=uuid.uuid4().hex[:6],
|
||||
label="api-key-1",
|
||||
auth_type=AUTH_TYPE_API_KEY,
|
||||
priority=0,
|
||||
source=SOURCE_MANUAL,
|
||||
access_token=token,
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_auto_detects_openrouter_from_pool(tmp_path, monkeypatch):
|
||||
"""With only a pool credential (no env var), auto-detection finds it."""
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes"))
|
||||
(tmp_path / "hermes").mkdir(parents=True, exist_ok=True)
|
||||
_seed_openrouter_pool()
|
||||
|
||||
from hermes_cli.auth import resolve_provider
|
||||
|
||||
assert resolve_provider("auto") == "openrouter"
|
||||
|
||||
|
||||
def test_no_credentials_still_raises(tmp_path, monkeypatch):
|
||||
"""Empty pool + no env var must still fail to resolve — no false positive."""
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes"))
|
||||
(tmp_path / "hermes").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
from hermes_cli.auth import AuthError, resolve_provider
|
||||
|
||||
with pytest.raises(AuthError):
|
||||
resolve_provider("auto")
|
||||
Loading…
Add table
Add a link
Reference in a new issue