fix(provider): route api.anthropic.com to anthropic_messages (#32243) - #56226
Closed
teknium1 wants to merge 6 commits into
Closed
fix(provider): route api.anthropic.com to anthropic_messages (#32243)#56226teknium1 wants to merge 6 commits into
teknium1 wants to merge 6 commits into
Conversation
…#32243) `_detect_api_mode_for_url` previously returned `None` for the bare `api.anthropic.com` host, causing every URL-fallback path (custom_providers, direct-alias, the api-key fallback inside `resolve_runtime_provider`) to default to `chat_completions` for native Anthropic — which routes requests to the OpenAI-compat `/chat/completions` shim instead of the native `/v1/messages` endpoint. Pro/Max OAuth subscriptions are only billed against the native Messages API; the shim bills against a separate "extra usage" pool that is empty by default, so a freshly authorized Pro/Max credential 400s with "You're out of extra usage" the moment it's used — even on an account that has consumed nothing for the current cycle. Brings the helper in line with `hermes_cli.providers.determine_api_mode` which already mapped `api.anthropic.com` to `anthropic_messages`.
Add a dedicated `TestDirectAnthropicHost` class to `test_detect_api_mode_for_url.py` covering the native Anthropic host shape (bare, trailing slash, /v1 suffix, uppercase host) plus the two negative-space regressions that matter for security: lookalike subdomains (`api.anthropic.com.attacker.test`) and path-segment spoofing (`https://proxy.example.test/api.anthropic.com/v1`) must NOT be classified as native — leaking an Anthropic OAuth token to either would be the worst case. Refs #32243.
…ime branches End-to-end regression coverage for #32243 that asserts every runtime branch resolving an Anthropic endpoint returns `api_mode == "anthropic_messages"`: * `_resolve_explicit_runtime` — the path used when a Hermes subcommand passes an explicit `--api-key` / `--base-url`. Pins that a stale persisted `model.api_mode: chat_completions` from a prior provider migration cannot override the anthropic pin. * `_resolve_runtime_from_pool_entry` — the path triggered by `hermes auth add anthropic --type oauth` (the exact flow from the issue). Same stale-api_mode regression pinned here. * `_try_resolve_from_custom_pool` — the user-defined `providers:` / `custom_providers:` path that depends on the URL detector fix landed in the prior commit. Asserts both the detector fallback fires for `api.anthropic.com` and that an explicit `api_mode_override` still wins (so users who DELIBERATELY pointed a chat_completions transport at api.anthropic.com for OpenAI-compat experiments aren't hijacked). Co-locates the three contracts so a future refactor of one branch cannot silently diverge from the others and re-introduce the "out of extra usage" 400 on fresh OAuth Pro/Max credentials.
…ction Widen the salvaged #32243 fix to the try_activate_fallback path: a custom provider pointed at the native api.anthropic.com host (no /anthropic path suffix, name != anthropic) fell through to chat_completions -> POST /v1/chat/completions -> 404. Match the host the same way determine_api_mode() and _detect_api_mode_for_url() now do. Absorbs #49247.
Pins that a custom provider on the native api.anthropic.com host resolves to anthropic_messages on the try_activate_fallback path. From #49247.
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
_detect_api_mode_for_urlnow returnsanthropic_messagesfor the nativeapi.anthropic.comhost, realigning it with its siblingproviders.determine_api_mode— which already maps that host. Previously the two helpers disagreed, so any URL-fallback path resolving a base_url ofapi.anthropic.comdefaulted tochat_completions→POST /v1/chat/completions, the OpenAI-compat shim that bills OAuth Pro/Max against a separate "extra usage" pool (surfacing as the misleading400 "out of extra usage").Root cause:
_detect_api_mode_for_urlonly matched the third-party/anthropicpath suffix (MiniMax, Zhipu GLM, LiteLLM proxies) andapi.kimi.com/coding; the direct Anthropic host fell through toNone.Salvage of #32318 (@xxxigm), widened to also cover the
try_activate_fallbacksibling call site (#49247, @x9x9x9x9x9x91) so the whole bug class is fixed, not just one path. Both bug sites confirmed live on currentmain.Changes
hermes_cli/runtime_provider.py:_detect_api_mode_for_urlmapshostname == "api.anthropic.com"→anthropic_messages. Exact-hostname match (base_url_hostname) rejects lookalike subdomains and path-segment spoofing.agent/chat_completion_helpers.py:try_activate_fallbackmatches the same native host, so a custom fallback provider onapi.anthropic.comno longer 404s on/v1/chat/completions.test_detect_api_mode_for_url.py(host shapes + spoof rejection),test_anthropic_oauth_routes_to_messages_api.py(pins all runtime branches +api_mode_overrideprecedence),test_provider_fallback.py(fallback path).scripts/release.py: AUTHOR_MAP entries for both contributors.Scope note
The canonical OAuth pool path (
provider: anthropic) already setsanthropic_messagesin_resolve_runtime_from_pool_entry. This bug only bit custom-provider / direct-alias / api-key-fallback configs pointing atapi.anthropic.com. Complementary to #56128 (which classifies the "out of extra usage" 400 as billing) — that's error classification, this is routing.Validation
_detect_api_mode_for_url("https://api.anthropic.com")None→chat_completionsanthropic_messagesapi.anthropic.com.attacker.testproxy.test/api.anthropic.com/v1Supersedes stale-based #56214. Cherry-picked onto current
main, contributor authorship preserved per-commit.Infographic