diff --git a/litellm/llms/bedrock/claude_platform/common_utils.py b/litellm/llms/bedrock/claude_platform/common_utils.py index fb7f2185ec5..e25e58056f5 100644 --- a/litellm/llms/bedrock/claude_platform/common_utils.py +++ b/litellm/llms/bedrock/claude_platform/common_utils.py @@ -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): diff --git a/litellm/llms/bedrock/claude_platform/messages_transformation.py b/litellm/llms/bedrock/claude_platform/messages_transformation.py index 1e3eea075f3..f423d22589b 100644 --- a/litellm/llms/bedrock/claude_platform/messages_transformation.py +++ b/litellm/llms/bedrock/claude_platform/messages_transformation.py @@ -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, ) diff --git a/litellm/llms/bedrock/claude_platform/transformation.py b/litellm/llms/bedrock/claude_platform/transformation.py index a57f309b605..96a6ee4a701 100644 --- a/litellm/llms/bedrock/claude_platform/transformation.py +++ b/litellm/llms/bedrock/claude_platform/transformation.py @@ -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,