Skip to content

fix(bedrock): translate body-level betas into additionalModelRequestFields.anthropic_beta for Converse - #28096

Closed
GauravS11112003 wants to merge 7 commits into
BerriAI:litellm_internal_stagingfrom
GauravS11112003:litellm_internal_staging
Closed

fix(bedrock): translate body-level betas into additionalModelRequestFields.anthropic_beta for Converse#28096
GauravS11112003 wants to merge 7 commits into
BerriAI:litellm_internal_stagingfrom
GauravS11112003:litellm_internal_staging

Conversation

@GauravS11112003

@GauravS11112003 GauravS11112003 commented May 16, 2026

Copy link
Copy Markdown

Relevant issues

Fixes #28081

Linear ticket

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Screenshots / Proof of Fix

Before (failure)

The reproducer from #28081 returns HTTP 400 from AWS Bedrock:

curl -s -X POST http://127.0.0.1:4000/v1/messages \
  -H "Authorization: Bearer sk-1234" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: context-1m-2025-08-07" \
  -d '{
    "model": "claude-opus-4-7-bedrock-converse",
    "max_tokens": 256,
    "messages": [{"role": "user", "content": "<~210k tokens of context here>"}],
    "betas": ["context-1m-2025-08-07"]
  }'
{"error": {"message": "litellm.BadRequestError: BedrockException - {\"message\":\"The model returned the following errors: betas: Extra inputs are not permitted\"}", "code": "400"}}

After (fix)

The body-level betas field is now translated into additionalModelRequestFields.anthropic_beta, which AWS Bedrock Converse accepts. The request body LiteLLM now sends to AWS becomes:

{
  "additionalModelRequestFields": {
    "anthropic_beta": ["context-1m-2025-08-07"]
  },
  ...
}

Test output

$ uv run pytest tests/test_litellm/llms/bedrock/test_anthropic_beta_support.py -v
...
tests/test_litellm/llms/bedrock/test_anthropic_beta_support.py::TestAnthropicBetaHeaderSupport::test_converse_transformation_betas_from_body PASSED
tests/test_litellm/llms/bedrock/test_anthropic_beta_support.py::TestAnthropicBetaHeaderSupport::test_converse_transformation_anthropic_beta_from_body PASSED
tests/test_litellm/llms/bedrock/test_anthropic_beta_support.py::TestAnthropicBetaHeaderSupport::test_converse_transformation_betas_body_and_header_merged PASSED
tests/test_litellm/llms/bedrock/test_anthropic_beta_support.py::TestAnthropicBetaHeaderSupport::test_converse_transformation_betas_body_non_anthropic_model PASSED
...
============================== 22 passed in 0.70s ==============================

Full Bedrock chat suite (tests/test_litellm/llms/bedrock/chat/ + test_anthropic_beta_support.py): 269 passed, 0 failed.

Type

🐛 Bug Fix

Changes

Problem. When an Anthropic 1M-context request was routed through bedrock_converse, LiteLLM forwarded the user-supplied betas: ["context-1m-2025-08-07"] array as a top-level body field. AWS Bedrock's Converse API rejects unknown top-level fields and returned HTTP 400 — betas: Extra inputs are not permitted. The same payload worked against anthropic (native), bedrock (Invoke), and vertex_ai, so Converse was the only blocked route to 1M context.

Root cause. AmazonConverseConfig._process_tools_and_beta() in litellm/llms/bedrock/chat/converse_transformation.py only collected beta values from the anthropic-beta request header. Body-level betas (Anthropic Messages API style) was untouched and passed straight through to AWS in additionalModelRequestFields.

Fix. Extract body-level betas (and the alternate anthropic_beta key) inside _process_tools_and_beta(), merge with header-derived values, and emit a single deduplicated additionalModelRequestFields.anthropic_beta list — the shape AWS Bedrock Converse accepts. Non-Anthropic models on Bedrock (Nova, Llama, etc.) still have the field stripped without re-adding it, preserving existing behavior.

Files changed

  • litellm/llms/bedrock/chat/converse_transformation.py
    • New _pop_body_anthropic_betas() static helper strips betas / anthropic_beta from additional_request_params.
    • _process_tools_and_beta() now merges header- and body-derived betas; final list is deduplicated with dict.fromkeys() (order-preserving) so the body, headers, and auto-detected sources like computer-use can't double-list the same beta.
  • tests/test_litellm/llms/bedrock/test_anthropic_beta_support.py
    • test_converse_transformation_betas_from_body — body betasadditionalModelRequestFields.anthropic_beta, top-level betas removed.
    • test_converse_transformation_anthropic_beta_from_body — same for the alternate anthropic_beta body key.
    • test_converse_transformation_betas_body_and_header_merged — body betas + header anthropic-beta merged into a single list.
    • test_converse_transformation_betas_body_non_anthropic_model — body betas stripped for non-Anthropic models (Nova) without re-adding anthropic_beta.

Verification

  • Full Bedrock chat suite: 269 passed, 0 failed (2 deselected — they require live AWS network access).
  • uv run ruff check and uv run black . both clean.
  • Backwards compatible: header-only flows are unchanged; non-Anthropic Bedrock models are unchanged.

FabrizioCafolla and others added 5 commits May 15, 2026 10:36
Squash-merged by litellm-agent from FabrizioCafolla's PR.
Squash-merged by litellm-agent from tomdee's PR.
Squash-merged by litellm-agent from escon1004's PR.
…conversations (BerriAI#28080)

Squash-merged by litellm-agent from Divyansh8321's PR.
@CLAassistant

CLAassistant commented May 16, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
5 out of 6 committers have signed the CLA.

✅ FabrizioCafolla
✅ tomdee
✅ Cyberfilo
✅ GauravS11112003
✅ Divyansh8321
❌ escon1004
You have signed the CLA already but the status is still pending? Let us recheck it.

@codecov

codecov Bot commented May 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.85185% with 38 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/llms/deepseek/chat/transformation.py 23.33% 23 Missing ⚠️
litellm/router.py 76.66% 7 Missing ⚠️
litellm/cost_calculator.py 87.50% 4 Missing ⚠️
litellm/proxy/proxy_server.py 0.00% 3 Missing ⚠️
litellm/proxy/route_llm_request.py 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a HTTP 400 error when routing Anthropic beta-flagged requests through AWS Bedrock Converse: body-level betas (Anthropic Messages API style) were being forwarded as top-level fields, which Bedrock Converse explicitly rejects. The fix extracts those values and places them in additionalModelRequestFields.anthropic_beta, the shape Bedrock accepts, while stripping them entirely for non-Anthropic models.

  • _pop_body_anthropic_betas() helper strips both the betas and the alternate anthropic_beta body keys from additional_request_params before they reach the AWS wire format, returning the collected strings so _process_tools_and_beta() can merge them with header-derived betas.
  • Deduplication via dict.fromkeys() (order-preserving) ensures the same beta string from headers, body, and auto-detected sources (e.g., computer-use) is emitted only once.
  • Four new unit tests cover body-only betas, the alternate key name, merged header+body, and the non-Anthropic stripping path; all tests are pure transformation-layer unit tests with no real network calls.

Confidence Score: 4/5

The change is a narrowly scoped transformation fix that only affects how beta flags flow into the Bedrock Converse request body; existing header-only and non-Anthropic-model paths are unchanged and covered by tests.

The implementation is correct and well-tested across all four relevant scenarios. The only gaps are a missing explicit test for the duplicate-beta deduplication path and a lack of observability when betas are silently dropped for non-Anthropic models — neither affects correctness on the fixed path.

No files require special attention; converse_transformation.py is the core change and the logic is straightforward.

Important Files Changed

Filename Overview
litellm/llms/bedrock/chat/converse_transformation.py Adds _pop_body_anthropic_betas() static helper and updates _process_tools_and_beta() to merge body-level betas/anthropic_beta with header-derived betas, then deduplicate with dict.fromkeys(); non-Anthropic models have the field silently stripped.
tests/test_litellm/llms/bedrock/test_anthropic_beta_support.py Adds 4 new unit tests covering body betas, body anthropic_beta, merged header+body, and non-Anthropic model stripping; all tests call transformation methods directly with no real network calls.
.gitignore Adds .local/ to gitignore and fixes missing newline at end of file.

Reviews (1): Last reviewed commit: "fix(bedrock): translate body-level betas..." | Re-trigger Greptile

Comment on lines +178 to 180
assert "betas" not in additional_fields

def test_messages_transformation_anthropic_beta(self):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The merge+deduplication path is not tested for the exact scenario where the same beta string appears in both the request body (betas) and an anthropic-beta header. The existing test_converse_transformation_betas_body_and_header_merged test only checks distinct values; without a duplicate-entry case, the dict.fromkeys() dedup logic has no explicit regression coverage.

Suggested change
assert "betas" not in additional_fields
def test_messages_transformation_anthropic_beta(self):
assert "betas" not in additional_fields
def test_converse_transformation_betas_body_and_header_deduplicated(self):
"""Test that duplicate betas from body and header are collapsed into a single entry."""
config = AmazonConverseConfig()
headers = {"anthropic-beta": "context-1m-2025-08-07"}
result = config._transform_request_helper(
model="anthropic.claude-opus-4-7-20251101-v1:0",
system_content_blocks=[],
optional_params={"betas": ["context-1m-2025-08-07"]},
messages=[{"role": "user", "content": "Test"}],
headers=headers,
)
additional_fields = result["additionalModelRequestFields"]
assert additional_fields["anthropic_beta"].count("context-1m-2025-08-07") == 1
def test_messages_transformation_anthropic_beta(self):

Comment on lines +1310 to +1319
body_betas: List[str] = []
for body_beta_key in ("betas", "anthropic_beta"):
value = additional_request_params.pop(body_beta_key, None)
if value is None:
continue
if isinstance(value, list):
body_betas.extend(str(beta) for beta in value)
elif isinstance(value, str):
body_betas.append(value)
return body_betas

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Betas supplied via betas/anthropic_beta in the request body are silently dropped for non-Anthropic models. A user who mistakenly routes a beta-flagged request to a Nova or Llama model will get no indication that their betas were ignored. A single logger.debug call would make this much easier to diagnose without affecting the hot path.

Suggested change
body_betas: List[str] = []
for body_beta_key in ("betas", "anthropic_beta"):
value = additional_request_params.pop(body_beta_key, None)
if value is None:
continue
if isinstance(value, list):
body_betas.extend(str(beta) for beta in value)
elif isinstance(value, str):
body_betas.append(value)
return body_betas
body_betas: List[str] = []
for body_beta_key in ("betas", "anthropic_beta"):
value = additional_request_params.pop(body_beta_key, None)
if value is None:
continue
if isinstance(value, list):
body_betas.extend(str(beta) for beta in value)
elif isinstance(value, str):
body_betas.append(value)
if value is not None:
import logging
logger = logging.getLogger(__name__)
logger.debug(
"Stripped body-level '%s' from additionalModelRequestFields; "
"will re-emit as anthropic_beta for Anthropic models only.",
body_beta_key,
)
return body_betas

@oss-pr-review-agent-shin

Copy link
Copy Markdown
Contributor

🤖 litellm-agent: Auto-merge skipped — the staging branch shin_agent_oss_staging_05_16_2026 has 5 commit(s) not in your branch. Merging as-is would produce a confusing diff on the staging PR.

Please rebase your branch onto shin_agent_oss_staging_05_16_2026 and push; the agent will re-review automatically.

@GauravS11112003
GauravS11112003 force-pushed the litellm_internal_staging branch from 1c8632b to da8a0b2 Compare May 16, 2026 23:11
Adds two unit tests to AmazonConverseConfig._transform_request_helper
coverage:
- test_converse_transformation_betas_body_string_value: exercises the
  isinstance(value, str) branch in _pop_body_anthropic_betas, which the
  previous list-only tests left at 0% coverage (Codecov flag on PR).
- test_converse_transformation_betas_deduplicated: explicitly verifies
  that the same beta arriving via both the anthropic-beta header and the
  body betas array collapses to a single entry in
  additionalModelRequestFields.anthropic_beta. Greptile review noted the
  dedup path was only implicitly covered by an existing computer-use test.
No production changes.
@GauravS11112003
GauravS11112003 force-pushed the litellm_internal_staging branch from da8a0b2 to 09bf878 Compare May 16, 2026 23:29
@GauravS11112003

Copy link
Copy Markdown
Author

Heads up — the failing "Code Quality Checks" workflow is flagging
_is_deployment_blocked in router.py as untested. That function was
introduced in commit a66b965 (#27927, by @FabrizioCafolla ) on
shin_agent_oss_staging_05_16_2026 — not part of this PR's changes.

Once a follow-up adds the test (or the script is updated) on the staging
branch, I'll re-rebase and this should clear.

@github-actions

Copy link
Copy Markdown
Contributor

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@github-actions github-actions Bot added the stale label Aug 15, 2026
@github-actions github-actions Bot closed this Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Bedrock Converse rejects betas field forwarded by LiteLLM on Anthropic long-context (1M) requests

7 participants