hermes - ✅(Solved) Fix Bug: /v1 stripped from base_url when switching models via /model with opencode-go provider [3 pull requests, 4 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#16878Fetched 2026-04-29 06:38:19
View on GitHub
Comments
4
Participants
2
Timeline
16
Reactions
0
Author
Participants
Timeline (top)
commented ×4labeled ×4referenced ×4cross-referenced ×3

When using the opencode-go provider with a config default model that uses anthropic_messages mode (e.g. minimax-m2.7) and switching to a chat_completions model (e.g. deepseek-v4-flash) via the /model slash command, the base_url loses /v1, resulting in an HTTP 404 error.

Error Message

When using the opencode-go provider with a config default model that uses anthropic_messages mode (e.g. minimax-m2.7) and switching to a chat_completions model (e.g. deepseek-v4-flash) via the /model slash command, the base_url loses /v1, resulting in an HTTP 404 error.

Root Cause

In runtime_provider.py, the non-pool "API-key providers" block (~lines 1213-1237), when the credential pool is exhausted (all entries None):

  1. configured_mode = _parse_api_mode(model_cfg.get("api_mode")) reads "anthropic_messages" from the stale config (set when minimax was default)
  2. Line 1222: if configured_mode and _provider_supports_explicit_api_mode(provider, configured_provider): api_mode = configured_mode — applies the stale anthropic_messages mode to the new deepseek model
  3. The opencode-go/zen api_mode detection block (lines 1224-1230) is an elif to this check, so it never runs when configured_mode is truthy

Since opencode_model_api_mode() correctly returns chat_completions for deepseek, but is never called, the wrong api_mode remains. When api_mode is anthropic_messages, the base URL transformation strips /v1 (zen routes /v1 \u2192 / for anthropic mode).

Fix Action

Fix / Workaround

Fix Applied (two patches)

Patch 1 — _resolve_runtime_from_pool_entry (line 268): Changed elif to if so opencode detection always runs for opencode providers (defense-in-depth).

Patch 2 — API-key providers block (line 1214+): Promoted opencode-go/zen detection to an explicit if block before the else configured_mode check, so it runs unconditionally for opencode providers.

PR fix notes

PR #16885: fix(opencode): preserve /v1 in base_url when switching from anthropic_messages to chat_completions models

Description (problem / solution / changelog)

Summary

When using the opencode-go provider with a config default model that uses anthropic_messages mode (e.g. minimax-m2.7) and switching to a chat_completions model (e.g. deepseek-v4-flash) via the /model slash command, the base_url loses /v1, resulting in an HTTP 404 error.

Fixes: https://github.com/NousResearch/hermes-agent/issues/16878

Root Cause

In runtime_provider.py, the non-pool "API-key providers" block (~lines 1213-1237), when the credential pool is exhausted:

  1. configured_mode = _parse_api_mode(model_cfg.get("api_mode")) reads "anthropic_messages" from the stale config
  2. This stale mode is applied before the opencode-go/zen api_mode detection can run
  3. The opencode block was an elif, so it never ran when configured_mode was truthy

Fix Applied

Promoted opencode-go/zen detection to an explicit if block that runs before the configured_mode override, so it always correctly detects api_mode based on the target model.

Same change applied to _resolve_runtime_from_pool_entry for consistency.

Verification

# default=minimax-m2.7 → switch to deepseek-v4-flash
resolve_runtime_provider(requested="opencode-go", target_model="deepseek-v4-flash")
# → api_mode="chat_completions", base_url="https://opencode.ai/zen/go/v1" ✓

# reverse: default=deepseek-v4-flash → switch to minimax-m2.7
resolve_runtime_provider(requested="opencode-go", target_model="minimax-m2.7")
# → api_mode="anthropic_messages", base_url="https://opencode.ai/zen/go" ✓

Tests

12/12 passing: python -m pytest tests/hermes_cli/test_model_switch_opencode_anthropic.py -v

Changed files

  • hermes_cli/runtime_provider.py (modified, +18/-10)

PR #16888: fix(opencode): re-derive api_mode per target model on /model switch

Description (problem / solution / changelog)

What

opencode-zen and opencode-go each serve both anthropic_messages models (e.g. minimax-m2.7) and chat_completions models (e.g. deepseek-v4-flash) behind a single base_url (https://opencode.ai/zen/go/v1). When the user starts a session with a minimax-m2.7 default and runs /model deepseek-v4-flash, the api_mode resolver in hermes_cli/runtime_provider.py honours the persisted model_cfg.api_mode (= anthropic_messages, set by the previous default) before checking the opencode model registry, so the deepseek turn inherits anthropic_messages, strips /v1 from base_url (the Anthropic SDK adds its own /v1/messages), and 404s.

Reported and root-caused in #16878.

Fix

Promote the opencode detection branch above the configured_mode check in both api_mode resolution paths:

  • _resolve_runtime_from_pool_entry — pool-backed providers (~line 262)
  • _resolve_api_key_runtime — API-key fallback (~line 1213)

Both branches now call opencode_model_api_mode(provider, effective_model) unconditionally for opencode-zen / opencode-go before considering any persisted api_mode, so the mode always reflects the model the user just switched to. Other providers retain the existing precedence (persisted api_mode > URL detection).

Tests

Existing suite passes:

$ pytest tests/hermes_cli/test_model_switch_opencode_anthropic.py
............                                                             [100%]
12 passed, 13 warnings in 4.31s

This covers both the minimax-m2.7 → deepseek-v4-flash direction (the failing case in the issue) and the reverse deepseek-v4-flash → minimax-m2.7, which stays correct because the same code path also re-derives the mode from the target model.

Diff

hermes_cli/runtime_provider.py | 28 +++++++++++++++++---------
1 file changed, 19 insertions(+), 9 deletions(-)

Fixes #16878

Changed files

  • hermes_cli/runtime_provider.py (modified, +19/-9)
  • run_agent.py (modified, +17/-10)

PR #16890: fix(opencode): re-derive api_mode per target model on /model switch

Description (problem / solution / changelog)

Salvages PR #16888 (opencode fix only — the second stacked reasoning_content commit from that PR is dropped; it is a separate concern tracked under #16844 and will be evaluated on its own).

Summary

/model deepseek-v4-flash from a minimax-m2.7 default on opencode-go / opencode-zen 404'd. The api_mode resolver honoured the persisted api_mode: anthropic_messages (set when minimax was default) before the opencode model registry, so the deepseek turn inherited anthropic_messages, stripped /v1 from base_url, and went to https://opencode.ai/zen/go/messages instead of .../v1/chat/completions.

Fixes #16878.

Changes

  • hermes_cli/runtime_provider.py — promote the opencode-zen/go branch above the configured_mode check in both api_mode resolution paths (_resolve_runtime_from_pool_entry and the API-key fallback in resolve_runtime_provider). opencode always re-derives mode from the effective target_model; other providers keep existing precedence (persisted api_mode > URL detection).
  • tests/hermes_cli/test_runtime_provider_resolution.py — rename + invert test_opencode_go_configured_api_mode_still_overrides_default (added in #4508) to lock in the new precedence. A persisted chat_completions on a minimax model no longer wins — the model dictates the mode. Escape-hatch for opencode is intentionally removed; the model name is the unambiguous signal.

Validation

BeforeAfter
minimax default → /model deepseek-v4-flashapi_mode=anthropic_messages, base_url=.../zen/go (404)api_mode=chat_completions, base_url=.../zen/go/v1
deepseek default → /model minimax-m2.7api_mode=anthropic_messages, base_url=.../zen/gounchanged
no target_model (legacy callers)unchangedunchanged

Suites: tests/hermes_cli/test_runtime_provider_resolution.py, test_model_switch_opencode_anthropic.py, test_model_switch_custom_providers.py — 109/109 pass. E2E verified against origin/main with a real config.yaml and isolated HERMES_HOME.

Credit: @Sanjays2402 for the original fix (#16888).

Changed files

  • hermes_cli/runtime_provider.py (modified, +19/-9)
  • tests/hermes_cli/test_runtime_provider_resolution.py (modified, +13/-2)
RAW_BUFFERClick to expand / collapse

Summary

When using the opencode-go provider with a config default model that uses anthropic_messages mode (e.g. minimax-m2.7) and switching to a chat_completions model (e.g. deepseek-v4-flash) via the /model slash command, the base_url loses /v1, resulting in an HTTP 404 error.

Reproduction Steps

  1. Config default: minimax-m2.7 with provider: opencode-go and base_url: https://opencode.ai/zen/go/v1
  2. Start TUI (or CLI) — works fine with minimax
  3. Run /model deepseek-v4-flash
  4. Attempt to chat — fails with HTTP 404

Root Cause

In runtime_provider.py, the non-pool "API-key providers" block (~lines 1213-1237), when the credential pool is exhausted (all entries None):

  1. configured_mode = _parse_api_mode(model_cfg.get("api_mode")) reads "anthropic_messages" from the stale config (set when minimax was default)
  2. Line 1222: if configured_mode and _provider_supports_explicit_api_mode(provider, configured_provider): api_mode = configured_mode — applies the stale anthropic_messages mode to the new deepseek model
  3. The opencode-go/zen api_mode detection block (lines 1224-1230) is an elif to this check, so it never runs when configured_mode is truthy

Since opencode_model_api_mode() correctly returns chat_completions for deepseek, but is never called, the wrong api_mode remains. When api_mode is anthropic_messages, the base URL transformation strips /v1 (zen routes /v1 \u2192 / for anthropic mode).

Fix Applied (two patches)

Patch 1 — _resolve_runtime_from_pool_entry (line 268): Changed elif to if so opencode detection always runs for opencode providers (defense-in-depth).

Patch 2 — API-key providers block (line 1214+): Promoted opencode-go/zen detection to an explicit if block before the else configured_mode check, so it runs unconditionally for opencode providers.

Verification

```python

default=minimax-m2.7 \u2192 switch to deepseek-v4-flash

resolve_runtime_provider(requested="opencode-go", target_model="deepseek-v4-flash")

\u2192 api_mode="chat_completions", base_url="https://opencode.ai/zen/go/v1" \u2713

reverse: default=deepseek-v4-flash \u2192 switch to minimax-m2.7

resolve_runtime_provider(requested="opencode-go", target_model="minimax-m2.7")

\u2192 api_mode="anthropic_messages", base_url="https://opencode.ai/zen/go" \u2713

```

Environment

  • Hermes v0.11.0 (2026.4.23), commit 8081425a
  • Provider: opencode-go
  • Config base_url: https://opencode.ai/zen/go/v1

Test Suite

Existing tests pass (12/12): ```bash python -m pytest tests/hermes_cli/test_model_switch_opencode_anthropic.py -v ```

extent analysis

TL;DR

Apply the two patches to runtime_provider.py to ensure correct API mode detection for opencode-go providers when switching models.

Guidance

  • Review the changes made in Patch 1 and Patch 2 to understand the fix: promoting opencode-go/zen detection to an explicit if block and changing elif to if for opencode providers.
  • Verify the fix by running the provided test cases, specifically resolve_runtime_provider with different model switches.
  • Check the api_mode and base_url values returned by resolve_runtime_provider to ensure they match the expected values for each model.
  • Consider adding additional test cases to cover more scenarios and edge cases.

Example

# Example usage of resolve_runtime_provider
resolve_runtime_provider(requested="opencode-go", target_model="deepseek-v4-flash")
# Expected output: api_mode="chat_completions", base_url="https://opencode.ai/zen/go/v1"

Notes

The provided patches and test cases seem to address the issue, but it's essential to review the changes and test them thoroughly in your environment to ensure the fix works as expected.

Recommendation

Apply the workaround by implementing the two patches to runtime_provider.py, as they provide a clear fix for the issue and have been verified through test cases.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix Bug: /v1 stripped from base_url when switching models via /model with opencode-go provider [3 pull requests, 4 comments, 2 participants]