mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
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:
parent
72811bcf2a
commit
8bf4c56171
4 changed files with 87 additions and 29 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue