From 415682a0b166a34d3e29736f843103558750e7eb Mon Sep 17 00:00:00 2001 From: Will Koh Date: Wed, 29 Apr 2026 17:03:22 -0700 Subject: [PATCH] =?UTF-8?q?Propagate=20cache=5Fcontrol=20through=20Respons?= =?UTF-8?q?es=20API=20=E2=86=92=20Chat=20Completion=20transform?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transformation.py | 36 +++++++------ .../test_litellm_completion_responses.py | 52 +++++++++++++++++++ 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/litellm/responses/litellm_completion_transformation/transformation.py b/litellm/responses/litellm_completion_transformation/transformation.py index 48b12a5fba9..551f0ee552a 100644 --- a/litellm/responses/litellm_completion_transformation/transformation.py +++ b/litellm/responses/litellm_completion_transformation/transformation.py @@ -988,14 +988,16 @@ class LiteLLMCompletionResponsesConfig: # Since guardrails skip None content anyway, we return empty list to exclude it from structured messages if content is None: return [] - return [ - GenericChatCompletionMessage( - role=input_item.get("role") or "user", - content=LiteLLMCompletionResponsesConfig._transform_responses_api_content_to_chat_completion_content( - content - ), - ) - ] + message = GenericChatCompletionMessage( + role=input_item.get("role") or "user", + content=LiteLLMCompletionResponsesConfig._transform_responses_api_content_to_chat_completion_content( + content + ), + ) + cache_control = input_item.get("cache_control") + if cache_control is not None: + message["cache_control"] = cache_control # type: ignore[typeddict-unknown-key] + return [message] @staticmethod def _is_input_item_tool_call_output(input_item: Any) -> bool: @@ -1294,14 +1296,16 @@ class LiteLLMCompletionResponsesConfig: text_value = item.get("text") if text_value is None: continue - content_list.append( - { - "type": LiteLLMCompletionResponsesConfig._get_chat_completion_request_content_type( - item.get("type") or "text" - ), - "text": text_value, - } - ) + block = { + "type": LiteLLMCompletionResponsesConfig._get_chat_completion_request_content_type( + item.get("type") or "text" + ), + "text": text_value, + } + cache_control = item.get("cache_control") + if cache_control is not None: + block["cache_control"] = cache_control + content_list.append(block) return content_list else: raise ValueError(f"Invalid content type: {type(content)}") 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 5d8ff8022e5..8fa57eb6bb4 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 @@ -992,6 +992,58 @@ class TestContentTypeTransformation: assert result[0]["text"] == "valid text" assert result[1]["text"] == "another valid" + def test_cache_control_propagated_to_content_block(self): + """ + Test that a cache_control field on a Responses API content item is + propagated onto the Chat Completion content block. + """ + cache_control = {"type": "ephemeral"} + content = [ + {"type": "text", "text": "cached text", "cache_control": cache_control}, + {"type": "text", "text": "uncached text"}, + ] + result = LiteLLMCompletionResponsesConfig._transform_responses_api_content_to_chat_completion_content( + content + ) + assert len(result) == 2 + assert result[0]["text"] == "cached text" + assert result[0]["cache_control"] == cache_control + assert result[1]["text"] == "uncached text" + assert "cache_control" not in result[1] + + def test_cache_control_propagated_to_message(self): + """ + Test that a cache_control field on a Responses API input item is + propagated onto the resulting GenericChatCompletionMessage. + """ + cache_control = {"type": "ephemeral"} + input_item = { + "role": "user", + "content": [{"type": "input_text", "text": "hello"}], + "cache_control": cache_control, + } + result = LiteLLMCompletionResponsesConfig._transform_responses_api_input_item_to_chat_completion_message( + input_item + ) + assert len(result) == 1 + assert result[0]["role"] == "user" + assert result[0]["cache_control"] == cache_control + + def test_cache_control_absent_when_not_provided(self): + """ + Test that no cache_control key is added when the input item does not + carry one. + """ + input_item = { + "role": "user", + "content": [{"type": "input_text", "text": "hello"}], + } + result = LiteLLMCompletionResponsesConfig._transform_responses_api_input_item_to_chat_completion_message( + input_item + ) + assert len(result) == 1 + assert "cache_control" not in result[0] + class TestToolTransformation: """Test cases for tool transformation from Responses API to Chat Completion format"""