feat(messages): passthrough /v1/messages to native endpoints via supported_endpoints - #31685
Conversation
…orted_endpoints
The unified /v1/messages proxy endpoint always translated inbound Anthropic
requests down to /v1/chat/completions (or the Responses API for openai) when the
deployment's provider lacked a native Anthropic-messages config, dropping
Anthropic-only features like cache_control and thinking. Some customers run
OpenAI-compatible servers (self-hosted vLLM, DeepSeek's Anthropic endpoint, etc.)
that also natively expose /v1/messages and want the raw Anthropic payload
forwarded untranslated, while keeping provider openai so /v1/chat/completions to
the same deployment stays native.
Opt in per deployment via model_info.supported_endpoints containing
/v1/messages. When present, the gate routes to a generic, provider-agnostic
OpenAILikeAnthropicMessagesConfig that POSTs the Anthropic payload to
{api_base}/v1/messages with Bearer auth, instead of translating. Default
behavior is unchanged. Generalizes and supersedes the hosted_vllm-only,
env-var-toggled PR #28745.
Greptile SummaryThis PR adds an opt-in path for forwarding Anthropic
Confidence Score: 4/5The change is reasonably safe to merge because the new behavior is gated by explicit per-deployment configuration and existing translation behavior is preserved when the opt-in is absent. The implementation is focused and covered by tests for the new routing gate, payload preservation, URL construction, header defaults, and beta forwarding. Remaining risk is mainly around live upstream compatibility for openai-like providers that opt into the native messages path. No specific files require changes before merge.
What T-Rex did
Reviews (8): Last reviewed commit: "chore: remove accidentally committed loc..." | Re-trigger Greptile |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…hrough The OpenAI-like Anthropic passthrough config only checked for lowercase header names before injecting Bearer auth, anthropic-version, and content-type defaults. A caller sending standard-cased Authorization, Anthropic-Version, or Content-Type was treated as missing those headers, so LiteLLM added duplicate lowercase variants and overwrote the caller's credential/version at the HTTP layer. Header presence is now checked case-insensitively and the merge no longer mutates the caller dict. Also moves the feature docs out of the main repo (docs live in litellm-docs).
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Missing anthropic-beta header injection
- validate_anthropic_messages_environment now calls _update_headers_with_anthropic_beta after merging defaults, so opted-in features (context management, fast mode, structured outputs, advisor, tool search) get the required anthropic-beta values auto-injected.
- ✅ Fixed: Passthrough skips parent request prep
- Removed the bespoke transform_anthropic_messages_request override so the passthrough config inherits AnthropicMessagesConfig's request prep (advisor stripping, reasoning_effort mapping, legacy thinking translation, context_management normalization).
You can send follow-ups to the cloud agent here.
…thropic-beta headers The passthrough config bypassed the parent transform and skipped header beta injection. Both gaps cause native /v1/messages features (context management, advisor tool, fast mode, structured outputs, reasoning_effort, advisor stripping) to silently degrade on opted-in deployments. Reuse the parent's pipeline and call _update_headers_with_anthropic_beta after merging defaults
|
|
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Case-sensitive anthropic-beta merge
- Normalized any case variant of the anthropic-beta header key to lowercase before invoking _update_headers_with_anthropic_beta so existing caller beta flags are merged rather than duplicated.
You can send follow-ups to the cloud agent here.
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 107b468. Configure here.
ruff format --check requires the comprehension on one line (it fits within the 120 char limit); fixes the lint job failure on the bugbot autofix commit
1 similar comment
The shared anthropic_messages HTTP handler ran update_headers_with_filtered_beta with the deployment's custom_llm_provider after validate. For the native /v1/messages passthrough that provider is openai, which has no beta-header mapping, so every anthropic-beta value (caller-supplied or feature-derived for speed/context_management/etc.) was stripped to empty before the upstream request, breaking beta passthrough to the Anthropic-compatible endpoint. Beta filtering only makes sense on cross-provider translation paths where the upstream cannot understand Anthropic betas. Gate it on a new should_filter_anthropic_beta_headers() that defaults to True (bedrock, vertex_ai, native anthropic unchanged) and is overridden to False by OpenAILikeAnthropicMessagesConfig, whose upstream is a native Anthropic endpoint, so betas pass through verbatim.
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Local proxy debug logs committed
- Removed the accidentally committed proxy_after.log and proxy_before.log files from the repo root.
- ✅ Fixed: Local QA config committed
- Removed the accidentally committed qa_config.yaml from the repo root.
You can send follow-ups to the cloud agent here.
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 4c1ce65. Configure here.
…orted_endpoints (BerriAI#31685) * feat(messages): passthrough /v1/messages to native endpoints via supported_endpoints The unified /v1/messages proxy endpoint always translated inbound Anthropic requests down to /v1/chat/completions (or the Responses API for openai) when the deployment's provider lacked a native Anthropic-messages config, dropping Anthropic-only features like cache_control and thinking. Some customers run OpenAI-compatible servers (self-hosted vLLM, DeepSeek's Anthropic endpoint, etc.) that also natively expose /v1/messages and want the raw Anthropic payload forwarded untranslated, while keeping provider openai so /v1/chat/completions to the same deployment stays native. Opt in per deployment via model_info.supported_endpoints containing /v1/messages. When present, the gate routes to a generic, provider-agnostic OpenAILikeAnthropicMessagesConfig that POSTs the Anthropic payload to {api_base}/v1/messages with Bearer auth, instead of translating. Default behavior is unchanged. Generalizes and supersedes the hosted_vllm-only, env-var-toggled PR BerriAI#28745. * fix(messages): preserve standard-cased caller headers in native passthrough The OpenAI-like Anthropic passthrough config only checked for lowercase header names before injecting Bearer auth, anthropic-version, and content-type defaults. A caller sending standard-cased Authorization, Anthropic-Version, or Content-Type was treated as missing those headers, so LiteLLM added duplicate lowercase variants and overwrote the caller's credential/version at the HTTP layer. Header presence is now checked case-insensitively and the merge no longer mutates the caller dict. Also moves the feature docs out of the main repo (docs live in litellm-docs). * fix(openai_like/messages): delegate to parent transform and inject anthropic-beta headers The passthrough config bypassed the parent transform and skipped header beta injection. Both gaps cause native /v1/messages features (context management, advisor tool, fast mode, structured outputs, reasoning_effort, advisor stripping) to silently degrade on opted-in deployments. Reuse the parent's pipeline and call _update_headers_with_anthropic_beta after merging defaults * fix: normalize anthropic-beta header key case before beta injection * style: collapse anthropic-beta header normalization to single line ruff format --check requires the comprehension on one line (it fits within the 120 char limit); fixes the lint job failure on the bugbot autofix commit * fix(messages): forward anthropic-beta to native passthrough upstream The shared anthropic_messages HTTP handler ran update_headers_with_filtered_beta with the deployment's custom_llm_provider after validate. For the native /v1/messages passthrough that provider is openai, which has no beta-header mapping, so every anthropic-beta value (caller-supplied or feature-derived for speed/context_management/etc.) was stripped to empty before the upstream request, breaking beta passthrough to the Anthropic-compatible endpoint. Beta filtering only makes sense on cross-provider translation paths where the upstream cannot understand Anthropic betas. Gate it on a new should_filter_anthropic_beta_headers() that defaults to True (bedrock, vertex_ai, native anthropic unchanged) and is overridden to False by OpenAILikeAnthropicMessagesConfig, whose upstream is a native Anthropic endpoint, so betas pass through verbatim. * chore: remove accidentally committed local QA logs and config --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Relevant issues
Generalizes and supersedes #28745, which did the same thing but only for
hosted_vllmand only via adisable_anthropic_translationenv-var/litellm_params toggleLinear ticket
Resolves LIT-3750
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewDelays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
End-to-end on a live DB-less proxy hitting the real DeepSeek Anthropic-compatible endpoint (no mocks). The deployment is provider
openaiwithmodel_info.supported_endpointsincluding/v1/messages. The same Anthropic/v1/messagespayload (with acache_controlsystem block, an Anthropic-only feature) is sent to the proxy on both commits. On the base commit theopenaiprovider translates/v1/messagesand POSTs to a non-existenthttps://api.deepseek.com/anthropic/responses, returning 404. On the PR head the raw Anthropic payload is forwarded untranslated tohttps://api.deepseek.com/anthropic/v1/messages, returning a native Anthropicmessageresponse with Anthropic-styleusage(includingcache_creation_input_tokens)Config used (DB-less,
master_key: sk-1234):Before (base
26ee5dd59)Proxy debug log for this request shows the translated outbound call going to
https://api.deepseek.com/anthropic/responses, which does not exist, hence the 404After (PR head
ab174609)Same command, same payload:
The body is genuine native Anthropic format (
"type":"message","role":"assistant",content,stop_reason, and Anthropic-styleusagewithcache_creation_input_tokens/cache_read_input_tokens/service_tier), not an OpenAIchat.completion. The proxy debug log for this request confirms the call was handled vialitellm.anthropic_messages(...)and POSTed tohttps://api.deepseek.com/anthropic/v1/messageswith thecache_controlsystem block forwarded untranslatedType
🆕 New Feature
Changes
Today the unified
/v1/messagesproxy endpoint always translates an inbound Anthropic request down to/v1/chat/completions(or the Responses API foropenai) whenever the deployment's provider has no native Anthropic-messages config. That translation silently drops Anthropic-only features such ascache_controlandthinking. A customer runs OpenAI-compatible model servers (self-hosted vLLM, DeepSeek's Anthropic-compatible endpoint, and similar) that also natively expose the Anthropic/v1/messagesAPI, and wants LiteLLM to forward the raw Anthropic payload through untranslated, while keeping the provider asopenaiso/v1/chat/completionsto the same deployment stays nativeThis adds a per-endpoint, per-deployment opt-in. Declare
supported_endpointsundermodel_infoand include"/v1/messages":When
/v1/messagesis present, the gate routes to a new generic, provider-agnosticOpenAILikeAnthropicMessagesConfig(a subclass of the nativeAnthropicMessagesConfig, mirroring the existing DeepSeek pattern) that POSTs the Anthropic-shaped body to{api_base}/v1/messageswithAuthorization: Bearer {api_key}, a defaultanthropic-version, andcontent-type: application/json, inheriting the proven Anthropic request transformation, response parsing, and streaming from the parent native config. Because it delegates request shaping to the parent, it also forwardsanthropic-betaheaders for Anthropic features like context management and fast mode (merging with any caller-suppliedanthropic-beta) rather than silently dropping them, which is the same class of feature-loss the translation path causes. Without the opt-in, behavior is unchanged and the request is still translated./v1/chat/completionsto the same deployment is untouchedPlumbing: the router already puts each deployment's
model_info(which preservessupported_endpointsviaModelInfo'sextra="allow") ontokwargs["model_info"], which flows through the@clientasync wrapper intoanthropic_messages_handler. The gate reads it there and selects the passthrough config before the translation fallback, so the documentedmodel_infopath works end to end with no new hidden litellm_params fieldFiles
litellm/llms/openai_like/messages/transformation.py(new):OpenAILikeAnthropicMessagesConfiglitellm/llms/anthropic/experimental_pass_through/messages/handler.py:_deployment_passes_through_anthropic_messageshelper and the gate branch that selects the passthrough config when opted inlitellm/llms/base_llm/anthropic_messages/transformation.py:should_filter_anthropic_beta_headers()hook (defaults toTrue) so configs can opt out of provider-basedanthropic-betafiltering; the passthrough config overrides it toFalselitellm/llms/custom_httpx/llm_http_handler.py: gate the post-validateupdate_headers_with_filtered_betacall on that hook so the native passthrough does not stripanthropic-beta(the deployment routes asopenai, which has no beta mapping and would otherwise drop every value)tests/test_litellm/llms/openai_like/messages/test_openai_like_anthropic_messages_transformation.py(new): URL building forapi_basevariants, Anthropic-shape payload preservation, case-insensitive Bearer/version/content-type header handling (including standard-cased caller headers and a caller-suppliedx-api-key), andanthropic-betaforwarding/merging for context management and fast modetests/test_litellm/.../test_anthropic_experimental_pass_through_messages_handler.py: gate regression tests (passthrough when opted in, translate otherwise). These fail on the base commit and pass afterHeader defaults (
Authorization: Bearer {api_key},anthropic-version,content-type) are injected only when the caller did not already supply them, matched case-insensitively so a standard-casedAuthorization/Anthropic-Version/Content-Typeis not duplicated, and caller headers are returned untouched (no mutation of the inbound dict). A caller-suppliedanthropic-beta(any casing) is preserved and merged with the betas the parent config adds for the requested Anthropic featuresDocs live in the separate litellm-docs repo, not under
docs/my-website/docshere: BerriAI/litellm-docs#436Note
Medium Risk
Changes routing and outbound HTTP for the unified
/v1/messagespath on OpenAI-compatible deployments; misconfiguration could send requests to the wrong URL or bypass translation unexpectedly, though the opt-in is explicit and default behavior is preserved.Overview
Deployments can opt in to forwarding inbound Anthropic
/v1/messageswithout translating to chat/completions or the Responses API by settingmodel_info.supported_endpointsto include"/v1/messages"(alongside existing endpoints like/v1/chat/completions). When that flag is present and no native provider Anthropic-messages config exists, routing uses a newOpenAILikeAnthropicMessagesConfigthat POSTs the Anthropic-shaped body to{api_base}/v1/messages, with default auth/version headers only when the caller did not supply them.Passthrough configs disable provider-based
anthropic-betafiltering via a newshould_filter_anthropic_beta_headers()hook (defaultTrueon the base config;Falsefor passthrough), so beta headers and Anthropic-only payload fields (e.g.cache_control,thinking) are not stripped on theopenaiprovider path. Without the opt-in, behavior is unchanged: OpenAI-labeled deployments still go through translation.Regression tests cover the handler gate (opt-in vs translate) and the new transformation (URL building, payload shape, headers, beta forwarding).
Reviewed by Cursor Bugbot for commit 4c1ce65. Bugbot is set up for automated code reviews on this repo. Configure here.