diff --git a/litellm/responses/litellm_completion_transformation/transformation.py b/litellm/responses/litellm_completion_transformation/transformation.py index 8052f69f22b..217ed711d37 100644 --- a/litellm/responses/litellm_completion_transformation/transformation.py +++ b/litellm/responses/litellm_completion_transformation/transformation.py @@ -1190,7 +1190,9 @@ class LiteLLMCompletionResponsesConfig: "detail", ]: if hasattr(item, attr): - item_dict[attr] = getattr(item, attr) + val = getattr(item, attr) + if val is not None: + item_dict[attr] = val return item_dict @staticmethod diff --git a/litellm/utils.py b/litellm/utils.py index 3fdf0a5eee6..d1c7af7050d 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -7210,9 +7210,7 @@ def is_cached_message(message: AllMessageValues) -> bool: # Check message-level cache_control (set by cache_control_injection_points hook for string content) message_level_cache_control = ( - message.get("cache_control") - if isinstance(message, dict) - else getattr(message, "cache_control", None) + message.get("cache_control") if isinstance(message, dict) else getattr(message, "cache_control", None) ) if message_level_cache_control is not None: cc_type = ( @@ -7243,14 +7241,9 @@ def is_cached_message(message: AllMessageValues) -> bool: cache_control = getattr(content_item, "cache_control", None) item_type = getattr(content_item, "type", None) - if ( - item_type == "text" - and cache_control is not None - ): + if item_type == "text" and cache_control is not None: cc_type = ( - cache_control.get("type") - if isinstance(cache_control, dict) - else getattr(cache_control, "type", None) + cache_control.get("type") if isinstance(cache_control, dict) else getattr(cache_control, "type", None) ) if cc_type == "ephemeral": return True diff --git a/tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py b/tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py index 4b07d638261..537753ec3e1 100644 --- a/tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py +++ b/tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py @@ -2663,15 +2663,22 @@ class TestCacheControlPreservation: def dict(self): return {"type": self.type, "text": self.text, "cache_control": self.cache_control} - content = [MockContentBlock(), MockPydanticV2Block(), MockPydanticV1Block()] + class MockBlockWithNoneCacheControl: + def __init__(self): + self.type = "text" + self.text = "hello none" + self.cache_control = None + + content = [MockContentBlock(), MockPydanticV2Block(), MockPydanticV1Block(), MockBlockWithNoneCacheControl()] result = LiteLLMCompletionResponsesConfig._transform_responses_api_content_to_chat_completion_content(content) assert isinstance(result, list) - assert len(result) == 3 + assert len(result) == 4 assert result[0]["cache_control"] == {"type": "ephemeral"} assert result[1]["cache_control"] == {"type": "ephemeral"} assert result[2]["cache_control"] == {"type": "ephemeral"} assert result[1]["text"] == "hello v2" assert result[2]["text"] == "hello v1" + assert "cache_control" not in result[3] def test_is_cached_message_for_object_message_and_content_item(self): """Test is_cached_message on custom objects / models."""