fix(opencode): re-derive api_mode per target model on /model switch - #16890
Merged
Conversation
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 #16878
…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.
This was referenced Jun 28, 2026
Closed
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.
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-flashfrom aminimax-m2.7default onopencode-go/opencode-zen404'd. The api_mode resolver honoured the persistedapi_mode: anthropic_messages(set when minimax was default) before the opencode model registry, so the deepseek turn inheritedanthropic_messages, stripped/v1frombase_url, and went tohttps://opencode.ai/zen/go/messagesinstead of.../v1/chat/completions.Fixes #16878.
Changes
hermes_cli/runtime_provider.py— promote the opencode-zen/go branch above theconfigured_modecheck in both api_mode resolution paths (_resolve_runtime_from_pool_entryand the API-key fallback inresolve_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 + inverttest_opencode_go_configured_api_mode_still_overrides_default(added in fix: repair OpenCode model routing and selection #4508) to lock in the new precedence. A persistedchat_completionson 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
/model deepseek-v4-flash.../zen/go(404).../zen/go/v1/model minimax-m2.7.../zen/goSuites:
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).