Skip to content

fix(cli): pass target_model to resolve_runtime_provider in _ensure_runtime_credentials - #54148

Closed
joviur wants to merge 2 commits into
NousResearch:mainfrom
joviur:fix/cli-ensure-runtime-target-model
Closed

fix(cli): pass target_model to resolve_runtime_provider in _ensure_runtime_credentials#54148
joviur wants to merge 2 commits into
NousResearch:mainfrom
joviur:fix/cli-ensure-runtime-target-model

Conversation

@joviur

@joviur joviur commented Jun 28, 2026

Copy link
Copy Markdown

Summary

_ensure_runtime_credentials() called resolve_runtime_provider() without passing target_model, so the resolver derived api_mode and base_url from model_cfg.default instead of the active model. When the config default uses anthropic_messages (e.g. minimax-m3 on opencode-go) and the user runs hermes chat -m <chat_completions_model>, the stale anthropic_messages mode strips /v1 from the base URL and every non-MiniMax model 404s.

Root cause

hermes_cli/cli_agent_setup_mixin.py:_ensure_runtime_credentials (line 41-45) called resolve_runtime_provider(requested=..., explicit_api_key=..., explicit_base_url=...) without target_model. The resolver then fell back to model_cfg.get("default") to derive api_mode, picking the wrong mode for any model that differs from the config default.

resolve_runtime_provider already accepts a target_model kwarg (added in #16878 / PR #16890 for the /model switch path) — it just wasn't being passed here.

Fix

Pass self.model as target_model so the resolver derives api_mode and base_url for the model actually being used.

         _primary_exc = None
         runtime = None
         try:
+            # Pass the active model so the resolver can derive the correct
+            # api_mode / base_url for it (e.g. opencode-go needs to pick
+            # ``chat_completions`` for non-MiniMax models even when the config
+            # default is a MiniMax model that would imply ``anthropic_messages``
+            # and strip ``/v1`` from the endpoint).
+            _target_model = getattr(self, "model", None) or None
             runtime = resolve_runtime_provider(
                 requested=self.requested_provider,
                 explicit_api_key=self._explicit_api_key,
                 explicit_base_url=self._explicit_base_url,
+                target_model=_target_model,
             )
         except Exception as exc:
             _primary_exc = exc

Why it's safe

Verification

  • New regression test test_ensure_runtime_credentials_passes_target_model_to_resolver fails on pre-fix code, passes with the fix.
  • 24/24 tests in test_cli_provider_resolution.py pass.
  • 1118/1119 in tests/cli/ + related tests/hermes_cli/ suites pass (1 pre-existing flaky failure in test_resume_quiet_stderr.py, unrelated — passes in isolation).
  • E2E: hermes chat -m qwen3.7-plus -q "ok" goes from HTTP 404 to a valid response; minimax-m3 continues to work.
# Before fix:
hermes chat -m qwen3.7-plus -q "ok"    → HTTP 404
hermes chat -m kimi-k2.7-code -q "ok"  → HTTP 404
hermes chat -m deepseek-v4-pro -q "ok"  → HTTP 404

# After fix:
hermes chat -m qwen3.7-plus -q "ok"    → valid response ✅
hermes chat -m kimi-k2.7-code -q "ok"  → valid response ✅
hermes chat -m deepseek-v4-pro -q "ok"  → valid response ✅
hermes chat -m minimax-m3 -q "ok"      → valid response ✅ (unchanged)

Related

joviur added 2 commits June 28, 2026 09:42
…ntime_credentials

_ensure_runtime_credentials() called resolve_runtime_provider() without
passing target_model, so the resolver derived api_mode and base_url from
model_cfg.default instead of the active model. When the config default uses
anthropic_messages (e.g. minimax-m3 on opencode-go) and the user runs
'hermes chat -m <chat_completions_model>', the stale anthropic_messages mode
strips /v1 from the base URL and every non-MiniMax model 404s.

Fix: pass self.model as target_model so the resolver derives api_mode and
base_url for the model actually being used.
…_credentials

Fails on pre-fix code (resolver never receives target_model), passes with
the fix. Covers the opencode-go case where minimax-m3 as config default
would wrongly imply anthropic_messages for any -m <other> invocation.
@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists labels Jun 28, 2026

@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: LGTM (approve-ready, COMMENT due to token permissions)

Fix: passes the active model (self.model) as target_model to resolve_runtime_provider so the api_mode/base_url are derived for the model actually being used, not the config default.

Changes

  • _ensure_runtime_credentials() now passes target_model=self.model
  • 1 regression test verifying the resolver receives the correct model

Looks Good

  • Clean one-line fix with clear test coverage
  • The test correctly verifies both the resolver call and the resulting CLI state
  • Edge case: getattr(self, "model", None) handles models without a model attribute

Reviewed by Hermes Agent

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for tracing the stale-model routing path. The primary call on current main is still missing target_model at hermes_cli/cli_agent_setup_mixin.py:41-45, and resolve_runtime_provider() uses target_model or model_cfg.default for OpenCode mode selection (hermes_cli/runtime_provider.py:2052-2053), so the main fix is sound.

Problems

  • The fallback path has the same omission: hermes_cli/cli_agent_setup_mixin.py:60 resolves _fb_provider without a target model, before assigning self.model = _fb_model at line 67. It can therefore still derive api_mode from model.default instead of the selected fallback model.

Suggested changes

  • Pass target_model=_fb_model in the fallback resolver call and add a fallback regression test. The linked #54365 already carries this small superset.

Automated hermes-sweeper review.

requested=self.requested_provider,
explicit_api_key=self._explicit_api_key,
explicit_base_url=self._explicit_base_url,
target_model=_target_model,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please apply the same propagation to the fallback resolver call: current main resolves _fb_provider without target_model before assigning self.model = _fb_model, so a fallback can still derive its route from model.default. Pass target_model=_fb_model there and cover that branch.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
asimons81 pushed a commit to asimons81/hermes-agent that referenced this pull request Jul 15, 2026
…nonical Zen Go URL

The opencode-go HermesOverlay in hermes_cli/providers.py was the only
URL-bearing overlay missing a base_url_override. Every sibling overlay
(openai-api, xai-oauth, qwen-oauth, nous, lmstudio, stepfun,
minimax-oauth) pins a canonical base URL; opencode-go fell through to
mdev_info.api and, depending on what models.dev returned, defaulted to
https://opencode.ai/zen/go with no /v1 suffix.

The /v1-stripped URL is then rounded by anthropic_messages transport
into the same wrong path, surfacing as 'HTTP 404 — Not Found |
opencode' for every non-MiniMax model on the provider. This is the
caller-agnostic half of the bug class tracked at NousResearch/hermes-
agent#54147 — NousResearch#54148 / NousResearch#54365 fix _ensure_runtime_credentials
forwarding target_model (the per-call-site half); this overlay pin
ensures the URL is right regardless of which caller resolves the
provider, covering cron resolution, delegation, kanban, model-picker
prewarm, TUI switcher, and auxiliary fallbacks.

The pinned URL is consistent with every other registration path for
opencode-go in the codebase:

  - hermes_cli/auth.py:385     ProviderConfig.inference_base_url
  - plugins/model-providers/
    opencode-zen/__init__.py:
    142                         OpenCodeGoProfile.base_url
  - ~/.hermes/.env template   OPENCODE_GO_BASE_URL example

Refs NousResearch#54147.
@joviur

joviur commented Aug 4, 2026

Copy link
Copy Markdown
Author

Closing this PR voluntarily — the fix was reimplemented upstream and I'm no longer maintaining this branch. Thanks!

@joviur joviur closed this Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: hermes chat -m <model> uses stale api_mode from config default → 404 on opencode-go

4 participants