fix: failed probe credentials cannot fall through to configured auth
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user