mirror of
https://github.com/encounter/adk-python.git
synced 2026-07-09 18:19:28 -07:00
fix: Create correct object for image and video content in litellm
PiperOrigin-RevId: 783478779
This commit is contained in:
committed by
Copybara-Service
parent
c2058f3779
commit
bf7745f428
@@ -36,11 +36,13 @@ from litellm import ChatCompletionAssistantMessage
|
|||||||
from litellm import ChatCompletionAssistantToolCall
|
from litellm import ChatCompletionAssistantToolCall
|
||||||
from litellm import ChatCompletionDeveloperMessage
|
from litellm import ChatCompletionDeveloperMessage
|
||||||
from litellm import ChatCompletionFileObject
|
from litellm import ChatCompletionFileObject
|
||||||
|
from litellm import ChatCompletionImageObject
|
||||||
from litellm import ChatCompletionImageUrlObject
|
from litellm import ChatCompletionImageUrlObject
|
||||||
from litellm import ChatCompletionMessageToolCall
|
from litellm import ChatCompletionMessageToolCall
|
||||||
from litellm import ChatCompletionTextObject
|
from litellm import ChatCompletionTextObject
|
||||||
from litellm import ChatCompletionToolMessage
|
from litellm import ChatCompletionToolMessage
|
||||||
from litellm import ChatCompletionUserMessage
|
from litellm import ChatCompletionUserMessage
|
||||||
|
from litellm import ChatCompletionVideoObject
|
||||||
from litellm import ChatCompletionVideoUrlObject
|
from litellm import ChatCompletionVideoUrlObject
|
||||||
from litellm import completion
|
from litellm import completion
|
||||||
from litellm import CustomStreamWrapper
|
from litellm import CustomStreamWrapper
|
||||||
@@ -250,17 +252,25 @@ def _get_content(
|
|||||||
data_uri = f"data:{part.inline_data.mime_type};base64,{base64_string}"
|
data_uri = f"data:{part.inline_data.mime_type};base64,{base64_string}"
|
||||||
|
|
||||||
if part.inline_data.mime_type.startswith("image"):
|
if part.inline_data.mime_type.startswith("image"):
|
||||||
|
# Extract format from mime type (e.g., "image/png" -> "png")
|
||||||
|
format_type = part.inline_data.mime_type.split("/")[-1]
|
||||||
content_objects.append(
|
content_objects.append(
|
||||||
ChatCompletionImageUrlObject(
|
ChatCompletionImageObject(
|
||||||
type="image_url",
|
type="image_url",
|
||||||
image_url=data_uri,
|
image_url=ChatCompletionImageUrlObject(
|
||||||
|
url=data_uri, format=format_type
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif part.inline_data.mime_type.startswith("video"):
|
elif part.inline_data.mime_type.startswith("video"):
|
||||||
|
# Extract format from mime type (e.g., "video/mp4" -> "mp4")
|
||||||
|
format_type = part.inline_data.mime_type.split("/")[-1]
|
||||||
content_objects.append(
|
content_objects.append(
|
||||||
ChatCompletionVideoUrlObject(
|
ChatCompletionVideoObject(
|
||||||
type="video_url",
|
type="video_url",
|
||||||
video_url=data_uri,
|
video_url=ChatCompletionVideoUrlObject(
|
||||||
|
url=data_uri, format=format_type
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif part.inline_data.mime_type == "application/pdf":
|
elif part.inline_data.mime_type == "application/pdf":
|
||||||
|
|||||||
@@ -780,39 +780,6 @@ async def test_generate_content_async_with_tool_response(
|
|||||||
assert kwargs["messages"][2]["content"] == '{"result": "test_result"}'
|
assert kwargs["messages"][2]["content"] == '{"result": "test_result"}'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_generate_content_async(mock_acompletion, lite_llm_instance):
|
|
||||||
|
|
||||||
async for response in lite_llm_instance.generate_content_async(
|
|
||||||
LLM_REQUEST_WITH_FUNCTION_DECLARATION
|
|
||||||
):
|
|
||||||
assert response.content.role == "model"
|
|
||||||
assert response.content.parts[0].text == "Test response"
|
|
||||||
assert response.content.parts[1].function_call.name == "test_function"
|
|
||||||
assert response.content.parts[1].function_call.args == {
|
|
||||||
"test_arg": "test_value"
|
|
||||||
}
|
|
||||||
assert response.content.parts[1].function_call.id == "test_tool_call_id"
|
|
||||||
|
|
||||||
mock_acompletion.assert_called_once()
|
|
||||||
|
|
||||||
_, kwargs = mock_acompletion.call_args
|
|
||||||
assert kwargs["model"] == "test_model"
|
|
||||||
assert kwargs["messages"][0]["role"] == "user"
|
|
||||||
assert kwargs["messages"][0]["content"] == "Test prompt"
|
|
||||||
assert kwargs["tools"][0]["function"]["name"] == "test_function"
|
|
||||||
assert (
|
|
||||||
kwargs["tools"][0]["function"]["description"]
|
|
||||||
== "Test function description"
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
kwargs["tools"][0]["function"]["parameters"]["properties"]["test_arg"][
|
|
||||||
"type"
|
|
||||||
]
|
|
||||||
== "string"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_generate_content_async_with_usage_metadata(
|
async def test_generate_content_async_with_usage_metadata(
|
||||||
lite_llm_instance, mock_acompletion
|
lite_llm_instance, mock_acompletion
|
||||||
@@ -924,6 +891,43 @@ def test_content_to_message_param_function_call():
|
|||||||
assert tool_call["function"]["arguments"] == '{"test_arg": "test_value"}'
|
assert tool_call["function"]["arguments"] == '{"test_arg": "test_value"}'
|
||||||
|
|
||||||
|
|
||||||
|
def test_content_to_message_param_multipart_content():
|
||||||
|
"""Test handling of multipart content where final_content is a list with text objects."""
|
||||||
|
content = types.Content(
|
||||||
|
role="assistant",
|
||||||
|
parts=[
|
||||||
|
types.Part.from_text(text="text part"),
|
||||||
|
types.Part.from_bytes(data=b"test_image_data", mime_type="image/png"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
message = _content_to_message_param(content)
|
||||||
|
assert message["role"] == "assistant"
|
||||||
|
# When content is a list and the first element is a text object with type "text",
|
||||||
|
# it should extract the text (for providers like ollama_chat that don't handle lists well)
|
||||||
|
# This is the behavior implemented in the fix
|
||||||
|
assert message["content"] == "text part"
|
||||||
|
assert message["tool_calls"] is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_content_to_message_param_single_text_object_in_list():
|
||||||
|
"""Test extraction of text from single text object in list (for ollama_chat compatibility)."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
# Mock _get_content to return a list with single text object
|
||||||
|
with patch("google.adk.models.lite_llm._get_content") as mock_get_content:
|
||||||
|
mock_get_content.return_value = [{"type": "text", "text": "single text"}]
|
||||||
|
|
||||||
|
content = types.Content(
|
||||||
|
role="assistant",
|
||||||
|
parts=[types.Part.from_text(text="single text")],
|
||||||
|
)
|
||||||
|
message = _content_to_message_param(content)
|
||||||
|
assert message["role"] == "assistant"
|
||||||
|
# Should extract the text from the single text object
|
||||||
|
assert message["content"] == "single text"
|
||||||
|
assert message["tool_calls"] is None
|
||||||
|
|
||||||
|
|
||||||
def test_message_to_generate_content_response_text():
|
def test_message_to_generate_content_response_text():
|
||||||
message = ChatCompletionAssistantMessage(
|
message = ChatCompletionAssistantMessage(
|
||||||
role="assistant",
|
role="assistant",
|
||||||
@@ -971,7 +975,11 @@ def test_get_content_image():
|
|||||||
]
|
]
|
||||||
content = _get_content(parts)
|
content = _get_content(parts)
|
||||||
assert content[0]["type"] == "image_url"
|
assert content[0]["type"] == "image_url"
|
||||||
assert content[0]["image_url"] == "data:image/png;base64,dGVzdF9pbWFnZV9kYXRh"
|
assert (
|
||||||
|
content[0]["image_url"]["url"]
|
||||||
|
== "data:image/png;base64,dGVzdF9pbWFnZV9kYXRh"
|
||||||
|
)
|
||||||
|
assert content[0]["image_url"]["format"] == "png"
|
||||||
|
|
||||||
|
|
||||||
def test_get_content_video():
|
def test_get_content_video():
|
||||||
@@ -980,7 +988,11 @@ def test_get_content_video():
|
|||||||
]
|
]
|
||||||
content = _get_content(parts)
|
content = _get_content(parts)
|
||||||
assert content[0]["type"] == "video_url"
|
assert content[0]["type"] == "video_url"
|
||||||
assert content[0]["video_url"] == "data:video/mp4;base64,dGVzdF92aWRlb19kYXRh"
|
assert (
|
||||||
|
content[0]["video_url"]["url"]
|
||||||
|
== "data:video/mp4;base64,dGVzdF92aWRlb19kYXRh"
|
||||||
|
)
|
||||||
|
assert content[0]["video_url"]["format"] == "mp4"
|
||||||
|
|
||||||
|
|
||||||
def test_to_litellm_role():
|
def test_to_litellm_role():
|
||||||
|
|||||||
Reference in New Issue
Block a user