diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index ea1f81f9b36..82afe54b809 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -2441,11 +2441,14 @@ def anthropic_messages_pt( # noqa: PLR0915 elif m.get("type", "") == "document": user_content.append(cast(AnthropicMessagesDocumentParam, m)) elif m.get("type", "") == "file": - user_content.append( - anthropic_process_openai_file_message( - cast(ChatCompletionFileObject, m) - ) + _file_content_element = anthropic_process_openai_file_message( + cast(ChatCompletionFileObject, m) ) + _file_content_element = add_cache_control_to_content( + anthropic_content_element=_file_content_element, + original_content_element=dict(m), + ) + user_content.append(_file_content_element) elif isinstance(user_message_types_block["content"], str): _anthropic_content_text_element: AnthropicMessagesTextParam = { "type": "text", diff --git a/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py b/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py index 8d68539564c..438674bfb1a 100644 --- a/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py +++ b/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py @@ -2027,3 +2027,56 @@ def test_sanitize_messages_combined_case_a_and_case_d(): ) finally: litellm.modify_params = original + + +def test_anthropic_messages_pt_file_block_preserves_cache_control(): + """ + Test that cache_control is preserved on file-type content blocks + when translated to Anthropic document params. + Regression test for https://github.com/BerriAI/litellm/issues/23873 + """ + from litellm.litellm_core_utils.prompt_templates.factory import ( + anthropic_messages_pt, + ) + + messages = [ + { + "role": "user", + "content": [ + { + "type": "file", + "file": { + "filename": "doc.pdf", + "file_data": "data:application/pdf;base64,JVBERi0xLjQ=", + }, + "cache_control": {"type": "ephemeral"}, + }, + { + "type": "text", + "text": "Summarize this document.", + "cache_control": {"type": "ephemeral"}, + }, + ], + } + ] + + result = anthropic_messages_pt( + messages, model="claude-sonnet-4-20250514", llm_provider="anthropic" + ) + + content_blocks = result[0]["content"] + assert len(content_blocks) == 2 + + # Document block (from file) should preserve cache_control + doc_block = content_blocks[0] + assert doc_block["type"] == "document" + assert "cache_control" in doc_block, ( + "cache_control was dropped from file/document block" + ) + assert doc_block["cache_control"]["type"] == "ephemeral" + + # Text block should also preserve cache_control + text_block = content_blocks[1] + assert text_block["type"] == "text" + assert "cache_control" in text_block + assert text_block["cache_control"]["type"] == "ephemeral"