Skip to content

Commit f74e87e

Browse files
committed
fix: patch lite_llm to support image_url and video_url
1 parent 8a7f970 commit f74e87e

2 files changed

Lines changed: 149 additions & 0 deletions

File tree

veadk/agent.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
DEFAULT_DESCRIPTION,
5050
DEFAULT_INSTRUCTION,
5151
)
52+
from veadk.models.lite_llm_ext import patch_litellm_get_content
5253
from veadk.prompts.prompt_manager import BasePromptManager
5354
from veadk.tracing.base_tracer import BaseTracer
5455
from veadk.utils.logger import get_logger
@@ -57,6 +58,7 @@
5758

5859
patch_tracer()
5960
patch_asyncio()
61+
patch_litellm_get_content()
6062
logger = get_logger(__name__)
6163

6264

veadk/models/lite_llm_ext.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
def patch_litellm_get_content():
17+
import base64
18+
from typing import Union, Iterable
19+
20+
import litellm
21+
from litellm import OpenAIMessageContent
22+
from google.adk.models import lite_llm
23+
from google.genai import types
24+
25+
async def _patch_get_content(
26+
parts: Iterable[types.Part],
27+
*,
28+
provider: str = "",
29+
) -> Union[OpenAIMessageContent, str]:
30+
from google.adk.models.lite_llm import (
31+
_decode_inline_text_data,
32+
_SUPPORTED_FILE_CONTENT_MIME_TYPES,
33+
_FILE_ID_REQUIRED_PROVIDERS,
34+
ChatCompletionFileUrlObject,
35+
)
36+
37+
content_objects = []
38+
for part in parts:
39+
if part.text:
40+
if len(parts) == 1:
41+
return part.text
42+
content_objects.append(
43+
{
44+
"type": "text",
45+
"text": part.text,
46+
}
47+
)
48+
elif (
49+
part.inline_data
50+
and part.inline_data.data
51+
and part.inline_data.mime_type
52+
):
53+
if part.inline_data.mime_type.startswith("text/"):
54+
decoded_text = _decode_inline_text_data(part.inline_data.data)
55+
if len(parts) == 1:
56+
return decoded_text
57+
content_objects.append(
58+
{
59+
"type": "text",
60+
"text": decoded_text,
61+
}
62+
)
63+
continue
64+
base64_string = base64.b64encode(part.inline_data.data).decode("utf-8")
65+
data_uri = f"data:{part.inline_data.mime_type};base64,{base64_string}"
66+
# LiteLLM providers extract the MIME type from the data URI; avoid
67+
# passing a separate `format` field that some backends reject.
68+
69+
if part.inline_data.mime_type.startswith("image"):
70+
content_objects.append(
71+
{
72+
"type": "image_url",
73+
"image_url": {"url": data_uri},
74+
}
75+
)
76+
elif part.inline_data.mime_type.startswith("video"):
77+
content_objects.append(
78+
{
79+
"type": "video_url",
80+
"video_url": {"url": data_uri},
81+
}
82+
)
83+
elif part.inline_data.mime_type.startswith("audio"):
84+
content_objects.append(
85+
{
86+
"type": "audio_url",
87+
"audio_url": {"url": data_uri},
88+
}
89+
)
90+
elif part.inline_data.mime_type in _SUPPORTED_FILE_CONTENT_MIME_TYPES:
91+
# OpenAI/Azure require file_id from uploaded file, not inline data
92+
if provider in _FILE_ID_REQUIRED_PROVIDERS:
93+
file_response = await litellm.acreate_file(
94+
file=part.inline_data.data,
95+
purpose="assistants",
96+
custom_llm_provider=provider,
97+
)
98+
content_objects.append(
99+
{
100+
"type": "file",
101+
"file": {"file_id": file_response.id},
102+
}
103+
)
104+
else:
105+
content_objects.append(
106+
{
107+
"type": "file",
108+
"file": {"file_data": data_uri},
109+
}
110+
)
111+
else:
112+
raise ValueError(
113+
"LiteLlm(BaseLlm) does not support content part with MIME type "
114+
f"{part.inline_data.mime_type}."
115+
)
116+
elif part.file_data and part.file_data.file_uri:
117+
# Modify to enable file_data to support image, video URLs
118+
if part.file_data.mime_type.startswith("image"):
119+
content_objects.append(
120+
{
121+
"type": "image_url",
122+
"image_url": {"url": part.file_data.file_uri},
123+
}
124+
)
125+
elif part.file_data.mime_type.startswith("video"):
126+
content_objects.append(
127+
{
128+
"type": "video_url",
129+
"video_url": {"url": part.file_data.file_uri},
130+
}
131+
)
132+
else:
133+
# The original part of lite_llm `_get_content`
134+
file_object: ChatCompletionFileUrlObject = {
135+
"file_id": part.file_data.file_uri,
136+
}
137+
content_objects.append(
138+
{
139+
"type": "file",
140+
"file": file_object,
141+
}
142+
)
143+
144+
return content_objects
145+
146+
# Apply the patch
147+
lite_llm._get_content = _patch_get_content

0 commit comments

Comments
 (0)