Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from litellm.llms.bedrock.common_utils import (
get_anthropic_beta_from_headers,
normalize_tool_input_schema_types_for_bedrock_invoke,
remove_custom_field_from_tools,
)
from litellm.types.llms.anthropic import ANTHROPIC_TOOL_SEARCH_BETA_HEADER
from litellm.types.llms.openai import AllMessageValues
Expand Down Expand Up @@ -173,8 +172,6 @@ def _build_bedrock_anthropic_request_base(
if "anthropic_version" not in anthropic_request:
anthropic_request["anthropic_version"] = self.anthropic_version

# Remove `custom` field from tools (Bedrock doesn't support it)
remove_custom_field_from_tools(anthropic_request)
normalize_tool_input_schema_types_for_bedrock_invoke(anthropic_request)
return anthropic_request

Expand Down
21 changes: 0 additions & 21 deletions litellm/llms/bedrock/common_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,27 +49,6 @@ def get_cached_model_info():
return _get_model_info


def remove_custom_field_from_tools(request_body: dict) -> None:
"""
Remove ``custom`` field from each tool in the request body.

Claude Code (v2.1.69+) sends ``custom: {defer_loading: true}`` on tool
definitions, which Anthropic's API accepts but Bedrock rejects with
``"Extra inputs are not permitted"``.

Args:
request_body: The request dictionary to modify in-place.

Ref: https://github.com/BerriAI/litellm/issues/22847
"""
tools = request_body.get("tools")
if not tools or not isinstance(tools, list):
return
for tool in tools:
if isinstance(tool, dict):
tool.pop("custom", None)


def normalize_json_schema_custom_types_to_object(schema: dict) -> None:
"""
In-place: replace JSON Schema ``type: \"custom\"`` with ``\"object\"`` (iterative walk).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
get_anthropic_beta_from_headers,
is_claude_4_5_on_bedrock,
normalize_tool_input_schema_types_for_bedrock_invoke,
remove_custom_field_from_tools,
)
from litellm.types.llms.anthropic import ANTHROPIC_TOOL_SEARCH_BETA_HEADER
from litellm.types.llms.openai import AllMessageValues
Expand Down Expand Up @@ -504,11 +503,6 @@ def transform_anthropic_messages_request(
# Fixes: https://github.com/BerriAI/litellm/issues/22797
anthropic_messages_request.pop("output_config", None)

# 5a. Remove `custom` field from tools (Bedrock doesn't support it)
# Claude Code sends `custom: {defer_loading: true}` on tool definitions,
# which causes Bedrock to reject the request with "Extra inputs are not permitted"
# Ref: https://github.com/BerriAI/litellm/issues/22847
remove_custom_field_from_tools(anthropic_messages_request)
normalize_tool_input_schema_types_for_bedrock_invoke(anthropic_messages_request)
ensure_bedrock_anthropic_messages_tool_names(anthropic_messages_request)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from litellm.llms.bedrock.common_utils import (
ensure_bedrock_anthropic_messages_tool_names,
normalize_tool_input_schema_types_for_bedrock_invoke,
remove_custom_field_from_tools,
)
from litellm.constants import BEDROCK_MIN_THINKING_BUDGET_TOKENS
from litellm.llms.bedrock.messages.invoke_transformations.anthropic_claude3_transformation import (
Expand Down Expand Up @@ -246,60 +245,6 @@ def test_remove_ttl_from_cache_control():
request5 = {}
cfg._remove_ttl_from_cache_control(request5)
assert request5 == {}


def test_remove_custom_field_from_tools():
"""
Ensure the `custom` field is stripped from every tool definition.

Claude Code v2.1.69+ sends `custom: {defer_loading: true}` on tool
objects. Bedrock does not accept this extra field and returns
"Extra inputs are not permitted".

Ref: https://github.com/BerriAI/litellm/issues/22847
"""

# Case 1: tool with `custom` field should have it removed
request = {
"tools": [
{
"name": "Read",
"description": "Read a file",
"input_schema": {"type": "object", "properties": {}},
"custom": {"defer_loading": True},
},
{
"name": "Write",
"description": "Write a file",
"input_schema": {"type": "object", "properties": {}},
},
]
}

remove_custom_field_from_tools(request)

for tool in request["tools"]:
assert "custom" not in tool, f"Tool {tool['name']} still has 'custom' field"
# Other fields should be preserved
assert request["tools"][0]["name"] == "Read"
assert request["tools"][1]["name"] == "Write"

# Case 2: request without tools key (should not raise error)
request2 = {"messages": [{"role": "user", "content": "hi"}]}
remove_custom_field_from_tools(request2)
assert "tools" not in request2

# Case 3: empty tools list (should not raise error)
request3 = {"tools": []}
remove_custom_field_from_tools(request3)
assert request3["tools"] == []

# Case 4: tools with None value (should not raise error)
request4 = {"tools": None}
remove_custom_field_from_tools(request4)
assert request4["tools"] is None


def test_normalize_tool_input_schema_types_for_bedrock_invoke():
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing test for preserved custom field passthrough

The deleted test verified that custom fields were stripped; now that the stripping is removed, there is no corresponding test verifying that custom: {defer_loading: true} is actually preserved (not dropped) in the final Bedrock request body. A regression here (e.g., a future developer re-adding the strip or an upstream transform clearing it) would go undetected. Consider adding a test asserting the custom field survives the full transformation pipeline alongside test_normalize_tool_input_schema_types_for_bedrock_invoke.

"""
Claude Code sends ``input_schema.type: \"custom\"`` for custom tools.
Expand Down