mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
fix: lost tool_calls when streaming has both text and tool_calls
This commit is contained in:
parent
acadaca7e7
commit
b2588dc399
2 changed files with 57 additions and 4 deletions
|
|
@ -740,9 +740,7 @@ class LiteLLMAnthropicMessagesAdapter:
|
|||
from litellm.types.llms.anthropic import TextBlock, ToolUseBlock
|
||||
|
||||
for choice in choices:
|
||||
if choice.delta.content is not None and len(choice.delta.content) > 0:
|
||||
return "text", TextBlock(type="text", text="")
|
||||
elif (
|
||||
if (
|
||||
choice.delta.tool_calls is not None
|
||||
and len(choice.delta.tool_calls) > 0
|
||||
and choice.delta.tool_calls[0].function is not None
|
||||
|
|
@ -753,6 +751,8 @@ class LiteLLMAnthropicMessagesAdapter:
|
|||
name=choice.delta.tool_calls[0].function.name or "",
|
||||
input={}, # type: ignore[typeddict-item]
|
||||
)
|
||||
elif choice.delta.content is not None and len(choice.delta.content) > 0:
|
||||
return "text", TextBlock(type="text", text="")
|
||||
elif isinstance(choice, StreamingChoices) and hasattr(
|
||||
choice.delta, "thinking_blocks"
|
||||
):
|
||||
|
|
@ -796,7 +796,7 @@ class LiteLLMAnthropicMessagesAdapter:
|
|||
for choice in choices:
|
||||
if choice.delta.content is not None and len(choice.delta.content) > 0:
|
||||
text += choice.delta.content
|
||||
elif choice.delta.tool_calls is not None:
|
||||
if choice.delta.tool_calls is not None:
|
||||
partial_json = ""
|
||||
for tool in choice.delta.tool_calls:
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -1055,3 +1055,56 @@ def test_translate_anthropic_messages_to_openai_tool_result_single_item_backward
|
|||
f"got {type(tool_message['content'])}"
|
||||
)
|
||||
assert tool_message["content"] == "72°F and sunny"
|
||||
|
||||
|
||||
def test_streaming_chunk_with_both_text_and_tool_calls_issue_18238():
|
||||
"""
|
||||
When a streaming choice contains both text content and tool_calls,
|
||||
both should be processed (tool_calls should not be ignored).
|
||||
"""
|
||||
# streaming choice with both text and tool_calls
|
||||
choices = [
|
||||
StreamingChoices(
|
||||
finish_reason=None,
|
||||
index=0,
|
||||
delta=Delta(
|
||||
provider_specific_fields=None,
|
||||
content="Here is some text for litellm",
|
||||
role=None,
|
||||
function_call=None,
|
||||
tool_calls=[
|
||||
ChatCompletionDeltaToolCall(
|
||||
id="toolu_bdrk_013xRVejhv3ybmLEGCoZib2b",
|
||||
function=Function(arguments='{"cmd": "init"}', name="Bash"),
|
||||
type="function",
|
||||
index=0,
|
||||
)
|
||||
],
|
||||
audio=None,
|
||||
),
|
||||
logprobs=None,
|
||||
)
|
||||
]
|
||||
|
||||
adapter = LiteLLMAnthropicMessagesAdapter()
|
||||
|
||||
# When both text and tool_calls exist, tool_calls (input_json_delta) takes priority
|
||||
(
|
||||
type_of_content,
|
||||
content_block_delta,
|
||||
) = adapter._translate_streaming_openai_chunk_to_anthropic(choices=choices)
|
||||
|
||||
assert type_of_content == "input_json_delta"
|
||||
assert content_block_delta["partial_json"] == '{"cmd": "init"}'
|
||||
|
||||
# When both text and tool_calls exist, tool_use should be detected and tool name captured
|
||||
(
|
||||
block_type,
|
||||
content_block_start,
|
||||
) = adapter._translate_streaming_openai_chunk_to_anthropic_content_block(
|
||||
choices=choices
|
||||
)
|
||||
|
||||
assert block_type == "tool_use"
|
||||
assert content_block_start["name"] == "Bash"
|
||||
assert content_block_start["id"] == "toolu_bdrk_013xRVejhv3ybmLEGCoZib2b"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue