From 934ae7dd7f6d72ff0628d3a55cc006b6abac4c57 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 24 Jun 2026 16:19:03 +0000 Subject: [PATCH] fix(bedrock/claude_platform): align messages beta headers with filtered body validate_anthropic_messages_environment derived anthropic-beta from unfiltered optional_params, while transform_anthropic_messages_request strips context_management from the body. Requests advertised a beta the body did not use. Filter optional_params with the same helper before computing headers (silently, to avoid a duplicate warning since the transform path still logs once). --- .../bedrock/claude_platform/common_utils.py | 3 +- .../messages_transformation.py | 8 ++- .../bedrock/test_claude_platform_provider.py | 66 +++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/litellm/llms/bedrock/claude_platform/common_utils.py b/litellm/llms/bedrock/claude_platform/common_utils.py index 1dce8f6a98a..dc530dbbc6f 100644 --- a/litellm/llms/bedrock/claude_platform/common_utils.py +++ b/litellm/llms/bedrock/claude_platform/common_utils.py @@ -34,6 +34,7 @@ CLAUDE_PLATFORM_ON_AWS_UNSUPPORTED_REQUEST_PARAMS = { def filter_claude_platform_request_body( params: dict, unsupported_override: Optional[frozenset[str]] = None, + log_dropped: bool = True, ) -> dict: """Return a copy of ``params`` with fields the Claude Platform on AWS endpoint rejects removed. @@ -58,7 +59,7 @@ def filter_claude_platform_request_body( else CLAUDE_PLATFORM_ON_AWS_UNSUPPORTED_REQUEST_PARAMS ) dropped_unsupported = [k for k in params if k in unsupported] - if dropped_unsupported: + if dropped_unsupported and log_dropped: verbose_logger.warning( "bedrock/claude_platform: dropping unsupported Messages API " "param(s) %s from the request body - the Claude Platform on AWS " diff --git a/litellm/llms/bedrock/claude_platform/messages_transformation.py b/litellm/llms/bedrock/claude_platform/messages_transformation.py index 986dbe48631..9948a03fe57 100644 --- a/litellm/llms/bedrock/claude_platform/messages_transformation.py +++ b/litellm/llms/bedrock/claude_platform/messages_transformation.py @@ -52,9 +52,15 @@ class BedrockClaudePlatformMessagesConfig( if resolved_api_key and "x-api-key" not in headers: headers["x-api-key"] = resolved_api_key + unsupported_override = resolve_unsupported_override(litellm_params) + filtered_optional_params = filter_claude_platform_request_body( + optional_params, + unsupported_override=unsupported_override, + log_dropped=False, + ) headers = self._update_headers_with_anthropic_beta( headers=headers, - optional_params=optional_params, + optional_params=filtered_optional_params, ) return headers, api_base diff --git a/tests/test_litellm/llms/bedrock/test_claude_platform_provider.py b/tests/test_litellm/llms/bedrock/test_claude_platform_provider.py index 85665aac853..a535bbfca80 100644 --- a/tests/test_litellm/llms/bedrock/test_claude_platform_provider.py +++ b/tests/test_litellm/llms/bedrock/test_claude_platform_provider.py @@ -507,6 +507,72 @@ def test_claude_platform_unsupported_override_ignores_invalid_type(): assert request_body["max_tokens"] == 10 +def test_claude_platform_messages_does_not_advertise_beta_for_stripped_context_management(): + """ + Regression: validate_anthropic_messages_environment used to compute + anthropic-beta from unfiltered optional_params, while + transform_anthropic_messages_request strips context_management from the + body. The resulting request claimed a beta the body did not use, which + can trip strict gateway validation. Headers must be derived from the + same filtered view as the body. + """ + import litellm + from litellm.types.utils import LlmProviders + + config = litellm.ProviderConfigManager.get_provider_anthropic_messages_config( + model="claude_platform/claude-sonnet-4-6", + provider=LlmProviders.BEDROCK, + ) + assert config is not None + + headers, _ = config.validate_anthropic_messages_environment( + api_key="fake-platform-key", + headers={}, + model="claude_platform/claude-sonnet-4-6", + messages=[{"role": "user", "content": "hello"}], + optional_params={ + "max_tokens": 10, + "context_management": {"edits": [{"type": "clear_tool_uses_20250919"}]}, + }, + litellm_params={"workspace_id": "wrkspc_test"}, + ) + + assert "context-management-2025-06-27" not in headers.get("anthropic-beta", "") + + +def test_claude_platform_messages_override_keeps_beta_for_context_management(): + """ + When the operator opts back into context_management via + claude_platform_unsupported_params=[], the body keeps the field and the + header must still advertise the matching beta. + """ + import litellm + from litellm.types.utils import LlmProviders + + config = litellm.ProviderConfigManager.get_provider_anthropic_messages_config( + model="claude_platform/claude-sonnet-4-6", + provider=LlmProviders.BEDROCK, + ) + assert config is not None + + headers, _ = config.validate_anthropic_messages_environment( + api_key="fake-platform-key", + headers={}, + model="claude_platform/claude-sonnet-4-6", + messages=[{"role": "user", "content": "hello"}], + optional_params={ + "max_tokens": 10, + "context_management": {"edits": [{"type": "clear_tool_uses_20250919"}]}, + }, + litellm_params={ + "workspace_id": "wrkspc_test", + "claude_platform_unsupported_params": [], + }, + ) + + assert "context-management-2025-06-27" in headers.get("anthropic-beta", "") + + def test_claude_platform_messages_unsupported_override_allows_context_management(): """ Messages-path: operators can pass claude_platform_unsupported_params=[]