converse_transformation: pass 'description' if set in response_format (#8907)

* test(test_bedrock_completion.py): e2e test ensuring tool description is passed in

* fix(converse_transformation.py): pass description, if set

* fix(transformation.py): Fixes https://github.com/BerriAI/litellm/issues/8767#issuecomment-2689887663
This commit is contained in:
Krish Dholakia
2025-02-28 18:47:07 -08:00
committed by GitHub
parent a65bfab697
commit c8dc4f3eec
5 changed files with 109 additions and 5 deletions
@@ -123,6 +123,7 @@ class AnthropicConfig(BaseConfig):
prompt_caching_set: bool = False,
pdf_used: bool = False,
is_vertex_request: bool = False,
user_anthropic_beta_headers: Optional[List[str]] = None,
) -> dict:
betas = []
@@ -139,6 +140,9 @@ class AnthropicConfig(BaseConfig):
"content-type": "application/json",
}
if user_anthropic_beta_headers is not None:
betas.extend(user_anthropic_beta_headers)
# Don't send any beta headers to Vertex, Vertex has failed requests when they are sent
if is_vertex_request is True:
pass
@@ -795,6 +799,13 @@ class AnthropicConfig(BaseConfig):
headers=cast(httpx.Headers, headers),
)
def _get_user_anthropic_beta_headers(
self, anthropic_beta_header: Optional[str]
) -> Optional[List[str]]:
if anthropic_beta_header is None:
return None
return anthropic_beta_header.split(",")
def validate_environment(
self,
headers: dict,
@@ -815,13 +826,18 @@ class AnthropicConfig(BaseConfig):
prompt_caching_set = self.is_cache_control_set(messages=messages)
computer_tool_used = self.is_computer_tool_used(tools=tools)
pdf_used = self.is_pdf_used(messages=messages)
user_anthropic_beta_headers = self._get_user_anthropic_beta_headers(
anthropic_beta_header=headers.get("anthropic-beta")
)
anthropic_headers = self.get_anthropic_headers(
computer_tool_used=computer_tool_used,
prompt_caching_set=prompt_caching_set,
pdf_used=pdf_used,
api_key=api_key,
is_vertex_request=optional_params.get("is_vertex_request", False),
user_anthropic_beta_headers=user_anthropic_beta_headers,
)
headers = {**headers, **anthropic_headers}
return headers
@@ -167,6 +167,7 @@ class AmazonConverseConfig(BaseConfig):
self,
json_schema: Optional[dict] = None,
schema_name: str = "json_tool_call",
description: Optional[str] = None,
) -> ChatCompletionToolParam:
"""
Handles creating a tool call for getting responses in JSON format.
@@ -189,11 +190,15 @@ class AmazonConverseConfig(BaseConfig):
else:
_input_schema = json_schema
tool_param_function_chunk = ChatCompletionToolParamFunctionChunk(
name=schema_name, parameters=_input_schema
)
if description:
tool_param_function_chunk["description"] = description
_tool = ChatCompletionToolParam(
type="function",
function=ChatCompletionToolParamFunctionChunk(
name=schema_name, parameters=_input_schema
),
function=tool_param_function_chunk,
)
return _tool
@@ -214,12 +219,14 @@ class AmazonConverseConfig(BaseConfig):
json_schema: Optional[dict] = None
schema_name: str = ""
description: Optional[str] = None
if "response_schema" in value:
json_schema = value["response_schema"]
schema_name = "json_tool_call"
elif "json_schema" in value:
json_schema = value["json_schema"]["schema"]
schema_name = value["json_schema"]["name"]
description = value["json_schema"].get("description")
"""
Follow similar approach to anthropic - translate to a single tool call.
@@ -228,10 +235,10 @@ class AmazonConverseConfig(BaseConfig):
- You should set tool_choice (see Forcing tool use) to instruct the model to explicitly use that tool
- Remember that the model will pass the input to the tool, so the name of the tool and description should be from the models perspective.
"""
_tool_choice = {"name": schema_name, "type": "tool"}
_tool = self._create_json_tool_call_for_response_format(
json_schema=json_schema,
schema_name=schema_name if schema_name != "" else "json_tool_call",
description=description,
)
optional_params["tools"] = [_tool]
if litellm.utils.supports_tool_choice(
+4 -1
View File
@@ -19,7 +19,10 @@ model_list:
model: bedrock/invoke/us.anthropic.claude-3-7-sonnet-20250219-v1:0
- model_name: bedrock-claude-3-5-sonnet
litellm_params:
model: bedrock/invoke/anthropic.claude-3-5-sonnet-20240620-v1:0
model: bedrock/invoke/us.anthropic.claude-3-5-sonnet-20240620-v1:0
- model_name: bedrock-nova
litellm_params:
model: bedrock/us.amazon.nova-pro-v1:0
litellm_settings:
callbacks: ["langfuse"]
@@ -1224,3 +1224,42 @@ def test_anthropic_thinking_output_stream(model):
assert reasoning_content_exists
except litellm.Timeout:
pytest.skip("Model is timing out")
def test_anthropic_custom_headers():
from litellm import completion
from litellm.llms.custom_httpx.http_handler import HTTPHandler
client = HTTPHandler()
tools = [
{
"type": "computer_20241022",
"function": {
"name": "get_current_weather",
"parameters": {
"display_height_px": 100,
"display_width_px": 100,
"display_number": 1,
},
},
}
]
with patch.object(client, "post") as mock_post:
try:
resp = completion(
model="claude-3-5-sonnet-20240620",
headers={"anthropic-beta": "structured-output-2024-03-01"},
messages=[
{"role": "user", "content": "What is the capital of France?"}
],
client=client,
tools=tools,
)
except Exception as e:
print(f"Error: {e}")
mock_post.assert_called_once()
headers = mock_post.call_args[1]["headers"]
assert "structured-output-2024-03-01" in headers["anthropic-beta"]
@@ -2715,3 +2715,42 @@ def test_bedrock_top_k_param(model, expected_params):
assert data["top_k"] == 2
else:
assert data["additionalModelRequestFields"] == expected_params
def test_bedrock_description_param():
from litellm import completion
from litellm.llms.custom_httpx.http_handler import HTTPHandler
client = HTTPHandler()
with patch.object(client, "post") as mock_post:
try:
response = completion(
model="bedrock/us.amazon.nova-pro-v1:0",
messages=[
{"role": "user", "content": "What is the meaning of this poem?"}
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "meaning_reasoning",
"description": "Find the meaning inside a poem",
"schema": {
"type": "object",
"properties": {"meaning": {"type": "string"}},
},
},
},
client=client,
)
except Exception as e:
print(e)
mock_post.assert_called_once()
request_body = json.loads(mock_post.call_args.kwargs["data"])
request_body_str = json.dumps(request_body, indent=4, default=str)
print("request_body=", request_body_str)
assert (
"Find the meaning inside a poem" in request_body_str
) # assert description is passed