fix: preserve dots in model names for OpenCode Zen and ZAI providers (#8794)

OpenCode Zen was in _DOT_TO_HYPHEN_PROVIDERS, causing all dotted model
names (minimax-m2.5-free, gpt-5.4, glm-5.1) to be mangled. The fix:

Layer 1 (model_normalize.py): Remove opencode-zen from the blanket
dot-to-hyphen set. Add an explicit block that preserves dots for
non-Claude models while keeping Claude hyphenated (Zen's Claude
endpoint uses anthropic_messages mode which expects hyphens).

Layer 2 (run_agent.py _anthropic_preserve_dots): Add opencode-zen and
zai to the provider allowlist. Broaden URL check from opencode.ai/zen/go
to opencode.ai/zen/ to cover both Go and Zen endpoints. Add bigmodel.cn
for ZAI URL detection.

Also adds glm-5.1 to ZAI model lists in models.py and setup.py.

Closes #7710

Salvaged from contributions by:
- konsisumer (PR #7739, #7719)
- DomGrieco (PR #8708)
- Esashiero (PR #7296)
- sharziki (PR #7497)
- XiaoYingGee (PR #8750)
- APTX4869-maker (PR #8752)
- kagura-agent (PR #7157)
This commit is contained in:
Teknium
2026-04-12 21:22:59 -07:00
committed by GitHub
parent a0cd2c5338
commit 83ca0844f7
6 changed files with 63 additions and 12 deletions

View File

@@ -308,6 +308,34 @@ class TestMinimaxPreserveDots:
from run_agent import AIAgent
assert AIAgent._anthropic_preserve_dots(agent) is False
def test_opencode_zen_provider_preserves_dots(self):
from types import SimpleNamespace
agent = SimpleNamespace(provider="opencode-zen", base_url="")
from run_agent import AIAgent
assert AIAgent._anthropic_preserve_dots(agent) is True
def test_opencode_zen_url_preserves_dots(self):
from types import SimpleNamespace
agent = SimpleNamespace(provider="custom", base_url="https://opencode.ai/zen/v1")
from run_agent import AIAgent
assert AIAgent._anthropic_preserve_dots(agent) is True
def test_zai_provider_preserves_dots(self):
from types import SimpleNamespace
agent = SimpleNamespace(provider="zai", base_url="")
from run_agent import AIAgent
assert AIAgent._anthropic_preserve_dots(agent) is True
def test_bigmodel_cn_url_preserves_dots(self):
from types import SimpleNamespace
agent = SimpleNamespace(provider="custom", base_url="https://open.bigmodel.cn/api/paas/v4")
from run_agent import AIAgent
assert AIAgent._anthropic_preserve_dots(agent) is True
def test_normalize_preserves_m25_free_dot(self):
from agent.anthropic_adapter import normalize_model_name
assert normalize_model_name("minimax-m2.5-free", preserve_dots=True) == "minimax-m2.5-free"
def test_normalize_preserves_m27_dot(self):
from agent.anthropic_adapter import normalize_model_name
assert normalize_model_name("MiniMax-M2.7", preserve_dots=True) == "MiniMax-M2.7"

View File

@@ -54,14 +54,19 @@ class TestAnthropicDotToHyphen:
# ── OpenCode Zen regression ────────────────────────────────────────────
class TestOpenCodeZenDotToHyphen:
"""OpenCode Zen follows Anthropic convention (dots→hyphens)."""
class TestOpenCodeZenModelNormalization:
"""OpenCode Zen preserves dots for most models, but Claude stays hyphenated."""
@pytest.mark.parametrize("model,expected", [
("claude-sonnet-4.6", "claude-sonnet-4-6"),
("glm-4.5", "glm-4-5"),
("opencode-zen/claude-opus-4.5", "claude-opus-4-5"),
("glm-4.5", "glm-4.5"),
("glm-5.1", "glm-5.1"),
("gpt-5.4", "gpt-5.4"),
("minimax-m2.5-free", "minimax-m2.5-free"),
("kimi-k2.5", "kimi-k2.5"),
])
def test_zen_converts_dots(self, model, expected):
def test_zen_normalizes_models(self, model, expected):
result = normalize_model_for_provider(model, "opencode-zen")
assert result == expected
@@ -69,6 +74,10 @@ class TestOpenCodeZenDotToHyphen:
result = normalize_model_for_provider("opencode-zen/claude-sonnet-4.6", "opencode-zen")
assert result == "claude-sonnet-4-6"
def test_zen_strips_vendor_prefix_for_non_claude(self):
result = normalize_model_for_provider("opencode-zen/glm-5.1", "opencode-zen")
assert result == "glm-5.1"
# ── Copilot dot preservation (regression) ──────────────────────────────