mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-28 06:51:16 +08:00
fix: repair OpenCode model routing and selection (#4508)
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. Co-authored-by: SaM13997 <139419381+SaM13997@users.noreply.github.com>
This commit is contained in:
@@ -125,6 +125,12 @@ _PROVIDER_MODELS: dict[str, list[str]] = {
|
||||
"kimi-k2-turbo-preview",
|
||||
"kimi-k2-0905-preview",
|
||||
],
|
||||
"moonshot": [
|
||||
"kimi-k2.5",
|
||||
"kimi-k2-thinking",
|
||||
"kimi-k2-turbo-preview",
|
||||
"kimi-k2-0905-preview",
|
||||
],
|
||||
"minimax": [
|
||||
"MiniMax-M2.7",
|
||||
"MiniMax-M2.7-highspeed",
|
||||
@@ -948,6 +954,53 @@ def copilot_model_api_mode(
|
||||
return "chat_completions"
|
||||
|
||||
|
||||
def normalize_opencode_model_id(provider_id: Optional[str], model_id: Optional[str]) -> str:
|
||||
"""Normalize OpenCode config IDs to the bare model slug used in API requests."""
|
||||
provider = normalize_provider(provider_id)
|
||||
current = str(model_id or "").strip()
|
||||
if not current or provider not in {"opencode-zen", "opencode-go"}:
|
||||
return current
|
||||
|
||||
prefix = f"{provider}/"
|
||||
if current.lower().startswith(prefix):
|
||||
return current[len(prefix):]
|
||||
return current
|
||||
|
||||
|
||||
def opencode_model_api_mode(provider_id: Optional[str], model_id: Optional[str]) -> str:
|
||||
"""Determine the API mode for an OpenCode Zen / Go model.
|
||||
|
||||
OpenCode routes different models behind different API surfaces:
|
||||
|
||||
- GPT-5 / Codex models on Zen use ``/v1/responses``
|
||||
- Claude models on Zen use ``/v1/messages``
|
||||
- MiniMax models on Go use ``/v1/messages``
|
||||
- GLM / Kimi on Go use ``/v1/chat/completions``
|
||||
- Other Zen models (Gemini, GLM, Kimi, MiniMax, Qwen, etc.) use
|
||||
``/v1/chat/completions``
|
||||
|
||||
This follows the published OpenCode docs for Zen and Go endpoints.
|
||||
"""
|
||||
provider = normalize_provider(provider_id)
|
||||
normalized = normalize_opencode_model_id(provider_id, model_id).lower()
|
||||
if not normalized:
|
||||
return "chat_completions"
|
||||
|
||||
if provider == "opencode-go":
|
||||
if normalized.startswith("minimax-"):
|
||||
return "anthropic_messages"
|
||||
return "chat_completions"
|
||||
|
||||
if provider == "opencode-zen":
|
||||
if normalized.startswith("claude-"):
|
||||
return "anthropic_messages"
|
||||
if normalized.startswith("gpt-"):
|
||||
return "codex_responses"
|
||||
return "chat_completions"
|
||||
|
||||
return "chat_completions"
|
||||
|
||||
|
||||
def github_model_reasoning_efforts(
|
||||
model_id: Optional[str],
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user