Files
hermes-agent/tests/test_model_normalize.py
ClintonEmok f77be22c65 Fix #5211: Preserve dots in OpenCode Go model names
OpenCode Go model names with dots (minimax-m2.7, glm-4.5, kimi-k2.5)
were being mangled to hyphens (minimax-m2-7), causing HTTP 401 errors.

Two code paths were affected:
1. model_normalize.py: opencode-go was incorrectly in DOT_TO_HYPHEN_PROVIDERS
2. run_agent.py: _anthropic_preserve_dots() did not check for opencode-go

Fix:
- Remove opencode-go from _DOT_TO_HYPHEN_PROVIDERS (dots are correct for Go)
- Add opencode-go to _anthropic_preserve_dots() provider check
- Add opencode.ai/zen/go to base_url fallback check
- Add regression tests in tests/test_model_normalize.py

Co-authored-by: jacob3712 <jacob3712@users.noreply.github.com>
2026-04-06 11:25:06 -07:00

117 lines
4.5 KiB
Python

"""Tests for hermes_cli.model_normalize — provider-aware model name normalization.
Covers issue #5211: opencode-go model names with dots (e.g. minimax-m2.7)
must NOT be mangled to hyphens (minimax-m2-7).
"""
import pytest
from hermes_cli.model_normalize import (
normalize_model_for_provider,
_DOT_TO_HYPHEN_PROVIDERS,
_AGGREGATOR_PROVIDERS,
detect_vendor,
)
# ── Regression: issue #5211 ────────────────────────────────────────────
class TestIssue5211OpenCodeGoDotPreservation:
"""OpenCode Go model names with dots must pass through unchanged."""
@pytest.mark.parametrize("model,expected", [
("minimax-m2.7", "minimax-m2.7"),
("minimax-m2.5", "minimax-m2.5"),
("glm-4.5", "glm-4.5"),
("kimi-k2.5", "kimi-k2.5"),
("some-model-1.0.3", "some-model-1.0.3"),
])
def test_opencode_go_preserves_dots(self, model, expected):
result = normalize_model_for_provider(model, "opencode-go")
assert result == expected, f"Expected {expected!r}, got {result!r}"
def test_opencode_go_not_in_dot_to_hyphen_set(self):
"""opencode-go must NOT be in the dot-to-hyphen provider set."""
assert "opencode-go" not in _DOT_TO_HYPHEN_PROVIDERS
# ── Anthropic dot-to-hyphen conversion (regression) ────────────────────
class TestAnthropicDotToHyphen:
"""Anthropic API still needs dots→hyphens."""
@pytest.mark.parametrize("model,expected", [
("claude-sonnet-4.6", "claude-sonnet-4-6"),
("claude-opus-4.5", "claude-opus-4-5"),
])
def test_anthropic_converts_dots(self, model, expected):
result = normalize_model_for_provider(model, "anthropic")
assert result == expected
def test_anthropic_strips_vendor_prefix(self):
result = normalize_model_for_provider("anthropic/claude-sonnet-4.6", "anthropic")
assert result == "claude-sonnet-4-6"
# ── OpenCode Zen regression ────────────────────────────────────────────
class TestOpenCodeZenDotToHyphen:
"""OpenCode Zen follows Anthropic convention (dots→hyphens)."""
@pytest.mark.parametrize("model,expected", [
("claude-sonnet-4.6", "claude-sonnet-4-6"),
("glm-4.5", "glm-4-5"),
])
def test_zen_converts_dots(self, model, expected):
result = normalize_model_for_provider(model, "opencode-zen")
assert result == expected
def test_zen_strips_vendor_prefix(self):
result = normalize_model_for_provider("opencode-zen/claude-sonnet-4.6", "opencode-zen")
assert result == "claude-sonnet-4-6"
# ── Copilot dot preservation (regression) ──────────────────────────────
class TestCopilotDotPreservation:
"""Copilot preserves dots in model names."""
@pytest.mark.parametrize("model,expected", [
("claude-sonnet-4.6", "claude-sonnet-4.6"),
("gpt-5.4", "gpt-5.4"),
])
def test_copilot_preserves_dots(self, model, expected):
result = normalize_model_for_provider(model, "copilot")
assert result == expected
# ── Aggregator providers (regression) ──────────────────────────────────
class TestAggregatorProviders:
"""Aggregators need vendor/model slugs."""
def test_openrouter_prepends_vendor(self):
result = normalize_model_for_provider("claude-sonnet-4.6", "openrouter")
assert result == "anthropic/claude-sonnet-4.6"
def test_nous_prepends_vendor(self):
result = normalize_model_for_provider("gpt-5.4", "nous")
assert result == "openai/gpt-5.4"
def test_vendor_already_present(self):
result = normalize_model_for_provider("anthropic/claude-sonnet-4.6", "openrouter")
assert result == "anthropic/claude-sonnet-4.6"
# ── detect_vendor ──────────────────────────────────────────────────────
class TestDetectVendor:
@pytest.mark.parametrize("model,expected", [
("claude-sonnet-4.6", "anthropic"),
("gpt-5.4-mini", "openai"),
("minimax-m2.7", "minimax"),
("glm-4.5", "z-ai"),
("kimi-k2.5", "moonshotai"),
])
def test_detects_known_vendors(self, model, expected):
assert detect_vendor(model) == expected