fix(provider): route api.anthropic.com to anthropic_messages api_mode (#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`.
This commit is contained in:
xxxigm 2026-05-26 07:06:48 +07:00 committed by Teknium
parent f981d47cb0
commit a344c92050

View file

@ -93,6 +93,13 @@ def _detect_api_mode_for_url(base_url: str) -> Optional[str]:
- Direct api.openai.com endpoints need the Responses API for GPT-5.x
tool calls with reasoning (chat/completions returns 400).
- Direct api.anthropic.com endpoints must use the native Messages
API (``/v1/messages``). Anthropic also exposes an OpenAI-compat
``/chat/completions`` shim on the same host, but Pro/Max OAuth
subscriptions are only billed against the native Messages route;
hitting the shim accounts against a separate "extra usage" pool
that is empty by default and surfaces as HTTP 400 "You're out of
extra usage." See issue #32243.
- Third-party Anthropic-compatible gateways (MiniMax, Zhipu GLM,
LiteLLM proxies, etc.) conventionally expose the native Anthropic
protocol under a ``/anthropic`` suffix treat those as
@ -108,6 +115,12 @@ def _detect_api_mode_for_url(base_url: str) -> Optional[str]:
return "codex_responses"
if hostname == "api.openai.com":
return "codex_responses"
# Direct native Anthropic host: realign with providers.determine_api_mode,
# which already maps this host to anthropic_messages. The exact-hostname
# match rejects lookalike subdomains (api.anthropic.com.attacker.test) and
# path-segment spoofing (proxy.test/api.anthropic.com/v1). (#32243)
if hostname == "api.anthropic.com":
return "anthropic_messages"
path = urlparse(normalized).path.rstrip("/")
if path.endswith("/anthropic") or path.endswith("/anthropic/v1"):
return "anthropic_messages"