mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
fix(bedrock): don't raise a retryable error on malformed tool call arguments
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
71b825a7f0
commit
7f18a84bcd
2 changed files with 49 additions and 3 deletions
|
|
@ -3682,7 +3682,10 @@ def _convert_to_bedrock_tool_call_invoke(
|
|||
# '{"cmd":"a"}{"cmd":"b"}{"cmd":"c"}'
|
||||
# Split them and emit one toolUse block per object.
|
||||
# Fixes: https://github.com/BerriAI/litellm/issues/20543
|
||||
parsed_objects = split_concatenated_json_objects(arguments)
|
||||
try:
|
||||
parsed_objects = split_concatenated_json_objects(arguments)
|
||||
except json.JSONDecodeError:
|
||||
parsed_objects = []
|
||||
if parsed_objects:
|
||||
# First object keeps the original tool id.
|
||||
for obj_idx, obj in enumerate(parsed_objects):
|
||||
|
|
@ -3718,8 +3721,13 @@ def _convert_to_bedrock_tool_call_invoke(
|
|||
_parts_list.append(cache_point_block)
|
||||
return _parts_list
|
||||
except Exception as e:
|
||||
raise Exception(
|
||||
"Unable to convert openai tool calls={} to bedrock tool calls. Received error={}".format(tool_calls, str(e))
|
||||
tool_call_ids = [tool.get("id") for tool in tool_calls if isinstance(tool, dict)]
|
||||
raise litellm.BadRequestError(
|
||||
message="Unable to convert openai tool calls with ids={} to bedrock tool calls. Received error={}".format(
|
||||
tool_call_ids, str(e)
|
||||
),
|
||||
model=model or "",
|
||||
llm_provider="bedrock",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2286,6 +2286,44 @@ def test_bedrock_tool_call_invoke_non_dict_arguments():
|
|||
assert result[0]["toolUse"]["input"] == {}
|
||||
|
||||
|
||||
def test_bedrock_tool_call_invoke_truncated_json_arguments():
|
||||
"""
|
||||
Truncated tool call arguments (issue #35303) must not raise. A client replaying a
|
||||
partially streamed tool call would otherwise trigger a pre-network exception that the
|
||||
router maps to a retryable APIConnectionError and retries through the fallback graph.
|
||||
"""
|
||||
tool_calls = [
|
||||
{
|
||||
"id": "tooluse_MAh2QLVjBRkvi5QJkLQ08V",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "replace_note_content",
|
||||
"arguments": '{"note_id": "999af35c-4061-4ece-8581-7d43fc988ba4", "title": "WG"',
|
||||
},
|
||||
}
|
||||
]
|
||||
result = _convert_to_bedrock_tool_call_invoke(tool_calls)
|
||||
assert len(result) == 1
|
||||
assert result[0]["toolUse"]["toolUseId"] == "tooluse_MAh2QLVjBRkvi5QJkLQ08V"
|
||||
assert result[0]["toolUse"]["input"] == {}
|
||||
|
||||
|
||||
def test_bedrock_tool_call_invoke_unconvertible_raises_non_retryable_bad_request():
|
||||
"""
|
||||
Conversion failures are client input errors, so they must surface as a non-retryable
|
||||
BadRequestError instead of a bare Exception that maps to APIConnectionError, and the
|
||||
message must not embed the tool call payload (issue #35303).
|
||||
"""
|
||||
tool_calls = [{"id": "call_bad", "type": "function", "function": None}]
|
||||
|
||||
with pytest.raises(litellm.BadRequestError) as exc_info:
|
||||
_convert_to_bedrock_tool_call_invoke(tool_calls)
|
||||
|
||||
assert exc_info.value.status_code == 400
|
||||
assert "call_bad" in str(exc_info.value)
|
||||
assert "function" not in str(exc_info.value).split("Received error=")[0]
|
||||
|
||||
|
||||
def test_make_valid_bedrock_tool_name_preserves_hyphens():
|
||||
assert make_valid_bedrock_tool_name("my-tool") == "my-tool"
|
||||
assert (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue