Skip to content

fix(runtime): resolve bare custom provider to loopback or CUSTOM_BASE_URL (salvage #14719)#15103

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-37dfb080
Apr 24, 2026
Merged

fix(runtime): resolve bare custom provider to loopback or CUSTOM_BASE_URL (salvage #14719)#15103
teknium1 merged 2 commits into
mainfrom
hermes/hermes-37dfb080

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Partial salvage of #14719 by @georgex8001 onto current main. Only the runtime_provider.py commit is picked; the ACP/approval contextvar refactor in the same PR is unrelated scope — worth a separate review.

Closes #14676.

What this PR does

Two related bare-custom resolution fixes:

  1. Consult CUSTOM_BASE_URL env var in runtime resolution (not just model-list display). Before, setting only CUSTOM_BASE_URL with bare provider: custom fell through to OpenRouter.
  2. Protect against stale YAML state hijacking a Custom session. When /model switches to Custom while model.provider in YAML still reflects a prior cloud provider (OpenRouter, Z.AI, etc.), model.base_url is only trusted if it's a loopback host (localhost, 127.0.0.1, ::1, 0.0.0.0) OR the YAML provider is already custom.

How

  • New _loopback_hostname() + _config_base_url_trustworthy_for_bare_custom() helpers in hermes_cli/runtime_provider.py
  • _resolve_openrouter_runtime now reads CUSTOM_BASE_URL ahead of the OpenRouter default URLs
  • use_config_base_url gate for bare custom runs through the loopback-trust check

Cherry-pick scope

PR #14719 bundled two unrelated fixes: (a) this runtime fix, (b) an ACP approval thread-safety refactor via contextvars. Only (a) is salvaged here; (b) lives in commit d9ef7a72 on the original PR — it can be evaluated separately.

Changes

Validation

Scenario Before After
Loopback YAML + stale provider: openrouter, select custom hijacked → openrouter.ai trusted → localhost:8082
Non-loopback YAML + stale provider: openrouter, select custom trusted, hijacked session falls back to OpenRouter (correct)
CUSTOM_BASE_URL=http://localhost:9090/v1, no model.base_url ignored → openrouter.ai honored → localhost:9090
Explicit provider: custom + loopback base_url worked still works (regression guard)
  • tests/hermes_cli/test_runtime_provider_resolution.py — 74/74 pass (3 new regression tests)
  • E2E: all 4 scenarios above verified with real module imports

Co-authored-by: @georgex8001

georgex8001 and others added 2 commits April 24, 2026 04:51
…_URL

When /model selects Custom but model.provider in YAML still reflects a prior provider, trust model.base_url only for loopback hosts or when provider is custom. Consult CUSTOM_BASE_URL before OpenRouter defaults (#14676).
@teknium1 teknium1 merged commit 3cb43df into main Apr 24, 2026
11 of 12 checks passed
@teknium1 teknium1 deleted the hermes/hermes-37dfb080 branch April 24, 2026 11:54
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard area/config Config system, migrations, profiles labels Apr 24, 2026
gtopolice added a commit to gtopolice/clawdeez-hermes-template that referenced this pull request May 14, 2026
…eam boot

`provider: custom` configured purely via env vars (OPENAI_BASE_URL +
OPENAI_API_KEY) is unreliable on Hermes' Docker image: the upstream entrypoint
copies a default `.env.example` onto fresh volumes, and the dotenv loader masks
the process-level OPENAI_API_KEY when resolving the custom-provider call.
Symptom on Morpheus BYOK deployments: request reaches api.mor.org but with no
auth header → 401 "Missing Authentication header" surfaced through Hermes as
agent_http_502.

Fix: materialize /opt/data/config.yaml ourselves from HERMES_INFERENCE_PROVIDER
+ HERMES_MODEL (+ OPENAI_BASE_URL/OPENAI_API_KEY for `custom`) before exec'ing
the upstream entrypoint. config.yaml is documented as the "single source of
truth" for model/provider/api_key, so this works on every Hermes version and
sidesteps both NousResearch/hermes-agent#15103 and the dotenv masking.

OpenRouter rows still rely on OPENROUTER_API_KEY env var (documented as
process-level by Hermes); only the `model.default` + `provider` go into yaml.

Co-authored-by: Cursor <cursoragent@cursor.com>
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 type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: bare 'custom' provider falls through to OpenRouter — no base_url resolution

3 participants