Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions litellm/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7941,6 +7941,16 @@ def validate_and_fix_openai_messages(messages: List):
if message.get("tool_calls"):
message["tool_calls"] = jsonify_tools(tools=message["tool_calls"])

content = message.get("content")
if isinstance(content, list):
normalized_content = []
for item in content:
if isinstance(item, str):
normalized_content.append({"type": "text", "text": item})
else:
normalized_content.append(item)
message["content"] = normalized_content

convert_msg_to_dict = cast(AllMessageValues, convert_to_dict(message))
cleaned_message = cleanup_none_field_in_message(message=convert_msg_to_dict)
new_messages.append(cleaned_message)
Expand Down
32 changes: 32 additions & 0 deletions tests/test_litellm/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
get_llm_provider,
get_optional_params_image_gen,
is_cached_message,
validate_and_fix_openai_messages,
)

# Adds the parent directory to the system path
Expand Down Expand Up @@ -3277,6 +3278,37 @@ def test_message_level_cache_control_non_dict_returns_false(self):
assert is_cached_message(message) is False


def test_normalize_array_of_strings_in_content():
"""String items in list content become OpenAI multimodal text parts; dict parts unchanged."""
only_strings = validate_and_fix_openai_messages(
[
{
"role": "user",
"content": ["what is the capital of France?"],
}
]
)
assert only_strings[0]["content"] == [
{"type": "text", "text": "what is the capital of France?"}
]

mixed = validate_and_fix_openai_messages(
[
{
"role": "user",
"content": [
"some text",
{"type": "image_url", "image_url": {"url": "https://example.com/x.png"}},
],
}
]
)
assert mixed[0]["content"] == [
{"type": "text", "text": "some text"},
{"type": "image_url", "image_url": {"url": "https://example.com/x.png"}},
]


@pytest.mark.asyncio
class TestProxyLoggingBudgetAlerts:
"""Test budget_alerts method in ProxyLogging class."""
Expand Down