Skip to content

fix(provider): route api.anthropic.com to anthropic_messages (#32243) - #56226

Closed
teknium1 wants to merge 6 commits into
mainfrom
salvage/32318-anthropic-host-clean
Closed

fix(provider): route api.anthropic.com to anthropic_messages (#32243)#56226
teknium1 wants to merge 6 commits into
mainfrom
salvage/32318-anthropic-host-clean

Conversation

@teknium1

@teknium1 teknium1 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

_detect_api_mode_for_url now returns anthropic_messages for the native api.anthropic.com host, realigning it with its sibling providers.determine_api_mode — which already maps that host. Previously the two helpers disagreed, so any URL-fallback path resolving a base_url of api.anthropic.com defaulted to chat_completionsPOST /v1/chat/completions, the OpenAI-compat shim that bills OAuth Pro/Max against a separate "extra usage" pool (surfacing as the misleading 400 "out of extra usage").

Root cause: _detect_api_mode_for_url only matched the third-party /anthropic path suffix (MiniMax, Zhipu GLM, LiteLLM proxies) and api.kimi.com/coding; the direct Anthropic host fell through to None.

Salvage of #32318 (@xxxigm), widened to also cover the try_activate_fallback sibling call site (#49247, @x9x9x9x9x9x91) so the whole bug class is fixed, not just one path. Both bug sites confirmed live on current main.

Changes

  • hermes_cli/runtime_provider.py: _detect_api_mode_for_url maps hostname == "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_fallback matches the same native host, so a custom fallback provider on api.anthropic.com no longer 404s on /v1/chat/completions.
  • Tests: 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_override precedence), 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 sets anthropic_messages in _resolve_runtime_from_pool_entry. This bug only bit custom-provider / direct-alias / api-key-fallback configs pointing at api.anthropic.com. Complementary to #56128 (which classifies the "out of extra usage" 400 as billing) — that's error classification, this is routing.

Validation

Before After
_detect_api_mode_for_url("https://api.anthropic.com") Nonechat_completions anthropic_messages
api.anthropic.com.attacker.test rejected (not native)
proxy.test/api.anthropic.com/v1 rejected (path-segment spoof)
Targeted tests 53/53 pass

Supersedes stale-based #56214. Cherry-picked onto current main, contributor authorship preserved per-commit.

Infographic

Anthropic OAuth routing fix

xxxigm and others added 6 commits July 1, 2026 02:11
…#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.
@teknium1

teknium1 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Closing as redundant — #56214 rebase-merged the identical 6 commits onto current main first (a344c92..522a5e9), with contributor authorship preserved. This PR was a re-cherry-pick prepared in parallel; the fix is already live.

@teknium1 teknium1 closed this Jul 1, 2026
@teknium1
teknium1 deleted the salvage/32318-anthropic-host-clean branch July 1, 2026 09:22
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard provider/anthropic Anthropic native Messages API duplicate This issue or pull request already exists labels Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists provider/anthropic Anthropic native Messages API type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants