From 68d406f7ffe6e5735ab3d684fe22ae6e3db7fffa Mon Sep 17 00:00:00 2001 From: sbe27 <283218367+sbe27@users.noreply.github.com> Date: Wed, 8 Jul 2026 02:04:21 +0200 Subject: [PATCH] fix(models): refresh OpenCode Go static catalog --- api/config.py | 21 ++++-- ...est_issue5311_opencode_go_static_models.py | 70 +++++++++++++++---- 2 files changed, 70 insertions(+), 21 deletions(-) diff --git a/api/config.py b/api/config.py index b2f80efc..3d14e7bb 100644 --- a/api/config.py +++ b/api/config.py @@ -1766,22 +1766,29 @@ _PROVIDER_MODELS = { {"id": "nemotron-3-super-free", "label": "Nemotron 3 Super Free"}, {"id": "big-pickle", "label": "Big Pickle"}, ], - # OpenCode Go — flat-rate models via opencode.ai/go ($10/month) + # OpenCode Go — flat-rate models via opencode.ai/go ($10/month). + # Synced 2026-07-08 from the public Go docs and documented models endpoint. + # Keep preview/free-only Zen models out of this Go picker snapshot. "opencode-go": [ + {"id": "minimax-m3", "label": "MiniMax M3"}, + {"id": "minimax-m2.7", "label": "MiniMax M2.7"}, + {"id": "minimax-m2.5", "label": "MiniMax M2.5"}, + {"id": "kimi-k2.7-code", "label": "Kimi K2.7 Code"}, + {"id": "kimi-k2.6", "label": "Kimi K2.6"}, + {"id": "kimi-k2.5", "label": "Kimi K2.5"}, + {"id": "glm-5.2", "label": "GLM-5.2"}, {"id": "glm-5.1", "label": "GLM-5.1"}, {"id": "glm-5", "label": "GLM-5"}, - {"id": "kimi-k2.5", "label": "Kimi K2.5"}, - {"id": "kimi-k2.6", "label": "Kimi K2.6"}, {"id": "deepseek-v4-pro", "label": "DeepSeek V4 Pro"}, {"id": "deepseek-v4-flash","label": "DeepSeek V4 Flash"}, + {"id": "qwen3.7-max", "label": "Qwen3.7 Max"}, + {"id": "qwen3.7-plus", "label": "Qwen3.7 Plus"}, + {"id": "qwen3.6-plus", "label": "Qwen3.6 Plus"}, + {"id": "qwen3.5-plus", "label": "Qwen3.5 Plus"}, {"id": "mimo-v2-pro", "label": "MiMo V2 Pro"}, {"id": "mimo-v2-omni", "label": "MiMo V2 Omni"}, {"id": "mimo-v2.5-pro", "label": "MiMo V2.5 Pro"}, {"id": "mimo-v2.5", "label": "MiMo V2.5"}, - {"id": "minimax-m2.7", "label": "MiniMax M2.7"}, - {"id": "minimax-m2.5", "label": "MiniMax M2.5"}, - {"id": "qwen3.6-plus", "label": "Qwen3.6 Plus"}, - {"id": "qwen3.5-plus", "label": "Qwen3.5 Plus"}, ], # 'gemini' is the hermes_cli provider ID for Google AI Studio # Model IDs are bare — sent directly to: diff --git a/tests/test_issue5311_opencode_go_static_models.py b/tests/test_issue5311_opencode_go_static_models.py index 679fa3e4..7539619d 100644 --- a/tests/test_issue5311_opencode_go_static_models.py +++ b/tests/test_issue5311_opencode_go_static_models.py @@ -1,30 +1,72 @@ -"""#5311 / #5611: OpenCode Go's live /v1/models probe returns public-catalog -models not enabled on the Go tier, so selecting one 404s on send. get_available_models -must skip the live probe for opencode-go and fall through to the curated static -_PROVIDER_MODELS list instead. +"""#5311 / #5611: OpenCode Go's generic live probe used to return public-catalog +models not enabled on the Go tier, so the picker intentionally falls back to a curated +static _PROVIDER_MODELS list. Keep that curated list in sync with the public Go docs +and documented Go models endpoint while excluding preview/free-only Zen models. """ +import ast from pathlib import Path ROOT = Path(__file__).resolve().parent.parent -CONFIG = (ROOT / "api" / "config.py").read_text(encoding="utf-8") +CONFIG_PATH = ROOT / "api" / "config.py" +CONFIG = CONFIG_PATH.read_text(encoding="utf-8") + +EXPECTED_OPENCODE_GO_MODEL_IDS = [ + "minimax-m3", + "minimax-m2.7", + "minimax-m2.5", + "kimi-k2.7-code", + "kimi-k2.6", + "kimi-k2.5", + "glm-5.2", + "glm-5.1", + "glm-5", + "deepseek-v4-pro", + "deepseek-v4-flash", + "qwen3.7-max", + "qwen3.7-plus", + "qwen3.6-plus", + "qwen3.5-plus", + "mimo-v2-pro", + "mimo-v2-omni", + "mimo-v2.5-pro", + "mimo-v2.5", +] + + +def _opencode_go_static_models(): + tree = ast.parse(CONFIG, filename=str(CONFIG_PATH)) + for node in tree.body: + if not isinstance(node, ast.Assign): + continue + if not any(isinstance(target, ast.Name) and target.id == "_PROVIDER_MODELS" for target in node.targets): + continue + provider_models = ast.literal_eval(node.value) + return provider_models["opencode-go"] + raise AssertionError("_PROVIDER_MODELS assignment not found") def test_opencode_go_skips_live_models_probe(): - # The provider-loop must special-case opencode-go to skip the live probe. + # The provider-loop must special-case opencode-go to skip the old generic + # live probe and fall through to this curated static list. body = CONFIG[CONFIG.index("def get_available_models"):] body = body[: body.index("\ndef ", 1)] assert 'elif pid == "opencode-go":' in body - # It must NOT call the live-provider probe on the opencode-go branch — the - # branch is a bare skip so the next `if not raw_models` falls back to static. idx = body.index('elif pid == "opencode-go":') branch = body[idx: idx + 400] assert "_models_from_live_provider_ids" not in branch.split("else:")[0] -def test_opencode_go_has_curated_static_models(): - # The static fallback the skip relies on must exist and be non-empty. - assert '"opencode-go": [' in CONFIG - block = CONFIG[CONFIG.index('"opencode-go": ['):] - block = block[: block.index("],") + 1] - assert block.count('"id":') >= 5 +def test_opencode_go_static_models_match_documented_endpoint_snapshot(): + # Snapshot from https://opencode.ai/zen/go/v1/models on 2026-07-07. + # This catches stale-list regressions such as missing GLM-5.2 / MiniMax M3. + models = _opencode_go_static_models() + assert [model["id"] for model in models] == EXPECTED_OPENCODE_GO_MODEL_IDS + + +def test_opencode_go_recent_additions_have_human_labels(): + labels = {model["id"]: model["label"] for model in _opencode_go_static_models()} + assert labels["glm-5.2"] == "GLM-5.2" + assert labels["minimax-m3"] == "MiniMax M3" + assert labels["kimi-k2.7-code"] == "Kimi K2.7 Code" + assert labels["qwen3.7-max"] == "Qwen3.7 Max"