fix(cli): pass target_model to resolve_runtime_provider in _ensure_runtime_credentials - #54148
fix(cli): pass target_model to resolve_runtime_provider in _ensure_runtime_credentials#54148joviur wants to merge 2 commits into
Conversation
…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.
tonydwb
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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:60resolves_fb_providerwithout a target model, before assigningself.model = _fb_modelat line 67. It can therefore still deriveapi_modefrommodel.defaultinstead of the selected fallback model.
Suggested changes
- Pass
target_model=_fb_modelin 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, |
There was a problem hiding this comment.
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.
…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.
|
Closing this PR voluntarily — the fix was reimplemented upstream and I'm no longer maintaining this branch. Thanks! |
Summary
_ensure_runtime_credentials()calledresolve_runtime_provider()without passingtarget_model, so the resolver derivedapi_modeandbase_urlfrommodel_cfg.defaultinstead of the active model. When the config default usesanthropic_messages(e.g.minimax-m3onopencode-go) and the user runshermes chat -m <chat_completions_model>, the staleanthropic_messagesmode strips/v1from the base URL and every non-MiniMax model 404s.Root cause
hermes_cli/cli_agent_setup_mixin.py:_ensure_runtime_credentials(line 41-45) calledresolve_runtime_provider(requested=..., explicit_api_key=..., explicit_base_url=...)withouttarget_model. The resolver then fell back tomodel_cfg.get("default")to deriveapi_mode, picking the wrong mode for any model that differs from the config default.resolve_runtime_provideralready accepts atarget_modelkwarg (added in #16878 / PR #16890 for the/modelswitch path) — it just wasn't being passed here.Fix
Pass
self.modelastarget_modelso the resolver derivesapi_modeandbase_urlfor 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 = excWhy it's safe
target_modelis already an optional kwarg ofresolve_runtime_provider(since Bug: /v1 stripped from base_url when switching models via /model with opencode-go provider #16878 / PR fix(opencode): re-derive api_mode per target model on /model switch #16890, merged in v2026.4.30).getattr(self, "model", None) or Noneis defensive: ifself.modelis missing or empty, it passesNoneand the resolver uses the default (previous behavior).resolve_runtime_providerorruntime_provider.py.self.api_mode = "chat_completions"initialization incli.py:3600.minimax-m3as default, the resolver now explicitly receivestarget_model="minimax-m3"and returnsanthropic_messages— same result as before, but deterministic instead of by coincidence.Verification
test_ensure_runtime_credentials_passes_target_model_to_resolverfails on pre-fix code, passes with the fix.test_cli_provider_resolution.pypass.tests/cli/+ relatedtests/hermes_cli/suites pass (1 pre-existing flaky failure intest_resume_quiet_stderr.py, unrelated — passes in isolation).hermes chat -m qwen3.7-plus -q "ok"goes from HTTP 404 to a valid response;minimax-m3continues to work.Related
hermes chat -m <model>uses stale api_mode from config default → 404 on opencode-go #54147/modelswitch path but not the CLI single-query path.delegate_tool.py(missingtarget_model), fixed with the same one-line pattern.runtime_overridestaleness in_init_agent(openai-codex); compatible with this change.