Skip to content

fix(runtime_provider, model_switch): resolve custom provider key_env from .env file - #41375

Closed
franksong2702 wants to merge 1 commit into
NousResearch:mainfrom
franksong2702:fix/custom-provider-key-env-resolution
Closed

fix(runtime_provider, model_switch): resolve custom provider key_env from .env file#41375
franksong2702 wants to merge 1 commit into
NousResearch:mainfrom
franksong2702:fix/custom-provider-key-env-resolution

Conversation

@franksong2702

Copy link
Copy Markdown

Summary

Named custom providers that declare key_env in config.yaml (e.g. key_env: VOLCENGINE_CODING_PLAN_API_KEY) silently lose their credentials when the key only exists in ~/.hermes/.env.

Three code paths read key_env via os.getenv() / os.environ.get() instead of get_env_value() — which means they only see process-level env vars and miss the .env file entirely. The fallback chain then produces an empty string, and the next fallback is the inline api_key field (also empty for users who migrated to .env). The end result: the provider resolves to "no-key-required" and every /models probe returns 401.

Root Cause

get_env_value() in hermes_cli/config.py reads os.environ first, then falls back to parsing ~/.hermes/.env. Three call sites bypassed this helper:

File Function Line Old code New code
runtime_provider.py _get_named_custom_provider 531 os.getenv(key_env, "") get_env_value(key_env)
runtime_provider.py _resolve_named_custom_runtime 735 os.getenv(key_env) get_env_value(key_env)
model_switch.py switch_model (user provider path) 921 os.environ.get(_kenv, "") get_env_value(_kenv)

Impact

Any user who:

  1. Defines a named custom provider in config.yaml with key_env: SOME_VAR
  2. Stores the actual credential in ~/.hermes/.env (the documented approach)
  3. Does not pre-load that variable into the process environment

...will experience silent credential loss on that provider. The model switch still appears to succeed (because the validation override at line 1002-1041 catches models declared in the provider's models: list), but:

  • /models probes fail with 401
  • The validation warning is misleading: "could not reach the API to validate"
  • Runtime calls that skip validation (e.g. chat completions) also fail with 401

Reproduction

# config.yaml
providers:
  volcengine-coding-plan:
    name: Volcengine Coding Plan
    base_url: https://ark.cn-beijing.volces.com/api/coding/v3
    key_env: VOLCENGINE_CODING_PLAN_API_KEY
    models: [deepseek-v4-pro]

# ~/.hermes/.env
VOLCENGINE_CODING_PLAN_API_KEY=sk-xxx

# Before fix:
resolve_runtime_provider(requested="volcengine-coding-plan")
# -> api_key = "no-key-required" (15 chars, wrong)

# After fix:
resolve_runtime_provider(requested="volcengine-coding-plan")
# -> api_key = "sk-xxx" (correct, from .env)

Tests

All 207 tests in the relevant test files pass:

  • tests/hermes_cli/test_runtime_provider_resolution.py
  • tests/hermes_cli/test_model_validation.py
  • tests/hermes_cli/test_model_switch_copilot_api_mode.py

Checklist

  • Code changes tested locally
  • Existing tests pass
  • No new dependencies
  • No breaking changes — this is a strict bug fix (widens credential resolution, does not narrow it)

…from .env file

When a custom provider declares key_env in config.yaml (e.g.
VOLCENGINE_CODING_PLAN_API_KEY), three code paths only read from
os.environ instead of using get_env_value() which also checks
~/.hermes/.env. This causes named custom providers to silently
fall back to 'no-key-required' when the key only exists in .env.

Affected paths:
- _get_named_custom_provider(): line 531, os.getenv() -> get_env_value()
- _resolve_named_custom_runtime(): line 735, os.getenv() -> get_env_value()
- switch_model() user-provider path: line 921, os.environ.get() -> get_env_value()

Fix: All three now use get_env_value() from hermes_cli.config,
which reads os.environ first then falls back to ~/.hermes/.env.

This is a regression for any user who stores provider credentials
in .env (the documented approach) rather than pre-loading them
into the process environment.

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Approved

  • Consistent fix for provider key_env resolution: replaces raw os.getenv with get_env_value at 3 call sites.
  • This aligns custom-provider credential lookup with the rest of the hermes_cli code path, which is likely required for profile-aware .env resolution.
  • Small diff, focused scope, low regression risk.

Reviewed by Hermes Agent

@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard area/config Config system, migrations, profiles P2 Medium — degraded but workaround exists labels Jun 7, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused credential-resolution report and patch. This is an automated hermes-sweeper review; the requested behavior is already present on current main.

  • hermes_cli/main.py:528-535 resolves the profile before loading its .env; run_agent.py:119-127, gateway/run.py:1309-1311, and tui_gateway/server.py:26-44 cover the other runtime entry paths.
  • hermes_cli/runtime_provider.py:49-59 now centralizes credential reads through profile-scoped _getenv() / get_secret(), while named-provider resolution uses it at :669 and :1013.
  • This was incorporated by f538470cf4afddb9ae6cc476c4f71b671f5a8420 and shipped in v2026.6.19.
  • The linked duplicate discussion in [BUG] Named custom provider's api_key_env not resolved in resolve_provider_client — leads to HTTP 401 #46690 also identifies this PR as one of the original fixes for the same named-provider credential symptom.

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

Labels

area/config Config system, migrations, profiles comp/cli CLI entry point, hermes_cli/, setup wizard 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.

4 participants