feat(bedrock): forward strict and additionalProperties to Converse toolSpec (#29814)
* feat(bedrock): forward strict and additionalProperties to Converse toolSpec Bedrock Converse supports strict in toolSpec since 2026-02, but _bedrock_tools_pt only whitelisted type/properties/required/name/description, so strict: true was silently dropped and Claude-on-Bedrock ignored enum constraints that GPT and direct-Anthropic honored. Forward strict from the OpenAI function and additionalProperties from the schema (Bedrock requires the latter alongside strict), passing each only when present. https://claude.ai/code/session_01WQjWd8NfUB3vxERwudbHkv * fix(bedrock): only forward strict tool schemas to Claude on Converse Nova, Llama and GPT-OSS on Bedrock reject the strict field (BedrockException 'This model doesn't support the strict field'), and the GPT-OSS request-body test asserts strict/additionalProperties are stripped. Forwarding them to every model broke the llm_translation suite, so gate the forwarding on the anthropic base model since only Claude honours strict tool schemas on Bedrock.
This commit is contained in:
parent
273855b4e2
commit
d61f7747c0
@ -5496,6 +5496,7 @@ def _bedrock_tools_pt(
|
||||
]
|
||||
"""
|
||||
from litellm.llms.bedrock.common_utils import (
|
||||
get_bedrock_base_model,
|
||||
normalize_json_schema_custom_types_to_object,
|
||||
)
|
||||
from litellm.litellm_core_utils.prompt_templates.common_utils import unpack_defs
|
||||
@ -5503,6 +5504,11 @@ def _bedrock_tools_pt(
|
||||
_valid_json_schema_root_types = frozenset(
|
||||
("array", "boolean", "integer", "null", "number", "object", "string")
|
||||
)
|
||||
# Only Claude on Bedrock honours strict tool schemas; other families
|
||||
# (Nova, Llama, GPT-OSS) reject the strict field outright.
|
||||
supports_strict_tools = bool(
|
||||
model and get_bedrock_base_model(model).startswith("anthropic")
|
||||
)
|
||||
tool_block_list: List[BedrockToolBlock] = []
|
||||
for tool_idx, tool in enumerate(tools):
|
||||
# Check if tool is already a BedrockToolBlock (e.g., systemTool for Nova grounding)
|
||||
@ -5548,16 +5554,21 @@ def _bedrock_tools_pt(
|
||||
normalize_json_schema_custom_types_to_object(parameters)
|
||||
if parameters.get("type") not in _valid_json_schema_root_types:
|
||||
parameters["type"] = "object"
|
||||
tool_input_schema = BedrockToolInputSchemaBlock(
|
||||
json=BedrockToolJsonSchemaBlock(
|
||||
type=parameters["type"],
|
||||
properties=parameters.get("properties", {}),
|
||||
required=parameters.get("required", []),
|
||||
)
|
||||
json_schema = BedrockToolJsonSchemaBlock(
|
||||
type=parameters["type"],
|
||||
properties=parameters.get("properties", {}),
|
||||
required=parameters.get("required", []),
|
||||
)
|
||||
additional_properties = parameters.get("additionalProperties", None)
|
||||
if supports_strict_tools and additional_properties is not None:
|
||||
json_schema["additionalProperties"] = additional_properties
|
||||
tool_input_schema = BedrockToolInputSchemaBlock(json=json_schema)
|
||||
tool_spec = BedrockToolSpecBlock(
|
||||
inputSchema=tool_input_schema, name=name, description=description
|
||||
)
|
||||
strict = tool.get("function", {}).get("strict", None)
|
||||
if supports_strict_tools and strict is not None:
|
||||
tool_spec["strict"] = strict
|
||||
tool_block = BedrockToolBlock(toolSpec=tool_spec)
|
||||
tool_block_list.append(tool_block)
|
||||
|
||||
|
||||
@ -250,6 +250,7 @@ class ToolJsonSchemaBlock(TypedDict, total=False):
|
||||
type: Literal["object"]
|
||||
properties: dict
|
||||
required: List[str]
|
||||
additionalProperties: bool
|
||||
|
||||
|
||||
class ToolInputSchemaBlock(TypedDict):
|
||||
@ -260,6 +261,7 @@ class ToolSpecBlock(TypedDict, total=False):
|
||||
inputSchema: Required[ToolInputSchemaBlock]
|
||||
name: Required[str]
|
||||
description: str
|
||||
strict: bool
|
||||
|
||||
|
||||
class SystemToolBlock(TypedDict, total=False):
|
||||
|
||||
@ -898,6 +898,61 @@ def test_bedrock_tools_unpack_defs():
|
||||
_bedrock_tools_pt(tools=tools)
|
||||
|
||||
|
||||
def test_bedrock_tools_pt_strict_parameter():
|
||||
"""Regression for strict tools on the Bedrock Converse path.
|
||||
|
||||
Claude on Bedrock honours strict in toolSpec (with additionalProperties, which
|
||||
Bedrock requires alongside strict); without forwarding it the model ignores the
|
||||
enum constraint the caller asked for. Every other Bedrock family (Nova, Llama,
|
||||
GPT-OSS) rejects the strict field, so it must only be forwarded for Claude.
|
||||
"""
|
||||
tools_with_strict = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "generate_sql",
|
||||
"strict": True,
|
||||
"description": "Generate a SQL query",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {"query": {"type": "string"}},
|
||||
"required": ["query"],
|
||||
"additionalProperties": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
]
|
||||
result = _bedrock_tools_pt(
|
||||
tools_with_strict, model="anthropic.claude-sonnet-4-5-20250929-v1:0"
|
||||
)
|
||||
assert result[0]["toolSpec"]["strict"] is True
|
||||
assert result[0]["toolSpec"]["inputSchema"]["json"]["additionalProperties"] is False
|
||||
|
||||
result = _bedrock_tools_pt(tools_with_strict, model="us.amazon.nova-micro-v1:0")
|
||||
assert "strict" not in result[0]["toolSpec"]
|
||||
assert "additionalProperties" not in result[0]["toolSpec"]["inputSchema"]["json"]
|
||||
|
||||
tools_without_strict = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "generate_sql",
|
||||
"description": "Generate a SQL query",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {"query": {"type": "string"}},
|
||||
"required": ["query"],
|
||||
},
|
||||
},
|
||||
}
|
||||
]
|
||||
result = _bedrock_tools_pt(
|
||||
tools_without_strict, model="anthropic.claude-sonnet-4-5-20250929-v1:0"
|
||||
)
|
||||
assert "strict" not in result[0]["toolSpec"]
|
||||
assert "additionalProperties" not in result[0]["toolSpec"]["inputSchema"]["json"]
|
||||
|
||||
|
||||
def test_bedrock_image_processor_content_type_fallback_url_extension():
|
||||
"""
|
||||
Test that _post_call_image_processing falls back to URL extension
|
||||
|
||||
Loading…
Reference in New Issue
Block a user