diff --git a/litellm/llms/bedrock/common_utils.py b/litellm/llms/bedrock/common_utils.py index 4ad20772ed0..fc30f422ac2 100644 --- a/litellm/llms/bedrock/common_utils.py +++ b/litellm/llms/bedrock/common_utils.py @@ -206,10 +206,12 @@ def normalize_custom_field_on_tools(request_body: dict) -> None: def normalize_json_schema_custom_types_to_object(schema: dict) -> None: """ - In-place: replace JSON Schema ``type: \"custom\"`` with ``\"object\"`` (iterative walk). + In-place: normalize JSON Schema fields unsupported by Bedrock (iterative walk). Anthropic / Claude Code use ``custom`` for tool schemas; Bedrock Invoke and Bedrock Converse only accept standard JSON Schema type strings. + Bedrock Invoke also rejects ``minimum`` and ``maximum`` on number schemas, + even though other providers accept those JSON Schema validation keywords. Uses an explicit stack (not recursion) to satisfy recursive-function guards in CI. """ @@ -225,6 +227,8 @@ def normalize_json_schema_custom_types_to_object(schema: dict) -> None: seen.add(node_id) if node.get("type") == "custom": node["type"] = "object" + node.pop("minimum", None) + node.pop("maximum", None) items = node.get("items") if isinstance(items, dict): stack.append(items) @@ -249,8 +253,9 @@ def normalize_tool_input_schema_types_for_bedrock_invoke(request_body: dict) -> Bedrock Invoke (Anthropic Messages) validates ``input_schema`` as JSON Schema. Anthropic's API allows ``type: \"custom\"`` for Claude Code custom tools; Bedrock rejects it with: ``tools.0.custom.input_schema.type: Input should be 'object'``. + Bedrock also rejects ``minimum`` and ``maximum`` constraints on number schemas. - Normalizes ``type: \"custom\"`` to ``\"object\"`` throughout each tool's + Normalizes provider-unsupported schema fields throughout each tool's ``input_schema`` (recursive for nested properties, items, combinators). Args: diff --git a/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py b/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py index 1e09afd6919..92743e6a76e 100644 --- a/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py +++ b/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py @@ -1,4 +1,3 @@ -import asyncio import copy import json import os @@ -624,6 +623,7 @@ def test_normalize_tool_input_schema_types_for_bedrock_invoke(): """ Claude Code sends ``input_schema.type: \"custom\"`` for custom tools. Bedrock Invoke rejects this; it requires JSON Schema ``type: \"object\"``. + Bedrock also rejects ``minimum`` and ``maximum`` numeric bounds. """ request = { @@ -638,7 +638,31 @@ def test_normalize_tool_input_schema_types_for_bedrock_invoke(): "properties": { "nested": { "type": "custom", - "properties": {"x": {"type": "string"}}, + "properties": { + "x": {"type": "string"}, + "score": { + "type": "number", + "minimum": 0, + "maximum": 1, + }, + "values": { + "type": "array", + "items": { + "type": "number", + "minimum": -1, + "maximum": 1, + }, + }, + "choice": { + "oneOf": [ + { + "type": "number", + "minimum": 0, + "maximum": 10, + } + ] + }, + }, } }, "required": ["nested"], @@ -656,7 +680,14 @@ def test_normalize_tool_input_schema_types_for_bedrock_invoke(): agent_tool = request["tools"][0] assert agent_tool["type"] == "custom" assert agent_tool["input_schema"]["type"] == "object" - assert agent_tool["input_schema"]["properties"]["nested"]["type"] == "object" + nested_schema = agent_tool["input_schema"]["properties"]["nested"] + assert nested_schema["type"] == "object" + assert "minimum" not in nested_schema["properties"]["score"] + assert "maximum" not in nested_schema["properties"]["score"] + assert "minimum" not in nested_schema["properties"]["values"]["items"] + assert "maximum" not in nested_schema["properties"]["values"]["items"] + assert "minimum" not in nested_schema["properties"]["choice"]["oneOf"][0] + assert "maximum" not in nested_schema["properties"]["choice"]["oneOf"][0] assert request["tools"][1]["input_schema"]["type"] == "object" request2 = {"messages": []}