From 35912a2fb89e45dfdf2c045153131b005c0257b2 Mon Sep 17 00:00:00 2001 From: Ruiming Zhao Date: Tue, 11 Aug 2026 22:45:16 -0700 Subject: [PATCH 1/6] fix(anthropic): preserve document blocks in guardrail write-back --- .../prompt_templates/factory.py | 18 ++++++++++++ ...llm_core_utils_prompt_templates_factory.py | 28 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 76b3f47db18..7bb25cabf58 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -2418,6 +2418,24 @@ def anthropic_messages_pt( "url": image_url_value["url"], "format": image_url_value.get("format"), } + url_str = ( + image_url_value.get("url") if isinstance(image_url_value, dict) else image_url_value + ) + if isinstance(url_str, str) and _is_anthropic_document_data_uri(url_str): + synth_file_message: ChatCompletionFileObject = { + "type": "file", + "file": {"file_data": url_str}, + } + _document_content_element = anthropic_process_openai_file_message(synth_file_message) + _document_content_element = add_cache_control_to_content( + anthropic_content_element=cast( + AnthropicMessagesDocumentParam, + _document_content_element, + ), + original_content_element=dict(m), + ) + user_content.append(cast(AnthropicMessagesDocumentParam, _document_content_element)) + continue # Bedrock invoke models have format: invoke/... # Vertex AI Anthropic also doesn't support URL sources for images is_bedrock_invoke = model.lower().startswith("invoke/") 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 8edc6a91cbf..4a06ce02359 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 @@ -2163,6 +2163,34 @@ def test_anthropic_messages_pt_file_block_without_cache_control(): assert "cache_control" not in file_block +def test_anthropic_messages_pt_maps_pdf_data_uri_to_document_block(): + messages = [ + { + "role": "user", + "content": [ + { + "type": "image_url", + "image_url": {"url": "data:application/pdf;base64,AAAA"}, + } + ], + } + ] + + result = anthropic_messages_pt( + messages=messages, + model="claude-sonnet-4-5", + llm_provider="anthropic", + ) + + document = result[0]["content"][0] + assert document["type"] == "document" + assert document["source"] == { + "type": "base64", + "media_type": "application/pdf", + "data": "AAAA", + } + + # ── _convert_to_bedrock_tool_call_invoke tests ── From 5f6d07ff326424821d6262736c8614f2003fae1c Mon Sep 17 00:00:00 2001 From: Ruiming Zhao Date: Fri, 14 Aug 2026 16:37:44 -0700 Subject: [PATCH 2/6] fix(anthropic): build document blocks directly from the data URI --- .../prompt_templates/factory.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 7bb25cabf58..574d53c8313 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -2422,17 +2422,21 @@ def anthropic_messages_pt( image_url_value.get("url") if isinstance(image_url_value, dict) else image_url_value ) if isinstance(url_str, str) and _is_anthropic_document_data_uri(url_str): - synth_file_message: ChatCompletionFileObject = { - "type": "file", - "file": {"file_data": url_str}, - } - _document_content_element = anthropic_process_openai_file_message(synth_file_message) - _document_content_element = add_cache_control_to_content( - anthropic_content_element=cast( - AnthropicMessagesDocumentParam, - _document_content_element, + image_chunk: Final = convert_to_anthropic_image_obj( + openai_image_url=url_str, + format=None, + ) + _document_content_element: Final = AnthropicMessagesDocumentParam( + type="document", + source=AnthropicContentParamSource( + type="base64", + media_type=image_chunk["media_type"], + data=image_chunk["data"], ), - original_content_element=dict(m), + ) + add_cache_control_to_content( + anthropic_content_element=_document_content_element, + original_content_element=m, ) user_content.append(cast(AnthropicMessagesDocumentParam, _document_content_element)) continue From 65a1b296cb12b278d9d906c885ed62c2f79b4ad2 Mon Sep 17 00:00:00 2001 From: Ruiming Zhao Date: Fri, 14 Aug 2026 16:39:33 -0700 Subject: [PATCH 3/6] fix(anthropic): keep lint budget within ceiling for document write-back --- litellm/litellm_core_utils/prompt_templates/factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 574d53c8313..52762dc597e 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -2438,7 +2438,7 @@ def anthropic_messages_pt( anthropic_content_element=_document_content_element, original_content_element=m, ) - user_content.append(cast(AnthropicMessagesDocumentParam, _document_content_element)) + user_content.append(cast(AnthropicMessagesDocumentParam, _document_content_element)) # cast-ok: same union narrowing as above continue # Bedrock invoke models have format: invoke/... # Vertex AI Anthropic also doesn't support URL sources for images From e974ec4da8af1603631997490fbe73df225926ad Mon Sep 17 00:00:00 2001 From: Ruiming Zhao Date: Fri, 14 Aug 2026 17:00:37 -0700 Subject: [PATCH 4/6] style(anthropic): wrap document block append for ruff format --- litellm/litellm_core_utils/prompt_templates/factory.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 52762dc597e..2ce157ae3b6 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -2438,7 +2438,9 @@ def anthropic_messages_pt( anthropic_content_element=_document_content_element, original_content_element=m, ) - user_content.append(cast(AnthropicMessagesDocumentParam, _document_content_element)) # cast-ok: same union narrowing as above + user_content.append( + cast(AnthropicMessagesDocumentParam, _document_content_element) + ) # cast-ok: same union narrowing as above continue # Bedrock invoke models have format: invoke/... # Vertex AI Anthropic also doesn't support URL sources for images From aab5a3cf91b61e54538090fa9e64c3e5dbb4c11a Mon Sep 17 00:00:00 2001 From: Ruiming Zhao Date: Fri, 14 Aug 2026 17:09:34 -0700 Subject: [PATCH 5/6] fix(anthropic): drop redundant cast when appending document block --- litellm/litellm_core_utils/prompt_templates/factory.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 2ce157ae3b6..32510caa5fb 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -2438,9 +2438,7 @@ def anthropic_messages_pt( anthropic_content_element=_document_content_element, original_content_element=m, ) - user_content.append( - cast(AnthropicMessagesDocumentParam, _document_content_element) - ) # cast-ok: same union narrowing as above + user_content.append(_document_content_element) continue # Bedrock invoke models have format: invoke/... # Vertex AI Anthropic also doesn't support URL sources for images From 9f1b470eeba0c13ce68fd8032bd15eac91b01c44 Mon Sep 17 00:00:00 2001 From: Ruiming Zhao Date: Fri, 14 Aug 2026 17:23:38 -0700 Subject: [PATCH 6/6] fix(anthropic): satisfy basedpyright on document block path --- litellm/litellm_core_utils/prompt_templates/factory.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 32510caa5fb..74f7da91052 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -2421,12 +2421,12 @@ def anthropic_messages_pt( url_str = ( image_url_value.get("url") if isinstance(image_url_value, dict) else image_url_value ) - if isinstance(url_str, str) and _is_anthropic_document_data_uri(url_str): - image_chunk: Final = convert_to_anthropic_image_obj( + if isinstance(url_str, str) and _is_anthropic_document_data_uri(url_str): # pyright: ignore[reportUnnecessaryIsInstance] # malformed payloads can carry a non-str url + image_chunk = convert_to_anthropic_image_obj( openai_image_url=url_str, format=None, ) - _document_content_element: Final = AnthropicMessagesDocumentParam( + _document_content_element = AnthropicMessagesDocumentParam( type="document", source=AnthropicContentParamSource( type="base64", @@ -2436,7 +2436,7 @@ def anthropic_messages_pt( ) add_cache_control_to_content( anthropic_content_element=_document_content_element, - original_content_element=m, + original_content_element=m, # pyright: ignore[reportArgumentType] # cache_control lives on the block ) user_content.append(_document_content_element) continue