# NousResearch/hermes-agent · ZAI/GLM model name and auxiliary provider corrections

## Open now

- Nothing open on GitHub.

Several issues with GLM model names having dots stripped (causing 'model not found' errors) on ZAI and OpenCode Zen providers were addressed, along with corrections to how auxiliary vision models and provider overrides are handled.

## TL;DR

- 5 member(s): 0 open issue(s), 2 closed issue(s), 0 open PR(s), 1 merged PR(s), 2 PR(s) closed without merging.
- A fix was merged to main in PR #8794. Verify it addresses your case.
- **Recommended:** read PR #8794 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `unresolved`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.34`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #7475: \[Bug\]: Select Model GLM-4.7 get "提示模型不存在，请检查模型代码"
- #60381: bug: zai provider vision — glm-4v-flash returns 1211 '模型不存在' despite model working via direct API

## Prior & competing PRs

- **#8794: fix: preserve dots in model names for OpenCode Zen and ZAI providers**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/model_normalize.py`, `hermes_cli/models.py`, `hermes_cli/setup.py`, `run_agent.py`, `tests/agent/test_minimax_provider.py`, `tests/hermes_cli/test_model_normalize.py`
  - fix hunks:
  - `hermes_cli/model_normalize.py`

    ```diff
    @@ -8,8 +8,9 @@
       hyphens: ``claude-sonnet-4-6``.
     - **Copilot** expects bare names *with* dots preserved:
       ``claude-sonnet-4.6``.
    -- **OpenCode Zen** follows the same dot-to-hyphen convention as
    -  Anthropic: ``claude-sonnet-4-6``.
    +- **OpenCode Zen** preserves dots for GPT/GLM/Gemini/Kimi/MiniMax-style
    +  model IDs, but Claude still uses hyphenated native names like
    +  ``claude-sonnet-4-6``.
     - **OpenCode Go** preserves dots in model names: ``minimax-m2.7``.
     - **DeepSeek** only accepts two model identifiers:
       ``deepseek-chat`` and ``deepseek-reasoner``.
    ```

    ```diff
    @@ -67,7 +68,6 @@
     # Providers that want bare names with dots replaced by hyphens.
     _DOT_TO_HYPHEN_PROVIDERS: frozenset[str] = frozenset({
         "anthropic",
    -    "opencode-zen",
     })
     
     # Providers that want bare names with dots preserved.
    ```

    ```diff
    @@ -329,6 +329,9 @@ def normalize_model_for_provider(model_input: str, target_provider: str) -> str:
             >>> normalize_model_for_provider("claude-sonnet-4.6", "opencode-zen")
             'claude-sonnet-4-6'
     
    +        >>> normalize_model_for_provider("minimax-m2.5-free", "opencode-zen")
    +        'minimax-m2.5-free'
    +
             >>> normalize_model_for_provider("deepseek-v3", "deepseek")
             'deepseek-chat'
     
    ```

    ```diff
    @@ -351,7 +354,16 @@ def normalize_model_for_provider(model_input: str, target_provider: str) -> str:
         if provider in _AGGREGATOR_PROVIDERS:
             return _prepend_vendor(name)
     
    -    # --- Anthropic / OpenCode: strip matching provider prefix, dots -> hyphens ---
    +    # --- OpenCode Zen: Claude stays hyphenated; other models keep dots ---
    +    if provider == "opencode-zen":
    +        bare = _strip_matching_provider_prefix(name, provider)
    +        if "/" in bare:
    +            return bare
    +        if bare.lower().startswith("claude-"):
    +            return _dots_to_hyphens(bare)
    +        return bare
    +
    +    # --- Anthropic: strip matching provider prefix, dots -> hyphens ---
         if provider in _DOT_TO_HYPHEN_PROVIDERS:
             bare = _strip_matching_provider_prefix(name, provider)
             if "/" in bare:
    ```
  - `hermes_cli/models.py`

    ```diff
    @@ -130,6 +130,7 @@ def _codex_curated_models() -> list[str]:
             "gemma-4-26b-it",
         ],
         "zai": [
    +        "glm-5.1",
             "glm-5",
             "glm-5-turbo",
             "glm-4.7",
    ```
  - `hermes_cli/setup.py`

    ```diff
    @@ -104,7 +104,7 @@ def _supports_same_provider_pool_setup(provider: str) -> bool:
             "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite",
             "gemma-4-31b-it", "gemma-4-26b-it",
         ],
    -    "zai": ["glm-5", "glm-4.7", "glm-4.5", "glm-4.5-flash"],
    +    "zai": ["glm-5.1", "glm-5", "glm-4.7", "glm-4.5", "glm-4.5-flash"],
         "kimi-coding": ["kimi-k2.5", "kimi-k2-thinking", "kimi-k2-turbo-preview"],
         "minimax": ["MiniMax-M2.7", "MiniMax-M2.5", "MiniMax-M2.1", "MiniMax-M2"],
         "minimax-cn": ["MiniMax-M2.7", "MiniMax-M2.5", "MiniMax-M2.1", "MiniMax-M2"],
    ```
  - `run_agent.py`

    ```diff
    @@ -5856,11 +5856,12 @@ def _anthropic_preserve_dots(self) -> bool:
             """True when using an anthropic-compatible endpoint that preserves dots in model names.
             Alibaba/DashScope keeps dots (e.g. qwen3.5-plus).
             MiniMax keeps dots (e.g. MiniMax-M2.7).
    -        OpenCode Go keeps dots (e.g. minimax-m2.7)."""
    -        if (getattr(self, "provider", "") or "").lower() in {"alibaba", "minimax", "minimax-cn", "opencode-go"}:
    +        OpenCode Go/Zen keeps dots for non-Claude models (e.g. minimax-m2.5-free).
    +        ZAI/Zhipu keeps dots (e.g. glm-4.7, glm-5.1)."""
    +        if (getattr(self, "provider", "") or "").lower() in {"alibaba", "minimax", "minimax-cn", "opencode-go", "opencode-zen", "zai"}:
                 return True
             base = (getattr(self, "base_url", "") or "").lower()
    -        return "dashscope" in base or "aliyuncs" in base or "minimax" in base or "opencode.ai/zen/go" in base
    +        return "dashscope" in base or "aliyuncs" in base or "minimax" in base or "opencode.ai/zen/" in base or "bigmodel.cn" in base
     
         def _is_qwen_portal(self) -> bool:
             """Return True when the base URL targets Qwen Portal."""
    ```
  - `tests/agent/test_minimax_provider.py`

    ```diff
    @@ -308,6 +308,34 @@ def test_anthropic_does_not_preserve_dots(self):
             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"
    ```
  - `tests/hermes_cli/test_model_normalize.py`

    ```diff
    @@ -54,14 +54,19 @@ def test_anthropic_strips_vendor_prefix(self):
     
     # ── 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
     
    ```

    ```diff
    @@ -69,6 +74,10 @@ 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"
     
    +    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) ──────────────────────────────
     
    ```
- **#7719: fix(zai): preserve dots in GLM model names on Anthropic-compatible endpoints**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#8794`
- **#60967: fix(auxiliary): respect explicit auxiliary.vision.model over _PROVIDER_VISION_MODELS mapping**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#60381`, `#60381`

## Fix status

- Merged to main in PR #8794 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-04-13`. This confirmation is old and may be out of date (a revert/reopen would not have invalidated it here); re-verify.
- Subsystem: `agent`
