fix(bedrock/claude_platform): make unsupported-param list overridable

Add claude_platform_unsupported_params in litellm_params so operators
can clear or override the blocked-param set without a code change when
the AWS endpoint adds support. Also add mutation-safety assertions to
the messages-path tests per Greptile review feedback.
This commit is contained in:
Venkat Donavalli 2026-06-24 08:47:40 -05:00 • committed by mateo-berri
parent 72811bcf2a
commit 8bf4c56171
No known key found for this signature in database
4 changed files with 87 additions and 29 deletions

View file

@ -14,7 +14,7 @@ CLAUDE_PLATFORM_BEDROCK_ROUTE = "claude_platform/"
# must not be forwarded in the Messages API request body (together with any
# key prefixed "aws_") — the API rejects unknown fields with
# "Extra inputs are not permitted".
CLAUDE_PLATFORM_NON_REQUEST_PARAMS = {
CLAUDE_PLATFORM_ON_AWS_NON_REQUEST_PARAMS = {
"workspace_id",
"anthropic_workspace_id",
"anthropic-workspace-id",
@ -25,12 +25,15 @@ CLAUDE_PLATFORM_NON_REQUEST_PARAMS = {
# endpoint, which rejects them with "Extra inputs are not permitted". Unlike
# the auth params above these carry user intent, so dropping them is logged at
# WARNING — the request succeeds but the requested feature is not applied.
CLAUDE_PLATFORM_UNSUPPORTED_REQUEST_PARAMS = {
CLAUDE_PLATFORM_ON_AWS_UNSUPPORTED_REQUEST_PARAMS = {
"context_management",
}
def filter_claude_platform_request_body(params: dict) -> dict:
def filter_claude_platform_request_body(
params: dict,
unsupported_override: Optional[frozenset[str]] = None,
) -> dict:
"""Return a copy of ``params`` with fields the Claude Platform on AWS
endpoint rejects removed.
@ -41,15 +44,23 @@ def filter_claude_platform_request_body(params: dict) -> dict:
WARNING, since those reflect user intent that will not be applied on this
route.
``unsupported_override``, when provided, replaces the default
CLAUDE_PLATFORM_ON_AWS_UNSUPPORTED_REQUEST_PARAMS set. Pass an empty frozenset
to disable unsupported-param filtering entirely (e.g. when the AWS
endpoint adds support before a litellm release).
Filters a copy so callers' ``sign_request`` still sees ``aws_region_name``.
"""
dropped_unsupported = [
k for k in params if k in CLAUDE_PLATFORM_UNSUPPORTED_REQUEST_PARAMS
]
unsupported = (
unsupported_override
if unsupported_override is not None
else CLAUDE_PLATFORM_ON_AWS_UNSUPPORTED_REQUEST_PARAMS
)
dropped_unsupported = [k for k in params if k in unsupported]
if dropped_unsupported:
verbose_logger.warning(
"bedrock/claude_platform: dropping unsupported Messages API "
"param(s) %s from the request body — the Claude Platform on AWS "
"param(s) %s from the request body - the Claude Platform on AWS "
"(aws-external-anthropic) endpoint does not support them and "
"rejects unknown fields. The request will proceed without them.",
dropped_unsupported,
@ -57,12 +68,29 @@ def filter_claude_platform_request_body(params: dict) -> dict:
return {
k: v
for k, v in params.items()
if k not in CLAUDE_PLATFORM_NON_REQUEST_PARAMS
and k not in CLAUDE_PLATFORM_UNSUPPORTED_REQUEST_PARAMS
if k not in CLAUDE_PLATFORM_ON_AWS_NON_REQUEST_PARAMS
and k not in unsupported
and not k.startswith("aws_")
}
def _resolve_unsupported_override(
litellm_params: dict,
) -> Optional[frozenset[str]]:
"""Read ``claude_platform_unsupported_params`` from litellm_params.
Returns None (use default set) when the key is absent. Returns a
frozenset when present, allowing operators to override or clear the
unsupported-param list via proxy config without a code change.
"""
raw = litellm_params.get("claude_platform_unsupported_params")
if raw is None:
return None
if isinstance(raw, (list, set, frozenset, tuple)):
return frozenset(raw)
return None
def strip_claude_platform_route(model: str) -> str:
if model.startswith(CLAUDE_PLATFORM_BEDROCK_ROUTE):
return model.replace(CLAUDE_PLATFORM_BEDROCK_ROUTE, "", 1)

View file

@ -10,6 +10,7 @@ from litellm.types.router import GenericLiteLLMParams
from .common_utils import (
BedrockClaudePlatformMixin,
_resolve_unsupported_override,
filter_claude_platform_request_body,
strip_claude_platform_route,
)
@ -66,13 +67,11 @@ class BedrockClaudePlatformMessagesConfig(
litellm_params: GenericLiteLLMParams,
headers: dict,
) -> Dict:
# Strip auth/routing config (workspace_id, aws_*) and Messages API
# fields the AWS endpoint does not support (e.g. context_management)
# from the body — the API rejects unknown fields with "Extra inputs
# are not permitted".
unsupported_override = _resolve_unsupported_override(litellm_params)
anthropic_messages_optional_request_params = (
filter_claude_platform_request_body(
anthropic_messages_optional_request_params
anthropic_messages_optional_request_params,
unsupported_override=unsupported_override,
)
)
return super().transform_anthropic_messages_request(

View file

@ -7,6 +7,7 @@ from litellm.types.llms.openai import AllMessageValues
from .common_utils import (
BedrockClaudePlatformMixin,
_resolve_unsupported_override,
filter_claude_platform_request_body,
)
@ -93,12 +94,10 @@ class BedrockClaudePlatformConfig(BedrockClaudePlatformMixin, AnthropicConfig):
litellm_params: dict,
headers: dict,
) -> dict:
# Strip auth/routing config (workspace_id, aws_*) and Messages API
# fields the AWS endpoint does not support (e.g. context_management)
# from the body — the API rejects unknown fields with "Extra inputs
# are not permitted". Filters a copy so sign_request still sees
# aws_region_name.
optional_params = filter_claude_platform_request_body(optional_params)
unsupported_override = _resolve_unsupported_override(litellm_params)
optional_params = filter_claude_platform_request_body(
optional_params, unsupported_override=unsupported_override
)
return super().transform_request(
model=model,
messages=messages,

View file

@ -361,14 +361,15 @@ def test_claude_platform_messages_strips_auth_params_from_request_body():
)
assert config is not None
input_params = {
"workspace_id": "wrkspc_test",
"aws_region_name": "us-west-2",
"max_tokens": 10,
}
request_body = config.transform_anthropic_messages_request(
model="claude_platform/claude-sonnet-4-6",
messages=[{"role": "user", "content": "hello"}],
anthropic_messages_optional_request_params={
"workspace_id": "wrkspc_test",
"aws_region_name": "us-west-2",
"max_tokens": 10,
},
anthropic_messages_optional_request_params=input_params,
litellm_params={},
headers={},
)
@ -376,6 +377,8 @@ def test_claude_platform_messages_strips_auth_params_from_request_body():
assert "workspace_id" not in request_body
assert "aws_region_name" not in request_body
assert request_body["max_tokens"] == 10
assert input_params["aws_region_name"] == "us-west-2"
assert input_params["workspace_id"] == "wrkspc_test"
def test_claude_platform_strips_unsupported_context_management_param(caplog):
@ -433,19 +436,48 @@ def test_claude_platform_messages_strips_unsupported_context_management_param():
)
assert config is not None
input_params = {
"context_management": {"edits": [{"type": "clear_tool_uses_20250919"}]},
"max_tokens": 10,
}
request_body = config.transform_anthropic_messages_request(
model="claude_platform/claude-sonnet-4-6",
messages=[{"role": "user", "content": "hello"}],
anthropic_messages_optional_request_params={
"context_management": {"edits": [{"type": "clear_tool_uses_20250919"}]},
"max_tokens": 10,
},
anthropic_messages_optional_request_params=input_params,
litellm_params={},
headers={},
)
assert "context_management" not in request_body
assert request_body["max_tokens"] == 10
assert "context_management" in input_params
def test_claude_platform_unsupported_override_allows_context_management():
"""
Operators can pass claude_platform_unsupported_params=[] in litellm_params
to stop filtering context_management once the AWS endpoint supports it.
"""
from litellm.llms.bedrock.claude_platform.transformation import (
BedrockClaudePlatformConfig,
)
config = BedrockClaudePlatformConfig()
optional_params = {
"context_management": {"edits": [{"type": "clear_tool_uses_20250919"}]},
"max_tokens": 10,
}
request_body = config.transform_request(
model="claude-sonnet-4-6",
messages=[{"role": "user", "content": "hello"}],
optional_params=optional_params,
litellm_params={"claude_platform_unsupported_params": []},
headers={},
)
assert request_body["max_tokens"] == 10
assert "context_management" in request_body
def test_chat_completion_claude_platform_sigv4_body_has_no_auth_params():