From 2303dd8686b8a2f317f9763bb3e0756fe7c036f3 Mon Sep 17 00:00:00 2001 From: H-Ali13381 Date: Sun, 19 Apr 2026 12:34:10 -0400 Subject: [PATCH] fix(models): use Anthropic-native headers for model validation The generic /v1/models probe in validate_requested_model() sent a plain 'Authorization: Bearer ' header, which works for OpenAI-compatible endpoints but results in a 401 Unauthorized from Anthropic's API. Anthropic requires x-api-key + anthropic-version headers (or Bearer for OAuth tokens from Claude Code). Add a provider-specific branch for normalized == 'anthropic' that calls the existing _fetch_anthropic_models() helper, which already handles both regular API keys and Claude Code OAuth tokens correctly. This mirrors the pattern already used for openai-codex, copilot, and bedrock. The branch also includes: - fuzzy auto-correct (cutoff 0.9) for near-exact model ID typos - fuzzy suggestions (cutoff 0.5) when the model is not listed - graceful fall-through when the token cannot be resolved or the network is unreachable (accepts with a warning rather than hard-fail) - a note that newer/preview/snapshot model IDs can be gate-listed and may still work even if not returned by /v1/models Fixes Anthropic provider users seeing 'service unreachable' errors when running /model because every probe 401'd. --- hermes_cli/models.py | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/hermes_cli/models.py b/hermes_cli/models.py index 72a4223b7..d0853bd31 100644 --- a/hermes_cli/models.py +++ b/hermes_cli/models.py @@ -2656,6 +2656,51 @@ def validate_requested_model( ), } + # Native Anthropic provider: /v1/models requires x-api-key (or Bearer for + # OAuth) plus anthropic-version headers. The generic OpenAI-style probe + # below uses plain Bearer auth and 401s against Anthropic, so dispatch to + # the native fetcher which handles both API keys and Claude-Code OAuth + # tokens. (The api_mode=="anthropic_messages" branch below handles the + # Messages-API transport case separately.) + if normalized == "anthropic": + anthropic_models = _fetch_anthropic_models() + if anthropic_models is not None: + if requested_for_lookup in set(anthropic_models): + return { + "accepted": True, + "persist": True, + "recognized": True, + "message": None, + } + auto = get_close_matches(requested_for_lookup, anthropic_models, n=1, cutoff=0.9) + if auto: + return { + "accepted": True, + "persist": True, + "recognized": True, + "corrected_model": auto[0], + "message": f"Auto-corrected `{requested}` → `{auto[0]}`", + } + suggestions = get_close_matches(requested, anthropic_models, n=3, cutoff=0.5) + suggestion_text = "" + if suggestions: + suggestion_text = "\n Similar models: " + ", ".join(f"`{s}`" for s in suggestions) + # Accept anyway — Anthropic sometimes gates newer/preview models + # (e.g. snapshot IDs, early-access releases) behind accounts + # even though they aren't listed on /v1/models. + return { + "accepted": True, + "persist": True, + "recognized": False, + "message": ( + f"Note: `{requested}` was not found in Anthropic's /v1/models listing. " + f"It may still work if you have early-access or snapshot IDs." + f"{suggestion_text}" + ), + } + # _fetch_anthropic_models returned None — no token resolvable or + # network failure. Fall through to the generic warning below. + # Anthropic Messages API: many proxies don't implement /v1/models. # Try probing with correct auth; if it fails, accept with a warning. if api_mode == "anthropic_messages":