mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-29 01:31:41 +00: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:
parent
f4f64c413f
commit
28a073edc6
13 changed files with 381 additions and 84 deletions
|
|
@ -643,6 +643,34 @@ def test_model_config_api_mode(monkeypatch):
|
|||
assert resolved["base_url"] == "http://127.0.0.1:9208/v1"
|
||||
|
||||
|
||||
def test_model_config_api_mode_ignored_when_provider_differs(monkeypatch):
|
||||
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "zai")
|
||||
monkeypatch.setattr(
|
||||
rp,
|
||||
"_get_model_config",
|
||||
lambda: {
|
||||
"provider": "opencode-go",
|
||||
"default": "minimax-m2.5",
|
||||
"api_mode": "anthropic_messages",
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
rp,
|
||||
"resolve_api_key_provider_credentials",
|
||||
lambda provider: {
|
||||
"provider": provider,
|
||||
"api_key": "test-key",
|
||||
"base_url": "https://api.z.ai/api/paas/v4",
|
||||
"source": "env",
|
||||
},
|
||||
)
|
||||
|
||||
resolved = rp.resolve_runtime_provider(requested="zai")
|
||||
|
||||
assert resolved["provider"] == "zai"
|
||||
assert resolved["api_mode"] == "chat_completions"
|
||||
|
||||
|
||||
def test_invalid_api_mode_ignored(monkeypatch):
|
||||
"""Invalid api_mode values should fall back to chat_completions."""
|
||||
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "openrouter")
|
||||
|
|
@ -808,6 +836,78 @@ def test_alibaba_anthropic_endpoint_override_uses_anthropic_messages(monkeypatch
|
|||
assert resolved["base_url"] == "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic"
|
||||
|
||||
|
||||
def test_opencode_zen_gpt_defaults_to_responses(monkeypatch):
|
||||
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "opencode-zen")
|
||||
monkeypatch.setattr(rp, "_get_model_config", lambda: {"default": "gpt-5.4"})
|
||||
monkeypatch.setenv("OPENCODE_ZEN_API_KEY", "test-opencode-zen-key")
|
||||
monkeypatch.delenv("OPENCODE_ZEN_BASE_URL", raising=False)
|
||||
|
||||
resolved = rp.resolve_runtime_provider(requested="opencode-zen")
|
||||
|
||||
assert resolved["provider"] == "opencode-zen"
|
||||
assert resolved["api_mode"] == "codex_responses"
|
||||
assert resolved["base_url"] == "https://opencode.ai/zen/v1"
|
||||
|
||||
|
||||
def test_opencode_zen_claude_defaults_to_messages(monkeypatch):
|
||||
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "opencode-zen")
|
||||
monkeypatch.setattr(rp, "_get_model_config", lambda: {"default": "claude-sonnet-4-6"})
|
||||
monkeypatch.setenv("OPENCODE_ZEN_API_KEY", "test-opencode-zen-key")
|
||||
monkeypatch.delenv("OPENCODE_ZEN_BASE_URL", raising=False)
|
||||
|
||||
resolved = rp.resolve_runtime_provider(requested="opencode-zen")
|
||||
|
||||
assert resolved["provider"] == "opencode-zen"
|
||||
assert resolved["api_mode"] == "anthropic_messages"
|
||||
assert resolved["base_url"] == "https://opencode.ai/zen/v1"
|
||||
|
||||
|
||||
def test_opencode_go_minimax_defaults_to_messages(monkeypatch):
|
||||
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "opencode-go")
|
||||
monkeypatch.setattr(rp, "_get_model_config", lambda: {"default": "minimax-m2.5"})
|
||||
monkeypatch.setenv("OPENCODE_GO_API_KEY", "test-opencode-go-key")
|
||||
monkeypatch.delenv("OPENCODE_GO_BASE_URL", raising=False)
|
||||
|
||||
resolved = rp.resolve_runtime_provider(requested="opencode-go")
|
||||
|
||||
assert resolved["provider"] == "opencode-go"
|
||||
assert resolved["api_mode"] == "anthropic_messages"
|
||||
assert resolved["base_url"] == "https://opencode.ai/zen/go/v1"
|
||||
|
||||
|
||||
def test_opencode_go_glm_defaults_to_chat_completions(monkeypatch):
|
||||
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "opencode-go")
|
||||
monkeypatch.setattr(rp, "_get_model_config", lambda: {"default": "glm-5"})
|
||||
monkeypatch.setenv("OPENCODE_GO_API_KEY", "test-opencode-go-key")
|
||||
monkeypatch.delenv("OPENCODE_GO_BASE_URL", raising=False)
|
||||
|
||||
resolved = rp.resolve_runtime_provider(requested="opencode-go")
|
||||
|
||||
assert resolved["provider"] == "opencode-go"
|
||||
assert resolved["api_mode"] == "chat_completions"
|
||||
assert resolved["base_url"] == "https://opencode.ai/zen/go/v1"
|
||||
|
||||
|
||||
def test_opencode_go_configured_api_mode_still_overrides_default(monkeypatch):
|
||||
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "opencode-go")
|
||||
monkeypatch.setattr(
|
||||
rp,
|
||||
"_get_model_config",
|
||||
lambda: {
|
||||
"provider": "opencode-go",
|
||||
"default": "minimax-m2.5",
|
||||
"api_mode": "chat_completions",
|
||||
},
|
||||
)
|
||||
monkeypatch.setenv("OPENCODE_GO_API_KEY", "test-opencode-go-key")
|
||||
monkeypatch.delenv("OPENCODE_GO_BASE_URL", raising=False)
|
||||
|
||||
resolved = rp.resolve_runtime_provider(requested="opencode-go")
|
||||
|
||||
assert resolved["provider"] == "opencode-go"
|
||||
assert resolved["api_mode"] == "chat_completions"
|
||||
|
||||
|
||||
def test_named_custom_provider_anthropic_api_mode(monkeypatch):
|
||||
"""Custom providers should accept api_mode: anthropic_messages."""
|
||||
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "my-anthropic-proxy")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue