fix: failed probe credentials cannot fall through to configured auth

This commit is contained in:
Teknium
2026-09-07 03:18:08 -07:00
parent 7d44fe9c74
commit bbbccd3935
4 changed files with 38 additions and 7 deletions

View File

@@ -99,14 +99,13 @@ def _clean_str(raw: Any) -> str:
return str(raw or "").strip()
def _runtime_main(key: str) -> str:
"""Stripped context-local main-runtime value, or "" when unavailable."""
def _runtime_main(key: str) -> Any:
"""Context-local credential source or stripped runtime text; "" when unavailable."""
try:
from agent.auxiliary_client import _runtime_main_value
from agent.command_token_source import materialize_probe_api_key
value = _runtime_main_value(key)
return materialize_probe_api_key(value) if key == "api_key" else _clean_str(value)
return value if key == "api_key" else _clean_str(value)
except Exception:
return ""
@@ -178,6 +177,12 @@ def _resolve_inference_value(
→ ``custom_providers[].<key>``, ``<name>`` covering the provider and
``model.provider`` in both bare and ``custom:``-prefixed forms."""
runtime = _runtime_main(key)
# A declared source owns authentication even when its mint fails.
if key == "api_key":
from agent.command_token_source import materialize_probe_api_key
if callable(runtime):
return materialize_probe_api_key(runtime)
runtime = materialize_probe_api_key(runtime)
if runtime and runtime_ok(runtime):
return runtime
if not isinstance(cfg, dict):

View File

@@ -158,8 +158,9 @@ def _get_ollama_native_headers(base_url: Optional[str], *, api_key: Optional[str
if not configured_matches and not explicit_key:
return {}
headers = _get_ollama_request_headers() if configured_matches else {}
if explicit_key:
if explicit_key or callable(api_key):
_drop_authorization(headers)
if explicit_key:
headers["Authorization"] = f"Bearer {explicit_key}"
return headers

View File

@@ -30,9 +30,31 @@ def test_capability_paths_share_concrete_bearer(credential, expected):
auxiliary_client.clear_runtime_main()
def test_failed_callable_never_becomes_a_bearer():
def test_failed_callable_never_becomes_a_bearer(monkeypatch):
def failed():
raise RuntimeError("secret-bearing command failure")
for value in (failed, lambda: object(), object(), None):
assert model_metadata._auth_headers(value) == {}
assert "Authorization" not in models_local._lmstudio_request_headers(value)
from hermes_cli import models
url = "http://localhost:11434/v1"
configured = {"base_url": url, "api_key": "provider-fallback", "extra_headers": {
"aUtHoRiZaTiOn": "Bearer configured-fallback", "X-Probe-Fixture": "preserved",
}}
monkeypatch.setattr(models, "_get_provider_config_dict", lambda _: configured)
for value in (failed, lambda: object(), lambda: ""):
auxiliary_client.set_runtime_main("custom", "fixture", api_key=value)
try:
for cfg in ({"model": {"api_key": "model-fallback"}},
{"providers": {"custom": {"api_key": "provider-fallback"}}}):
assert image_routing._resolve_inference_api_key(cfg, "custom") == ""
assert models_local._get_ollama_native_headers(url, api_key=value) == {
"X-Probe-Fixture": "preserved",
}
assert auxiliary_client._runtime_main_value("api_key") is value
finally:
auxiliary_client.clear_runtime_main()
# An absent explicit credential still permits configured authentication.
assert models_local._get_ollama_native_headers(url)["aUtHoRiZaTiOn"] == "Bearer configured-fallback"
assert image_routing._resolve_inference_api_key({"model": {"api_key": "model-fallback"}}, "custom") == "model-fallback"

View File

@@ -1327,7 +1327,10 @@ Vision, thinking, and native local-model capability probes materialize the same
callable credential used by chat before building authentication headers. They
reuse the command token cache without replacing the chat client's callable.
If a command cannot mint a string token, these best-effort probes send no bearer
rather than an object representation; chat retains its normal error handling.
rather than an object representation or a lower-priority configured credential.
Native local-model probes remove inherited Authorization on a failed explicit
callable while retaining unrelated configured headers. Chat retains its normal
error handling.
Enterprise gateways often issue short-lived bearer tokens (SSO/OIDC brokers, cloud IAM, internal auth proxies) rather than static API keys, so a token copied into `.env` goes stale mid-session and requests start returning 401. `key_cmd` names a command that *prints* a token; Hermes runs it and caches the result until shortly before expiry, so long sessions keep working with no restart: