fix(opencode): re-derive api_mode per target model on /model switch - #16888
Closed
Sanjays2402 wants to merge 2 commits into
Closed
fix(opencode): re-derive api_mode per target model on /model switch#16888Sanjays2402 wants to merge 2 commits into
Sanjays2402 wants to merge 2 commits into
Conversation
run_agent.py persisted assistant turns with the chain of thought under
the internal field 'reasoning' and only wrote the protocol-standard
'reasoning_content' when (a) the upstream SDK happened to expose it as
a top-level attribute, or (b) the current provider was DeepSeek and the
turn had tool_calls. Streaming-only providers (glm, MiniMax, gpt-5.x via
aigw, Anthropic via openai-compat shim, etc.) accumulate reasoning from
delta.reasoning_content into a local string but never set the SDK
attribute, so the persisted message is missing reasoning_content.
The bug is silent until the user later replays that history through a
DeepSeek-v4 / Kimi thinking model, which requires reasoning_content on
every replayed assistant turn:
The reasoning_content in the thinking mode must be passed back to
the API.
Read-side patches (NousResearch#15213, NousResearch#15741, NousResearch#15748, NousResearch#15353) each fix one build
path, but every new path that reads history from disk is a fresh place
the same 400 can resurface.
Normalize at write time:
- prefer the SDK-supplied reasoning_content when present (may carry
structured data);
- otherwise fall back to the already-sanitized reasoning_text that was
accumulated from streaming deltas;
- finally default to "" so non-thinking providers ignore the field
harmlessly while DeepSeek/Kimi see a valid (empty) value.
The internal 'reasoning' alias is preserved for backward compatibility
with existing read paths and downstream consumers.
Refs NousResearch#16844
opencode-zen and opencode-go each serve both anthropic_messages (e.g. minimax-m2.7) and chat_completions (e.g. deepseek-v4-flash) models behind a single base_url. The api_mode resolver in hermes_cli/runtime_provider.py honoured the persisted model_cfg.api_mode (set by the previous default model) before checking the opencode model registry, so /model deepseek-v4-flash from a session whose default was minimax-m2.7 inherited 'anthropic_messages', stripped '/v1' from base_url (the Anthropic SDK adds its own /v1/messages), and 404'd. Promote the opencode detection branch above the configured_mode check in both api_mode resolution paths: - _resolve_runtime_from_pool_entry (pool-backed providers) - _resolve_api_key_runtime (api-key providers, fallback path) Both branches now call opencode_model_api_mode(provider, effective_model) unconditionally for opencode-zen/go before considering any persisted api_mode, so the mode always reflects the model the user just switched to. Existing tests pass (12/12 in tests/hermes_cli/test_model_switch_opencode_anthropic.py). Fixes NousResearch#16878
Collaborator
teknium1
added a commit
that referenced
this pull request
Apr 28, 2026
…e api_mode PR #16888 swaps the opencode-zen/go resolver so that api_mode is always re-derived from the effective model before the persisted api_mode is consulted. That's the point of the fix — a stale anthropic_messages from a previous minimax default must not survive a /model switch to a chat_completions target (or vice versa) and strip /v1 from base_url. The prior test asserted the opposite precedence — that a persisted api_mode won over model-derived mode — and was added in #4508 to lock in escape-hatch behavior. Under the new precedence that escape hatch no longer exists for opencode (only for providers that genuinely support both modes at a single endpoint — and for opencode the model name is the unambiguous signal). Rename + invert the assertion to document the intentional behavior change. Refs #16878.
Contributor
|
Salvaged via PR #16890 — the opencode fix (commit efda405) was cherry-picked onto current main with your authorship preserved via rebase-merge (commits b52cecc + 54e24f7). The stacked run_agent.py reasoning_content change was dropped from this PR so it can be evaluated separately against #16844. One existing test ( Thanks @Sanjays2402 — the root-cause analysis in the PR body and issue #16878 was exactly right. |
cluricaun28
referenced
this pull request
in cluricaun28/Logos
Apr 28, 2026
…e api_mode PR #16888 swaps the opencode-zen/go resolver so that api_mode is always re-derived from the effective model before the persisted api_mode is consulted. That's the point of the fix — a stale anthropic_messages from a previous minimax default must not survive a /model switch to a chat_completions target (or vice versa) and strip /v1 from base_url. The prior test asserted the opposite precedence — that a persisted api_mode won over model-derived mode — and was added in #4508 to lock in escape-hatch behavior. Under the new precedence that escape hatch no longer exists for opencode (only for providers that genuinely support both modes at a single endpoint — and for opencode the model name is the unambiguous signal). Rename + invert the assertion to document the intentional behavior change. Refs #16878.
13 tasks
donald131
pushed a commit
to donald131/hermes-agent
that referenced
this pull request
May 2, 2026
…e api_mode PR NousResearch#16888 swaps the opencode-zen/go resolver so that api_mode is always re-derived from the effective model before the persisted api_mode is consulted. That's the point of the fix — a stale anthropic_messages from a previous minimax default must not survive a /model switch to a chat_completions target (or vice versa) and strip /v1 from base_url. The prior test asserted the opposite precedence — that a persisted api_mode won over model-derived mode — and was added in NousResearch#4508 to lock in escape-hatch behavior. Under the new precedence that escape hatch no longer exists for opencode (only for providers that genuinely support both modes at a single endpoint — and for opencode the model name is the unambiguous signal). Rename + invert the assertion to document the intentional behavior change. Refs NousResearch#16878.
02356abc
pushed a commit
to 02356abc/hermes-agent
that referenced
this pull request
May 14, 2026
…e api_mode PR NousResearch#16888 swaps the opencode-zen/go resolver so that api_mode is always re-derived from the effective model before the persisted api_mode is consulted. That's the point of the fix — a stale anthropic_messages from a previous minimax default must not survive a /model switch to a chat_completions target (or vice versa) and strip /v1 from base_url. The prior test asserted the opposite precedence — that a persisted api_mode won over model-derived mode — and was added in NousResearch#4508 to lock in escape-hatch behavior. Under the new precedence that escape hatch no longer exists for opencode (only for providers that genuinely support both modes at a single endpoint — and for opencode the model name is the unambiguous signal). Rename + invert the assertion to document the intentional behavior change. Refs NousResearch#16878.
dannyJ848
pushed a commit
to dannyJ848/hermes-agent
that referenced
this pull request
May 17, 2026
…e api_mode PR NousResearch#16888 swaps the opencode-zen/go resolver so that api_mode is always re-derived from the effective model before the persisted api_mode is consulted. That's the point of the fix — a stale anthropic_messages from a previous minimax default must not survive a /model switch to a chat_completions target (or vice versa) and strip /v1 from base_url. The prior test asserted the opposite precedence — that a persisted api_mode won over model-derived mode — and was added in NousResearch#4508 to lock in escape-hatch behavior. Under the new precedence that escape hatch no longer exists for opencode (only for providers that genuinely support both modes at a single endpoint — and for opencode the model name is the unambiguous signal). Rename + invert the assertion to document the intentional behavior change. Refs NousResearch#16878.
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
…e api_mode PR NousResearch#16888 swaps the opencode-zen/go resolver so that api_mode is always re-derived from the effective model before the persisted api_mode is consulted. That's the point of the fix — a stale anthropic_messages from a previous minimax default must not survive a /model switch to a chat_completions target (or vice versa) and strip /v1 from base_url. The prior test asserted the opposite precedence — that a persisted api_mode won over model-derived mode — and was added in NousResearch#4508 to lock in escape-hatch behavior. Under the new precedence that escape hatch no longer exists for opencode (only for providers that genuinely support both modes at a single endpoint — and for opencode the model name is the unambiguous signal). Rename + invert the assertion to document the intentional behavior change. Refs NousResearch#16878.
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
…e api_mode PR NousResearch#16888 swaps the opencode-zen/go resolver so that api_mode is always re-derived from the effective model before the persisted api_mode is consulted. That's the point of the fix — a stale anthropic_messages from a previous minimax default must not survive a /model switch to a chat_completions target (or vice versa) and strip /v1 from base_url. The prior test asserted the opposite precedence — that a persisted api_mode won over model-derived mode — and was added in NousResearch#4508 to lock in escape-hatch behavior. Under the new precedence that escape hatch no longer exists for opencode (only for providers that genuinely support both modes at a single endpoint — and for opencode the model name is the unambiguous signal). Rename + invert the assertion to document the intentional behavior change. Refs NousResearch#16878.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
opencode-zenandopencode-goeach serve bothanthropic_messagesmodels (e.g.minimax-m2.7) andchat_completionsmodels (e.g.deepseek-v4-flash) behind a singlebase_url(https://opencode.ai/zen/go/v1). When the user starts a session with aminimax-m2.7default and runs/model deepseek-v4-flash, the api_mode resolver inhermes_cli/runtime_provider.pyhonours the persistedmodel_cfg.api_mode(=anthropic_messages, set by the previous default) before checking the opencode model registry, so the deepseek turn inheritsanthropic_messages, strips/v1frombase_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_modecheck 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 foropencode-zen/opencode-gobefore considering any persistedapi_mode, so the mode always reflects the model the user just switched to. Other providers retain the existing precedence (persistedapi_mode> URL detection).Tests
Existing suite passes:
This covers both the
minimax-m2.7 → deepseek-v4-flashdirection (the failing case in the issue) and the reversedeepseek-v4-flash → minimax-m2.7, which stays correct because the same code path also re-derives the mode from the target model.Diff
Fixes #16878