Skip to content

fix(auxiliary): resolve API key from base_url pattern for custom endpoints - #18378

Closed
kevin-lucifer wants to merge 1 commit into
NousResearch:mainfrom
kevin-lucifer:fix/auxiliary-base-url-api-key-resolution
Closed

fix(auxiliary): resolve API key from base_url pattern for custom endpoints#18378
kevin-lucifer wants to merge 1 commit into
NousResearch:mainfrom
kevin-lucifer:fix/auxiliary-base-url-api-key-resolution

Conversation

@kevin-lucifer

Copy link
Copy Markdown

Problem

When auxiliary tasks configure a custom base_url without an explicit api_key (e.g. auxiliary.vision.base_url with api_key: null), the resolve_provider_client custom branch would only fall back to OPENAI_API_KEY, causing 401 errors for providers like Alibaba/DashScope, DeepSeek, MiniMax, Moonshot, Zhipu, SiliconFlow, ModelScope, etc.

Example Config That Would Fail

auxiliary:
  vision:
    provider: alibaba
    base_url: https://coding.dashscope.aliyuncs.com/v1
    api_key: null  # Expected to be resolved from DASHSCOPE_API_KEY

Before this fix, resolve_provider_client(provider="custom", explicit_base_url="https://coding.dashscope.aliyuncs.com/v1", explicit_api_key=None) would:

  1. Try explicit_api_key → None
  2. Try OPENAI_API_KEY → not set or wrong key
  3. Fall back to "no-key-required" → 401 error

Solution

This PR adds:

  1. _BASE_URL_TO_PROVIDER: Static mapping of base_url hostname patterns to (provider_name, env_var_name):

    • dashscope.aliyuncs.com → alibaba / DASHSCOPE_API_KEY
    • coding.dashscope.aliyuncs.com → alibaba / DASHSCOPE_API_KEY
    • api.moonshot.cn → moonshot / MOONSHOT_API_KEY
    • api.deepseek.com → deepseek / DEEPSEEK_API_KEY
    • api.minimax.chat → minimax / MINIMAX_API_KEY
    • api.zhipuai.cn → zhipu / ZHIPU_API_KEY
    • api.siliconflow.cn → siliconflow / SILICONFLOW_API_KEY
    • api-inference.modelscope.cn → modelscope / MODELSCOPE_API_KEY
  2. _get_api_key_for_base_url(): Resolution chain that looks up credentials based on base_url pattern:

    • Credential pool (via _select_pool_entry)
    • Environment variable (provider-specific)
    • Direct .env file read (bypasses stale/masked gateway env)
  3. Integration in resolve_provider_client(): The custom branch now calls _get_api_key_for_base_url(custom_base) before falling back to OPENAI_API_KEY.

Resolution Order for Custom Endpoints

  1. Explicit api_key parameter (highest priority)
  2. Credential pool lookup based on base_url pattern match
  3. Provider-specific environment variable (e.g. DASHSCOPE_API_KEY)
  4. Direct .env file read (handles masked *** values in gateway processes)
  5. OPENAI_API_KEY fallback
  6. "no-key-required" for local servers

Handles Masked Placeholder Values

Gateway processes may have *** masked placeholder values in their environment. The fix skips these and falls back to reading the actual value from .env file directly.

Tests

Added comprehensive tests in tests/agent/test_auxiliary_base_url_api_key.py:

  • TestBaseUrlToProviderMapping: Validates all provider mappings
  • TestReadEnvVarFromFile: Tests .env file reading with masked value handling
  • TestGetApiKeyForBaseUrl: Tests the full resolution chain
  • TestResolveProviderClientCustomBranch: Integration tests
  • TestIntegrationWithAuxiliaryConfig: Simulates the bug scenario

All 29 tests pass.

Files Changed

  • agent/auxiliary_client.py: +115 lines (mapping + helper function + integration)
  • tests/agent/test_auxiliary_base_url_api_key.py: +296 lines (new test file)

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels May 1, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Addresses #9318 (auxiliary client falls back to no-key-required when custom base_url set). Also related to #5392 (custom provider not resolved in fallback path).

…oints

When auxiliary tasks configure a custom base_url without an explicit
api_key (e.g. auxiliary.vision.base_url with api_key: null), the
resolve_provider_client custom branch would only fall back to
OPENAI_API_KEY, causing 401 errors for providers like Alibaba/DashScope,
DeepSeek, MiniMax, etc.

This fix adds:
- _BASE_URL_TO_PROVIDER: mapping of base_url patterns to provider credentials
- _get_api_key_for_base_url(): lookup chain (credential pool → env var → .env file)
- Integration in resolve_provider_client() custom branch

Resolution order for custom endpoints:
1. Explicit api_key parameter (highest priority)
2. Credential pool lookup based on base_url pattern match
3. Environment variable (provider-specific, e.g. DASHSCOPE_API_KEY)
4. Direct .env file read (bypasses stale/masked gateway env)
5. OPENAI_API_KEY fallback
6. "no-key-required" for local servers

Also handles masked placeholder values ('***') that gateway processes
may have in their environment, by reading directly from .env file.

Fixes scenario where config.yaml has:
  auxiliary:
    vision:
      provider: alibaba
      base_url: https://coding.dashscope.aliyuncs.com/v1
      api_key: null

The vision task will now correctly resolve DASHSCOPE_API_KEY from
credential pool or environment.
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the detailed reproduction and credential-resolution coverage. This is an automated hermes-sweeper review; current main already provides the requested behavior through the centralized provider resolver.

  • agent/auxiliary_client.py:5969 retains a configured first-class provider when an auxiliary task has a base_url but no api_key, so the PR's provider: alibaba configuration is not reduced to a generic custom endpoint.
  • agent/auxiliary_client.py:4876 resolves that provider through the shared API-key credential resolver while :4898 keeps the task's explicit endpoint. hermes_cli/auth.py:320-326 registers Alibaba's DASHSCOPE_API_KEY source.
  • The related [Bug]: Auxiliary client falls back to "no-key-required" when per-task custom base_url is set but api_key is empty #9318 follow-up was shipped in 8e09afda270cc9f1a582b32bf5cb736cfc69675e (v2026.7.7); current agent/auxiliary_client.py:4623-4629 additionally handles same-host custom endpoint key inheritance without leaking credentials cross-host.

Closing as implemented on main.

@teknium1 teknium1 closed this Jul 12, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:implemented-on-main Sweeper: behavior already present on current main type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants