fix(bedrock/claude_platform): strip body params the AWS endpoint rejects

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Venkata Donavalli 2026-09-22 01:48:46 +00:00 • committed by shrey kharbanda
parent b833e1fc4c
commit cf75f09a19
3 changed files with 114 additions and 13 deletions

View file

@ -1,15 +1,76 @@
from collections.abc import Mapping
from typing import Final
import httpx
import litellm
from litellm._logging import verbose_logger
from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM
from litellm.llms.bedrock.common_utils import BedrockError
from litellm.secret_managers.main import get_secret_str
from litellm.types.router import GenericLiteLLMParams
CLAUDE_PLATFORM_SERVICE_NAME: Final = "aws-external-anthropic"
CLAUDE_PLATFORM_BEDROCK_ROUTE: Final = "claude_platform/"
CLAUDE_PLATFORM_UNSUPPORTED_PARAMS_OVERRIDE_KEY: Final = "claude_platform_unsupported_params"
CLAUDE_PLATFORM_ON_AWS_NON_REQUEST_PARAMS: Final = frozenset(
{
"workspace_id",
"aws_workspace_id",
"anthropic_workspace_id",
"anthropic-workspace-id",
CLAUDE_PLATFORM_UNSUPPORTED_PARAMS_OVERRIDE_KEY,
}
)
CLAUDE_PLATFORM_ON_AWS_UNSUPPORTED_REQUEST_PARAMS: Final = frozenset({"context_management"})
def filter_claude_platform_request_body(
params: Mapping[str, object],
unsupported_override: frozenset[str] | None = None,
log_dropped: bool = True,
) -> dict[str, object]:
unsupported: Final = (
unsupported_override if unsupported_override is not None else CLAUDE_PLATFORM_ON_AWS_UNSUPPORTED_REQUEST_PARAMS
)
dropped_unsupported: Final = tuple(k for k in params if k in 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 endpoint rejects unknown fields. The request will proceed without them.",
dropped_unsupported,
)
return {
k: v
for k, v in params.items()
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: Mapping[str, object] | GenericLiteLLMParams,
optional_params: Mapping[str, object] | None = None,
log_invalid: bool = True,
) -> frozenset[str] | None:
from_optional: Final = (optional_params or {}).get(CLAUDE_PLATFORM_UNSUPPORTED_PARAMS_OVERRIDE_KEY)
raw: Final = (
from_optional
if from_optional is not None
else litellm_params.get(CLAUDE_PLATFORM_UNSUPPORTED_PARAMS_OVERRIDE_KEY)
)
if raw is None:
return None
if isinstance(raw, (list, set, frozenset, tuple)):
return frozenset(str(item) for item in raw)
if log_invalid:
verbose_logger.warning(
"bedrock/claude_platform: ignoring claude_platform_unsupported_params of type %s; "
"expected a list of param names. Using the default unsupported-param set.",
type(raw).__name__,
)
return None
def strip_claude_platform_route(model: str) -> str:
if model.startswith(CLAUDE_PLATFORM_BEDROCK_ROUTE):

View file

@ -8,7 +8,12 @@ from litellm.llms.anthropic.experimental_pass_through.messages.transformation im
from litellm.secret_managers.main import get_secret_str
from litellm.types.router import GenericLiteLLMParams
from .common_utils import BedrockClaudePlatformMixin, strip_claude_platform_route
from .common_utils import (
BedrockClaudePlatformMixin,
filter_claude_platform_request_body,
resolve_unsupported_override,
strip_claude_platform_route,
)
class BedrockClaudePlatformMessagesConfig(BedrockClaudePlatformMixin, AnthropicMessagesConfig):
@ -46,26 +51,38 @@ class BedrockClaudePlatformMessagesConfig(BedrockClaudePlatformMixin, AnthropicM
if resolved_api_key and "x-api-key" not in headers:
headers["x-api-key"] = resolved_api_key
headers = self._update_headers_with_anthropic_beta(
headers=headers,
optional_params=optional_params,
messages=messages,
return (
self._update_headers_with_anthropic_beta(
headers=headers,
optional_params=filter_claude_platform_request_body(
optional_params,
unsupported_override=resolve_unsupported_override(
litellm_params, optional_params=optional_params, log_invalid=False
),
log_dropped=False,
),
messages=messages,
),
api_base,
)
return headers, api_base
def transform_anthropic_messages_request(
self,
model: str,
messages: list[dict],
anthropic_messages_optional_request_params: dict,
messages: list[dict[str, object]],
anthropic_messages_optional_request_params: dict[str, object],
litellm_params: GenericLiteLLMParams,
headers: dict,
) -> dict:
headers: dict[str, str],
) -> dict[str, object]:
return super().transform_anthropic_messages_request(
model=strip_claude_platform_route(model),
messages=messages,
anthropic_messages_optional_request_params=anthropic_messages_optional_request_params,
anthropic_messages_optional_request_params=filter_claude_platform_request_body(
anthropic_messages_optional_request_params,
unsupported_override=resolve_unsupported_override(
litellm_params, optional_params=anthropic_messages_optional_request_params
),
),
litellm_params=litellm_params,
headers=headers,
)

View file

@ -5,7 +5,11 @@ from litellm.llms.anthropic.chat.transformation import AnthropicConfig
from litellm.secret_managers.main import get_secret_str
from litellm.types.llms.openai import AllMessageValues
from .common_utils import BedrockClaudePlatformMixin
from .common_utils import (
BedrockClaudePlatformMixin,
filter_claude_platform_request_body,
resolve_unsupported_override,
)
class BedrockClaudePlatformConfig(BedrockClaudePlatformMixin, AnthropicConfig):
@ -66,6 +70,25 @@ class BedrockClaudePlatformConfig(BedrockClaudePlatformMixin, AnthropicConfig):
anthropic_headers["anthropic-workspace-id"] = workspace_id
return {**headers, **anthropic_headers}
def transform_request(
self,
model: str,
messages: list[AllMessageValues],
optional_params: dict[str, object],
litellm_params: dict[str, object],
headers: dict[str, str],
) -> dict[str, object]:
return super().transform_request(
model=model,
messages=messages,
optional_params=filter_claude_platform_request_body(
optional_params,
unsupported_override=resolve_unsupported_override(litellm_params, optional_params=optional_params),
),
litellm_params=litellm_params,
headers=headers,
)
def get_model_response_iterator(
self,
streaming_response: Any,