diff --git a/litellm/utils.py b/litellm/utils.py index 7732cd88cb5..d28b9cb03a0 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -7973,7 +7973,12 @@ def cleanup_none_field_in_message(message: AllMessageValues): remove None fields in the message - e.g. {"function": None} - some providers raise validation errors """ new_message: Final = message.copy() - return {k: v for k, v in new_message.items() if v is not None} + return { + k: v + for k, v in new_message.items() + if v is not None + or (k == "content" and new_message.get("role") == "assistant" and new_message.get("tool_calls")) + } def validate_chat_completion_user_messages(messages: list[AllMessageValues]): diff --git a/tests/test_litellm/test_message_normalization.py b/tests/test_litellm/test_message_normalization.py new file mode 100644 index 00000000000..9483ade4637 --- /dev/null +++ b/tests/test_litellm/test_message_normalization.py @@ -0,0 +1,33 @@ +from litellm.utils import cleanup_none_field_in_message + + +def test_cleanup_preserves_null_content_for_assistant_tool_calls(): + message = { + "role": "assistant", + "content": None, + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": {"name": "get_weather", "arguments": "{}"}, + } + ], + } + + result = cleanup_none_field_in_message(message) + + assert result == message + assert "content" in result + + +def test_cleanup_still_removes_unneeded_null_fields(): + message = { + "role": "assistant", + "content": None, + "tool_calls": None, + "function_call": None, + } + + result = cleanup_none_field_in_message(message) + + assert result == {"role": "assistant"}