Skip to content

fix(auxiliary): inherit model.api_key for custom endpoint when per-task key is empty (#9318) - #26

Open
hashbender wants to merge 1 commit into
mainfrom
mirror/pr-55911
Open

fix(auxiliary): inherit model.api_key for custom endpoint when per-task key is empty (#9318)#26
hashbender wants to merge 1 commit into
mainfrom
mirror/pr-55911

Conversation

@hashbender

Copy link
Copy Markdown
Owner

Problem

When an auxiliary task is configured with provider: custom, an explicit base_url, and an empty api_key, the resolve_provider_client() custom provider branch falls through to the hardcoded "no-key-required" placeholder without ever consulting model.api_key from config.yaml. This causes 401 auth errors for users on self-hosted gateways who share the same endpoint and credentials for both their main model and auxiliary tasks.

# ~/.hermes/config.yaml
model:
  provider: custom
  base_url: https://my-gateway.example.com/v1
  api_key: sk-real-key

auxiliary:
  session_search:
    provider: custom
    model: 'some/fast-model'
    base_url: 'https://my-gateway.example.com/v1'
    api_key: ''  # empty — should inherit from model.api_key

Root Cause

The custom_key fallback chain in resolve_provider_client() (the provider == "custom" branch with explicit_base_url) only checks:

  1. explicit_api_key → None (empty string stripped)
  2. os.getenv("OPENAI_API_KEY") → often unset or wrong key
  3. "no-key-required" ← wins, causing 401

The main model.api_key from config.yaml is never consulted, unlike the _resolve_auto() path which already reads _RUNTIME_MAIN_API_KEY.

Fix

Added _read_main_api_key() — mirrors the existing _read_main_model() / _read_main_provider() pattern:

  1. Checks _RUNTIME_MAIN_API_KEY (runtime override set by set_runtime_main())
  2. Falls back to config.yamlmodel.api_key
  3. Returns "" if neither is set

Inserted into the custom_key fallback chain before "no-key-required":

custom_key = (
    (explicit_api_key or "").strip()
    or os.getenv("OPENAI_API_KEY", "").strip()
    or _read_main_api_key()           # NEW: inherit from model.api_key
    or "no-key-required"              # local servers (Ollama, etc.)
)

This covers both the explicit provider: custom path and (transitively) the provider: auto path when auto-detection routes auxiliary tasks to the main custom endpoint.

Tests

Added TestCustomEndpointApiKeyInheritance with 4 tests:

Test Type Description
test_inherits_main_api_key_when_aux_key_empty RED→GREEN Empty aux key + config model.api_key → inherits config key
test_runtime_override_key_is_used RED→GREEN _RUNTIME_MAIN_API_KEY override takes precedence over config
test_explicit_api_key_takes_precedence Guard rail Explicit api_key param wins over config
test_local_server_falls_to_no_key_required Guard rail No key anywhere → "no-key-required" for local servers

RED phase verified: both RED→GREEN tests fail on upstream/main with AssertionError: 'no-key-required' != 'sk-...'.

Full suite: 278 passed, 0 failed in tests/agent/test_auxiliary_client.py.

Related PRs

This fix is complementary to both — it handles the case where a user intentionally uses provider: custom with a self-hosted gateway and expects credential inheritance from the main model config.

Closes NousResearch#9318.


Auto-published by Moonsong via Path B automated pipeline.


Mirror-of: NousResearch#55911
NousResearch#55911

@tenki-reviewer

tenki-reviewer Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Complete
No issues found!

Risk: 🟢 Low (18/100) — no findings · 148 LOC across 2 files


Minor refactor in auxiliary_client.py adding _read_main_api_key() helper with consistent error-handling pattern. No high-confidence issues found.

Files Reviewed (2 files)
agent/auxiliary_client.py
tests/agent/test_auxiliary_client.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Auxiliary client falls back to "no-key-required" when per-task custom base_url is set but api_key is empty

1 participant