Dash vs dot notation in Anthropic model IDs across providers

NousResearch/hermes-agent · cluster 6879 · agent

Data as of

3 open items. Fix merged in #11561. Check it covers your case.

The short answer

2open issues
1open PR
8items grouped
Known?
Yes. 8 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #11561. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this partially resolved, deciding PR #11561. That is separate from the fix event.
Still open
2 issues and 1 PR.

Multiple providers (Copilot, OpenRouter, Nous) reject or misroute Anthropic model IDs when submitted in dash-notation (e.g. claude-opus-4-8) instead of dot-notation (e.g. claude-opus-4.8). The issues and accompanying fixes address normalization and aliasing of these ID formats across different provider integrations.

Open issues2

  • Issue#45362nous provider: dash-style model ids (anthropic/claude-sonnet-4-6) 404 where Nous needs dot-style (…4.6); catalog also lists chat-uncallable models (claude-fable-5) — opaque 'Couldn't find that, sorry.'
  • Issue#65152_find_openrouter_slug fails on Anthropic-native dash IDs (e.g. claude-opus-4-8), causing HTTP 400 not a valid model ID

Open PRs1

  • PR#45532fix: nous provider dash-vs-dot model ID format and uncallable models

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #11561 fix(copilot): normalize vendor-prefixed and dash-notation model IDs (#6879)

    merged to main

    files: hermes_cli/model_normalize.py, hermes_cli/models.py, tests/hermes_cli/test_model_normalize.py

    Show the diff
    • hermes_cli/model_normalize.py
      @@ -374,7 +374,26 @@ def normalize_model_for_provider(model_input: str, target_provider: str) -> str:
                   return bare
               return _dots_to_hyphens(bare)
       
      -    # --- Copilot: strip matching provider prefix, keep dots ---
      +    # --- Copilot / Copilot ACP: delegate to the Copilot-specific
      +    #     normalizer.  It knows about the alias table (vendor-prefix
      +    #     stripping for Anthropic/OpenAI, dash-to-dot repair for Claude)
      +    #     and live-catalog lookups.  Without this, vendor-prefixed or
      +    #     dash-notation Claude IDs survive to the Copilot API and hit
      +    #     HTTP 400 "model_not_supported".  See issue #6879.
      +    if provider in {"copilot", "copilot-acp"}:
      +        try:
      +            from hermes_cli.models import normalize_copilot_model_id
      +
      +            normalized = normalize_copilot_model_id(name)
      +            if normalized:
      +                return normalized
      +        except Exception:
      +            # Fall through to the generic strip-vendor behaviour below
      +            # if the Copilot-specific path is unavailable for any reason.
      +            pass
      +
      +    # --- Copilot / Copilot ACP / openai-codex fallback:
      +    #     strip matching provider prefix, keep dots ---
           if provider in _STRIP_VENDOR_ONLY_PROVIDERS:
               stripped = _strip_matching_provider_prefix(name, provider)
               if stripped == name and name.startswith("openai/"):
    • hermes_cli/models.py
      @@ -1488,6 +1488,19 @@ def _fetch_github_models(api_key: [redacted:secret-assignment] = None, timeout: float = 5.0) ->
           "anthropic/claude-sonnet-4.6": "claude-sonnet-4.6",
           "anthropic/claude-sonnet-4.5": "claude-sonnet-4.5",
           "anthropic/claude-haiku-4.5": "claude-haiku-4.5",
      +    # Dash-notation fallbacks: Hermes' default Claude IDs elsewhere use
      +    # hyphens (anthropic native format), but Copilot's API only accepts
      +    # dot-notation.  Accept both so users who configure copilot + a
      +    # default hyphenated Claude model don't hit HTTP 400
      +    # "model_not_supported".  See issue #6879.
      +    "claude-opus-4-6": "claude-opus-4.6",
      +    "claude-sonnet-4-6": "claude-sonnet-4.6",
      +    "claude-sonnet-4-5": "claude-sonnet-4.5",
      +    "claude-haiku-4-5": "claude-haiku-4.5",
      +    "anthropic/claude-opus-4-6": "claude-opus-4.6",
      +    "anthropic/claude-sonnet-4-6": "claude-sonnet-4.6",
      +    "anthropic/claude-sonnet-4-5": "claude-sonnet-4.5",
      +    "anthropic/claude-haiku-4-5": "claude-haiku-4.5",
       }
       
       
    • tests/hermes_cli/test_model_normalize.py
      @@ -93,6 +93,59 @@ def test_copilot_preserves_dots(self, model, expected):
               assert result == expected
       
       
      +# ── Copilot model-name normalization (issue #6879 regression) ──────────
      +
      +class TestCopilotModelNormalization:
      +    """Copilot requires bare dot-notation model IDs.
      +
      +    Regression coverage for issue #6879 and the broken Copilot branch
      +    that previously left vendor-prefixed Anthropic IDs (e.g.
      +    ``anthropic/claude-sonnet-4.6``) and dash-notation Claude IDs (e.g.
      +    ``claude-sonnet-4-6``) unchanged, causing the Copilot API to reject
      +    the request with HTTP 400 "model_not_supported".
      +    """
      +
      +    @pytest.mark.parametrize("model,expected", [
      +        # Vendor-prefixed Anthropic IDs — prefix must be stripped.
      +        ("anthropic/claude-opus-4.6",   "claude-opus-4.6"),
      +        ("anthropic/claude-sonnet-4.6", "claude-sonnet-4.6"),
      +        ("anthropic/claude-sonnet-4.5", "claude-sonnet-4.5"),
      +        ("anthropic/claude-haiku-4.5",  "claude-haiku-4.5"),
      +        # Vendor-prefixed OpenAI IDs — prefix must be stripped.
      +        ("openai/gpt-5.4",              "gpt-5.4"),
      +        ("openai/gpt-4o",               "gpt-4o"),
      +        ("openai/gpt-4o-mini",          "gpt-4o-mini"),
      +        # Dash-notation Claude IDs — must be converted to dot-notation.
      +        ("claude-opus-4-6",             "claude-opus-4.6"),
      +        ("claude-sonnet-4-6",           "claude-sonnet-4.6"),
      +        ("claude-sonnet-4-5",           "claude-sonnet-4.5"),
      +        ("claude-haiku-4-5",            "claude-haiku-4.5"),
      +        # Combined: vendor-prefixed + dash-notation.
      +        ("anthropic/claude-opus-4-6",   "claude-opus-4.6"),
      +        ("anthropic/claude-sonnet-4-6", "claude-sonnet-4.6"),
      +        # Already-canonical inputs pass through unchanged.
      +        ("claude-sonnet-4.6",           "claude-sonnet-4.6"),
      +        ("gpt-5.4",                     "gpt-5.4"),
      +        ("gpt-5-mini",                  "gpt-5-mini"),
      +    ])
      +    def test_copilot_normalization(self, model, expected):
      +        assert normalize_model_for_provider(model, "copilot") == expected
      +
      +    @pytest.mark.parametrize("model,expected", [
      +        ("anthropic/claude-sonnet-4.6", "claude-sonnet-4.6"),
      +        ("claude-sonnet-4-6",           "claude-sonnet-4.6"),
      +        ("claude-opus-4-6",             "claude-opus-4.6"),
      +        ("openai/gpt-5.4",              "gpt-5.4"),
      +    ])
      +    def test_copilot_acp_normalization(self, model, expected):
      +        """Copilot ACP shares the same API expectations as HTTP Copilot."""
      +        assert normalize_model_for_provider(model, "copilot-acp") == expected
      +
      +    def test_openai_codex_still_strips_openai_prefix(self):
      +        """Regression: openai-codex must still strip the openai/ prefix."""
      +        assert normalize_model_for_provider("openai/gpt-5.4", "openai-codex") == "gpt-5.4"
      +
      +
       # ── Aggregator providers (regression) ──────────────────────────────────
       
       class TestAggregatorProviders:

3 PRs closed without merging

From the close comments: done elsewhere 1 · duplicate 1 · merged via another change 1

  • #6743 fix(copilot): add dash-notation aliases for Claude model IDs · merged via another change
  • #45405 fix(models): normalize Anthropic dash-style IDs to dot-style for aggregator providers · duplicate → #45367
  • #65167 fix(models): resolve dash-form model IDs against dotted OpenRouter catalog (#65152) · done elsewhere → 148a625, #65152
Closed issues · 1
  • Closed#6879[Bug]: Copilot provider rejects Claude model names in dash-notation (HTTP 400)
How this cluster was grouped
  • Link evidence (partly structural links): 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.20: the share of item 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.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
agent
Merged PRs
#11561
Closed issues
1
Attempts
3 PRs closed without merging
Fix link checked
17 Apr 2026. This confirmation is old and may be out of date (a revert or reopen would not have invalidated it here); re-verify.

For your agent

The same facts, typed.