fix: repair OpenCode model routing and selection

OpenCode Zen and Go are mixed-API-surface providers — different models
behind them use different API surfaces (GPT on Zen uses codex_responses,
Claude on Zen uses anthropic_messages, MiniMax on Go uses
anthropic_messages, GLM/Kimi on Go use chat_completions).

Changes:
- Add normalize_opencode_model_id() and opencode_model_api_mode() to
  models.py for model ID normalization and API surface routing
- Add _provider_supports_explicit_api_mode() to runtime_provider.py
  to prevent stale api_mode from leaking across provider switches
- Wire opencode routing into all three api_mode resolution paths:
  pool entry, api_key provider, and explicit runtime
- Add api_mode field to ModelSwitchResult for propagation through the
  switch pipeline
- Consolidate _PROVIDER_MODELS from main.py into models.py (single
  source of truth, eliminates duplicate dict)
- Add opencode normalization to setup wizard and model picker flows
- Add opencode block to _normalize_model_for_provider in CLI
- Add opencode-zen/go fallback model lists to setup.py

Tests: 160 targeted tests pass (26 new tests covering normalization,
api_mode routing per provider/model, persistence, and setup wizard
normalization).

Based on PR #3017 by SaM13997.
This commit is contained in:
SaM13997
2026-04-01 16:35:47 -07:00
committed by Teknium
parent 3628ccc8c4
commit 11efa75bb9
13 changed files with 381 additions and 84 deletions
+22
View File
@@ -1545,6 +1545,28 @@ class HermesCLI:
pass
return changed
if resolved_provider in {"opencode-zen", "opencode-go"}:
try:
from hermes_cli.models import normalize_opencode_model_id, opencode_model_api_mode
canonical = normalize_opencode_model_id(resolved_provider, current_model)
if canonical and canonical != current_model:
if not self._model_is_default:
self.console.print(
f"[yellow]⚠️ Stripped provider prefix from '{current_model}'; using '{canonical}' for {resolved_provider}.[/]"
)
self.model = canonical
current_model = canonical
changed = True
resolved_mode = opencode_model_api_mode(resolved_provider, current_model)
if resolved_mode != self.api_mode:
self.api_mode = resolved_mode
changed = True
except Exception:
pass
return changed
if resolved_provider != "openai-codex":
return False