# NousResearch/hermes-agent · DeepSeek V4 and Ollama Cloud model ID fixes

## Open now

- 3 issues, 3 PRs open on GitHub.
- #12988: \[How-to\] Volcengine Coding Plan configuration + model_normalize bug with vendor-prefixed model IDs
- #17199: deepseek provider: model normalization and base_url override break custom endpoints (e.g. Volcengine ARK)
- #45137: ollama-cloud: validate_requested_model rejects bare model IDs like 'deepseek-v4-flash' because normalize_model_for_provider doesn't strip :cloud/-cloud suffix
- PR #17217: fix: pass through Volcengine ARK endpoint IDs (ep-*) in DeepSeek normalization
- PR #19529: feat(models): add Alibaba qwen3.6-flash
- PR #45163: fix(models): strip :cloud/-cloud suffix for ollama-cloud model IDs

These items cover adding DeepSeek V4 model identifiers and pricing, fixing normalization bugs that silently fold V4 IDs to wrong names or break custom endpoints like Volcengine ARK, and addressing Ollama Cloud issues where :cloud/-cloud suffixes cause invalid model IDs.

## TL;DR

- 17 member(s): 3 open issue(s), 5 closed issue(s), 3 open PR(s), 2 merged PR(s), 4 PR(s) closed without merging.
- A fix was merged to main in PR #19881. Verify it addresses your case.
- **Recommended:** read PR #19881 (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.09`: 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.

## Open related issues

- #12988: \[How-to\] Volcengine Coding Plan configuration + model_normalize bug with vendor-prefixed model IDs
- #17199: deepseek provider: model normalization and base_url override break custom endpoints (e.g. Volcengine ARK)
- #45137: ollama-cloud: validate_requested_model rejects bare model IDs like 'deepseek-v4-flash' because normalize_model_for_provider doesn't strip :cloud/-cloud suffix

## Closed / duplicate issues

- #14902: \[Feature\]: support for deepseek-v4-pro model
- #15936: \[特点\]Native support for DeepSeek V4 series (deepseek-v4-pro / flash)
- #16160: deepseek-v4-pro model name gets normalized to deepseek-chat silently
- #16179: \[Bug\]: Ollama Cloud model list shows wrong names with :cloud / -cloud suffix (e.g. kimi-k2.6:cloud), causing 400/404
- #19128: \[Feature\]: add \`qwen3.6-flash\`, \`deepseek-v4-flash\`, \`deepseek-v4-pro\` for Alibaba provider

## Prior & competing PRs

- **#15123: fix(model-normalize): pass DeepSeek V-series IDs through instead of folding to deepseek-chat**. merged to main · fix tier `suggested`
  - maintainer direction: category `merged`; implication `superseded`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#19881: fix(models): strip :cloud/-cloud suffix from models.dev Ollama Cloud IDs**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/models.py`, `tests/hermes_cli/test_ollama_cloud_provider.py`
  - fix hunks:
  - `hermes_cli/models.py`

    ```diff
    @@ -2906,6 +2906,19 @@ def fetch_api_models(
     _OLLAMA_CLOUD_CACHE_TTL = 3600  # 1 hour
     
     
    +def _strip_ollama_cloud_suffix(model_id: str) -> str:
    +    """Strip :cloud / -cloud suffixes that models.dev appends to Ollama Cloud IDs.
    +
    +    The live API uses clean IDs (e.g. 'kimi-k2.6') while models.dev sometimes
    +    returns them as 'kimi-k2.6:cloud'. Normalising before the dedup merge
    +    prevents duplicate entries in the merged model list.
    +    """
    +    for suffix in (":cloud", "-cloud"):
    +        if model_id.endswith(suffix):
    +            return model_id[: -len(suffix)]
    +    return model_id
    +
    +
     def _ollama_cloud_cache_path() -> Path:
         """Return the path for the Ollama Cloud model cache."""
         from hermes_constants import get_hermes_home
    ```

    ```diff
    @@ -3001,9 +3014,10 @@ def fetch_ollama_cloud_models(
                     seen.add(m)
                     merged.append(m)
             for m in mdev_models:
    -            if m and m not in seen:
    -                seen.add(m)
    -                merged.append(m)
    +            normalized = _strip_ollama_cloud_suffix(m)
    +            if normalized and normalized not in seen:
    +                seen.add(normalized)
    +                merged.append(normalized)
             if merged:
                 _save_ollama_cloud_cache(merged)
                 return merged
    ```
  - `tests/hermes_cli/test_ollama_cloud_provider.py`

    ```diff
    @@ -401,6 +401,103 @@ def test_get_provider(self):
             assert pdef.transport == "openai_chat"
     
     
    +# ── Cloud Suffix Stripping ──
    +
    +class TestOllamaCloudSuffixStripping:
    +    """models.dev appends :cloud / -cloud suffixes that the live API omits.
    +
    +    fetch_ollama_cloud_models() must normalise these before the dedup merge so
    +    users never see broken IDs like 'kimi-k2.6:cloud' in the model picker.
    +    """
    +
    +    def test_strips_colon_cloud_suffix(self, tmp_path, monkeypatch):
    +        """:cloud suffix from models.dev is stripped before merge."""
    +        from hermes_cli.models import fetch_ollama_cloud_models
    +
    +        monkeypatch.setenv("HERMES_HOME", str(tmp_path))
    +        monkeypatch.delenv("OLLAMA_API_KEY", raising=False)
    +
    +        mock_mdev = {
    +            "ollama-cloud": {
    +                "models": {"kimi-k2.6:cloud": {"tool_call": True}}
    +            }
    +        }
    +        with patch("agent.models_dev.fetch_models_dev", return_value=mock_mdev):
    +            result = fetch_ollama_cloud_models(force_refresh=True)
    +
    +        assert "kimi-k2.6" in result
    +        assert "kimi-k2.6:cloud" not in result
    +
    +    def test_strips_dash_cloud_suffix(self, tmp_path, monkeypatch):
    +        """-cloud suffix from models.dev is stripped before merge."""
    +        from hermes_cli.models import fetch_ollama_cloud_models
    +
    +        monkeypatch.setenv("HERMES_HOME", str(tmp_path))
    +        monkeypatch.delenv("OLLAMA_API_KEY", raising=False)
    +
    +        mock_mdev = {
    +            "ollama-cloud": {
    +                "models": {"qwen3-coder:480b-cloud": {"tool_call": True}}
    +            }
    +        }
    +        with patch("agent.models_dev.fetch_models_dev", return_value=mock_mdev):
    +            result = fetch_ollama_cloud_models(force_refresh=True)
    +
    +        assert "qwen3-coder:480b" in result
    +        assert "qwen3-coder:480b-cloud" not in result
    +
    +    def test_no_duplicate_when_live_clean_and_mdev_suffixed(self, tmp_path, monkeypatch):
    +        """Live API returns clean ID; mdev has :cloud variant — result has exactly one entry."""
    +        from hermes_cli.models import fetch_ollama_cloud_models
    +
    +        monkeypatch.setenv("HERMES_HOME", str(tmp_path))
    +        monkeypatch.setenv("OLLAMA_API_KEY", "test-key")
    +
    +        mock_mdev = {
    +            "ollama-cloud": {
    +                "models": {
    +                    "kimi-k2.6:cloud": {"tool_call": True},
    +                    "glm-5.1:cloud": {"tool_call": True},
    +                }
    +            }
    +        }
    +        with patch("hermes_cli.models.fetch_api_models", return_value=["kimi-k2.6", "glm-5.1"]), \
    +             patch("agent.models_dev.fetch_models_dev", return_value=mock_mdev):
    +            result = fetch_ollama_cloud_models(force_refresh=True)
    +
    +        assert result.count("kimi-k2.6") == 1
    +        assert result.count("glm-5.1") == 1
    +        assert "kimi-k2.6:cloud" not in result
    +        assert "glm-5.1:cloud" not in result
    +
    +    def test_unsuffixed_model_id_unchanged(self, tmp_path, monkeypatch):
    +        """Model IDs without :cloud / -cloud suffix are passed through unchanged."""
    +        from hermes_cli.models import fetch_ollama_cloud_models
    +
    +        monkeypatch.setenv("HERMES_HOME", str(tmp_path))
    +        monkeypatch.delenv("OLLAMA_API_KEY", raising=False)
    +
    +        mock_mdev = {
    +            "ollama-cloud": {
    +                "models": {"nemotron-3-nano:30b": {"tool_call": True}}
    +            }
    +        }
    +        with patch("agent.models_dev.fetch_models_dev", return_value=mock_mdev):
    +            result = fetch_ollama_cloud_models(force_refresh=True)
    +
    +        assert "nemotron-3-nano:30b" in result
    +
    +    def test_strip_suffix_helper(self):
    +        """Unit test for the _strip_ollama_cloud_suffix helper."""
    +        from hermes_cli.models import _strip_ollama_cloud_suffix
    +
    +        assert _strip_ollama_cloud_suffix("kimi-k2.6:cloud") == "kimi-k2.6"
    +        assert _strip_ollama_cloud_suffix("glm-5.1:cloud") == "glm-5.1"
    +        assert _strip_ollama_cloud_suffix("qwen3-coder:480b-cloud") == "qwen3-coder:480b"
    +        assert _strip_ollama_cloud_suffix("nemotron-3-nano:30b") == "nemotron-3-nano:30b"
    +        assert _strip_ollama_cloud_suffix("") == ""
    +
    +
     # ── Auxiliary Model ──
     
     class TestOllamaCloudAuxiliary:
    ```
- **#14891: feat: add DeepSeek V4 model identifiers**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#14946`, `#14934`
- **#14911: feat(models): add deepseek-v4-pro to DeepSeek provider catalog**. closed without merge
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#14891`
- **#16192: fix(models): strip :cloud/-cloud suffix from models.dev Ollama Cloud IDs**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#19881`
- **#16196: fix(cli): implement self-healing sanitization for Ollama Cloud model IDs**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#19881`, `#19881`
- **#17217: fix: pass through Volcengine ARK endpoint IDs (ep-*) in DeepSeek normalization**. open
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - files: `hermes_cli/model_normalize.py`
- **#19529: feat(models): add Alibaba qwen3.6-flash**. open
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - files: `agent/model_metadata.py`, `hermes_cli/models.py`, `tests/agent/test_model_metadata.py`, `tests/hermes_cli/test_models.py`
- **#45163: fix(models): strip :cloud/-cloud suffix for ollama-cloud model IDs**. open
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - files: `hermes_cli/model_normalize.py`, `hermes_cli/models.py`, `tests/hermes_cli/test_ollama_cloud_suffix_strip.py`

## Fix status

- Merged to main in PR #19881 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-04`.
- Subsystem: `agent`
