diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py index 855d0a2fdfd..1edf1499e19 100644 --- a/litellm/llms/anthropic/chat/transformation.py +++ b/litellm/llms/anthropic/chat/transformation.py @@ -964,11 +964,11 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): if mcp_servers: optional_params["mcp_servers"] = mcp_servers elif param == "tool_choice" or param == "parallel_tool_calls": - _tool_choice: Optional[ - AnthropicMessagesToolChoice - ] = self._map_tool_choice( - tool_choice=non_default_params.get("tool_choice"), - parallel_tool_use=non_default_params.get("parallel_tool_calls"), + _tool_choice: Optional[AnthropicMessagesToolChoice] = ( + self._map_tool_choice( + tool_choice=non_default_params.get("tool_choice"), + parallel_tool_use=non_default_params.get("parallel_tool_calls"), + ) ) if _tool_choice is not None: @@ -1068,9 +1068,9 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): self.map_openai_context_management_to_anthropic(value) ) if anthropic_context_management is not None: - optional_params[ - "context_management" - ] = anthropic_context_management + optional_params["context_management"] = ( + anthropic_context_management + ) elif param == "speed" and isinstance(value, str): # Pass through Anthropic-specific speed parameter for fast mode optional_params["speed"] = value @@ -1144,9 +1144,9 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): text=system_message_block["content"], ) if "cache_control" in system_message_block: - anthropic_system_message_content[ - "cache_control" - ] = system_message_block["cache_control"] + anthropic_system_message_content["cache_control"] = ( + system_message_block["cache_control"] + ) anthropic_system_message_list.append( anthropic_system_message_content ) @@ -1170,9 +1170,9 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): ) ) if "cache_control" in _content: - anthropic_system_message_content[ - "cache_control" - ] = _content["cache_control"] + anthropic_system_message_content["cache_control"] = ( + _content["cache_control"] + ) anthropic_system_message_list.append( anthropic_system_message_content @@ -1479,9 +1479,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): ) return _message - def extract_response_content( - self, completion_response: dict - ) -> Tuple[ + def extract_response_content(self, completion_response: dict) -> Tuple[ str, Optional[List[Any]], Optional[ @@ -1775,9 +1773,9 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): code_interpreter_results = self._build_code_interpreter_results( tool_results, code_by_id, container_id ) - provider_specific_fields[ - "code_interpreter_results" - ] = code_interpreter_results + provider_specific_fields["code_interpreter_results"] = ( + code_interpreter_results + ) container = completion_response.get("container") if container is not None: diff --git a/tests/test_litellm/llms/anthropic/test_anthropic_structured_output.py b/tests/test_litellm/llms/anthropic/test_anthropic_structured_output.py index ed0c7849f7b..2d1ff0052a0 100644 --- a/tests/test_litellm/llms/anthropic/test_anthropic_structured_output.py +++ b/tests/test_litellm/llms/anthropic/test_anthropic_structured_output.py @@ -16,48 +16,48 @@ class TestAnthropicStructuredOutput: def test_max_length_on_list_field_filtered(self): """ Test that max_length on List fields is filtered out for Anthropic models. - + Anthropic doesn't support 'maxItems' property for array types in their output_format.schema, so we need to filter it out. - + Related issue: https://github.com/BerriAI/litellm/issues/19444 """ from litellm.llms.anthropic.chat.transformation import AnthropicConfig - + # Define a Pydantic model with max_length on a List field class ResponseModel(BaseModel): items: List[str] = Field(max_length=5, description="List of items") name: str = Field(description="Name field") - + config = AnthropicConfig() - + # Get the JSON schema from the Pydantic model json_schema = config.get_json_schema_from_pydantic_object(ResponseModel) - + # Extract the actual schema schema = json_schema["json_schema"]["schema"] - + # Verify that maxItems is present in the raw schema (from Pydantic) assert "maxItems" in schema["properties"]["items"] - + # Now apply the Anthropic output format transformation response_format = { "type": "json_schema", - "json_schema": json_schema["json_schema"] + "json_schema": json_schema["json_schema"], } - + output_format = config.map_response_format_to_anthropic_output_format( response_format ) - + # Verify that maxItems is filtered out for Anthropic assert output_format is not None assert "schema" in output_format transformed_schema = output_format["schema"] - + # maxItems should be removed from the items property assert "maxItems" not in transformed_schema["properties"]["items"] - + # But other properties should remain assert "type" in transformed_schema["properties"]["items"] assert transformed_schema["properties"]["items"]["type"] == "array" @@ -66,29 +66,29 @@ class TestAnthropicStructuredOutput: def test_min_length_on_list_field_filtered(self): """ Test that min_length on List fields is filtered out for Anthropic models. - + Anthropic likely doesn't support 'minItems' either. """ from litellm.llms.anthropic.chat.transformation import AnthropicConfig - + class ResponseModel(BaseModel): items: List[str] = Field(min_length=2, description="List of items") - + config = AnthropicConfig() json_schema = config.get_json_schema_from_pydantic_object(ResponseModel) - + response_format = { "type": "json_schema", - "json_schema": json_schema["json_schema"] + "json_schema": json_schema["json_schema"], } - + output_format = config.map_response_format_to_anthropic_output_format( response_format ) - + assert output_format is not None transformed_schema = output_format["schema"] - + # minItems should be removed assert "minItems" not in transformed_schema["properties"]["items"] @@ -97,35 +97,38 @@ class TestAnthropicStructuredOutput: Test that array constraints are filtered at all nesting levels. """ from litellm.llms.anthropic.chat.transformation import AnthropicConfig - + class NestedItem(BaseModel): tags: List[str] = Field(max_length=3) - + class ResponseModel(BaseModel): items: List[NestedItem] = Field(max_length=5) - + config = AnthropicConfig() json_schema = config.get_json_schema_from_pydantic_object(ResponseModel) - + response_format = { "type": "json_schema", - "json_schema": json_schema["json_schema"] + "json_schema": json_schema["json_schema"], } - + output_format = config.map_response_format_to_anthropic_output_format( response_format ) - + assert output_format is not None transformed_schema = output_format["schema"] - + # Top-level maxItems should be removed assert "maxItems" not in transformed_schema["properties"]["items"] - + # Nested maxItems should also be removed if "$defs" in transformed_schema: nested_item_schema = transformed_schema["$defs"].get("NestedItem", {}) - if "properties" in nested_item_schema and "tags" in nested_item_schema["properties"]: + if ( + "properties" in nested_item_schema + and "tags" in nested_item_schema["properties"] + ): assert "maxItems" not in nested_item_schema["properties"]["tags"] def test_haiku_4_5_uses_native_structured_output(self): @@ -159,12 +162,11 @@ class TestAnthropicStructuredOutput: drop_params=False, ) # Should use native output_format, not json_tool_call - assert "output_format" in optional_params, ( - "claude-haiku-4-5 should use native output_format, not json_tool_call" - ) + assert ( + "output_format" in optional_params + ), "claude-haiku-4-5 should use native output_format, not json_tool_call" assert not any( - t.get("name") == "json_tool_call" - for t in optional_params.get("tools", []) + t.get("name") == "json_tool_call" for t in optional_params.get("tools", []) ), "claude-haiku-4-5 should not synthesize a json_tool_call" def test_other_constraints_preserved(self): @@ -186,7 +188,7 @@ class TestAnthropicStructuredOutput: response_format = { "type": "json_schema", - "json_schema": json_schema["json_schema"] + "json_schema": json_schema["json_schema"], } output_format = config.map_response_format_to_anthropic_output_format(