mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-09 03:08:09 +00:00
Fix missing content in Anthropic to OpenAI conversion (#17693)
This commit is contained in:
@@ -613,7 +613,14 @@ class LiteLLMAnthropicMessagesAdapter:
|
||||
)
|
||||
)
|
||||
|
||||
# Handle tool calls
|
||||
# Handle text content
|
||||
if choice.message.content is not None:
|
||||
new_content.append(
|
||||
AnthropicResponseContentBlockText(
|
||||
type="text", text=choice.message.content
|
||||
)
|
||||
)
|
||||
# Handle tool calls (in parallel to text content)
|
||||
if (
|
||||
choice.message.tool_calls is not None
|
||||
and len(choice.message.tool_calls) > 0
|
||||
@@ -642,13 +649,6 @@ class LiteLLMAnthropicMessagesAdapter:
|
||||
provider_specific_fields
|
||||
)
|
||||
new_content.append(tool_use_block)
|
||||
# Handle text content
|
||||
elif choice.message.content is not None:
|
||||
new_content.append(
|
||||
AnthropicResponseContentBlockText(
|
||||
type="text", text=choice.message.content
|
||||
)
|
||||
)
|
||||
|
||||
return new_content
|
||||
|
||||
|
||||
+78
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
import sys
|
||||
from typing import Any, cast
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -20,7 +21,9 @@ from litellm.types.utils import (
|
||||
Delta,
|
||||
Function,
|
||||
Message,
|
||||
ModelResponse,
|
||||
StreamingChoices,
|
||||
Usage,
|
||||
)
|
||||
|
||||
|
||||
@@ -341,6 +344,81 @@ def test_translate_openai_content_to_anthropic_empty_function_arguments():
|
||||
assert result[0].input == {}, "Empty function arguments should result in empty dict"
|
||||
|
||||
|
||||
def test_translate_openai_content_to_anthropic_text_and_tool_calls():
|
||||
"""Ensure content blocks contain both the assistant text + tool call data."""
|
||||
openai_choices = [
|
||||
Choices(
|
||||
message=Message(
|
||||
role="assistant",
|
||||
content="Calling get_weather now.",
|
||||
tool_calls=[
|
||||
ChatCompletionAssistantToolCall(
|
||||
id="call_weather",
|
||||
type="function",
|
||||
function=Function(
|
||||
name="get_weather",
|
||||
arguments='{"location": "Boston"}',
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
adapter = LiteLLMAnthropicMessagesAdapter()
|
||||
result = adapter._translate_openai_content_to_anthropic(choices=openai_choices)
|
||||
|
||||
assert len(result) == 2
|
||||
assert result[0].type == "text"
|
||||
assert result[0].text == "Calling get_weather now."
|
||||
assert result[1].type == "tool_use"
|
||||
assert result[1].id == "call_weather"
|
||||
assert result[1].name == "get_weather"
|
||||
assert result[1].input == {"location": "Boston"}
|
||||
|
||||
|
||||
def test_translate_openai_response_to_anthropic_text_and_tool_calls():
|
||||
"""`translate_openai_response_to_anthropic` should surface assistant text even when tools fire."""
|
||||
openai_response = ModelResponse(
|
||||
id="resp_text_tool",
|
||||
model="gpt-4o-mini",
|
||||
choices=[
|
||||
Choices(
|
||||
finish_reason="tool_calls",
|
||||
message=Message(
|
||||
role="assistant",
|
||||
content="Let me grab the current weather.",
|
||||
tool_calls=[
|
||||
ChatCompletionAssistantToolCall(
|
||||
id="call_tool_combo",
|
||||
type="function",
|
||||
function=Function(
|
||||
name="get_weather", arguments='{"location": "Paris"}'
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
usage=Usage(prompt_tokens=5, completion_tokens=2),
|
||||
)
|
||||
|
||||
adapter = LiteLLMAnthropicMessagesAdapter()
|
||||
anthropic_response = adapter.translate_openai_response_to_anthropic(
|
||||
response=openai_response
|
||||
)
|
||||
|
||||
anthropic_content = anthropic_response.get("content")
|
||||
assert anthropic_content is not None
|
||||
assert len(anthropic_content) == 2
|
||||
assert cast(Any, anthropic_content[0]).type == "text"
|
||||
assert cast(Any, anthropic_content[0]).text == "Let me grab the current weather."
|
||||
assert cast(Any, anthropic_content[1]).type == "tool_use"
|
||||
assert cast(Any, anthropic_content[1]).id == "call_tool_combo"
|
||||
assert cast(Any, anthropic_content[1]).input == {"location": "Paris"}
|
||||
assert anthropic_response.get("stop_reason") == "tool_use"
|
||||
|
||||
|
||||
def test_translate_streaming_openai_chunk_to_anthropic_with_partial_json():
|
||||
"""Test that partial tool arguments are correctly handled as input_json_delta."""
|
||||
choices = [
|
||||
|
||||
Reference in New Issue
Block a user