fix(anthropic): convert PDF image_url content to documents

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
mateo 2026-10-16 21:28:13 +00:00
parent ee6ca049b3
commit 8ed2976076
2 changed files with 84 additions and 19 deletions

View file

@ -907,9 +907,9 @@ def create_anthropic_image_param(
image_url_input: str | dict,
format: str | None = None,
is_bedrock_invoke: bool = False,
) -> AnthropicMessagesImageParam:
) -> AnthropicMessagesImageParam | AnthropicMessagesDocumentParam:
"""
Create an AnthropicMessagesImageParam from an image URL input.
Create an Anthropic content block from an image URL input.
Supports both URL references (for HTTP/HTTPS URLs) and base64 encoding.
"""
@ -928,14 +928,7 @@ def create_anthropic_image_param(
if is_bedrock_invoke or image_url.startswith("http://"):
base64_url: Final = convert_url_to_base64(url=image_url)
image_chunk = convert_to_anthropic_image_obj(openai_image_url=base64_url, format=format)
return AnthropicMessagesImageParam(
type="image",
source=AnthropicContentParamSource(
type="base64",
media_type=image_chunk["media_type"],
data=image_chunk["data"],
),
)
return _anthropic_content_element_factory(image_chunk)
else:
# HTTPS URL - pass directly for regular Anthropic
return AnthropicMessagesImageParam(
@ -948,14 +941,7 @@ def create_anthropic_image_param(
else:
# Convert to base64 for data URIs or other formats
image_chunk = convert_to_anthropic_image_obj(openai_image_url=image_url, format=format)
return AnthropicMessagesImageParam(
type="image",
source=AnthropicContentParamSource(
type="base64",
media_type=image_chunk["media_type"],
data=image_chunk["data"],
),
)
return _anthropic_content_element_factory(image_chunk)
# The following XML functions will be deprecated once JSON schema support is available on Bedrock and Vertex
@ -1795,7 +1781,12 @@ def convert_to_anthropic_tool_result(
anthropic_content_element=_anthropic_image_param,
original_content_element=content,
)
anthropic_content_list.append(cast(AnthropicMessagesImageParam, _anthropic_image_param))
anthropic_content_list.append(
cast(
AnthropicMessagesImageParam | AnthropicMessagesDocumentParam,
_anthropic_image_param,
)
)
elif content["type"] == "tool_reference":
anthropic_content_list.append(ToolReference(type="tool_reference", tool_name=content["tool_name"]))
elif content["type"] == "file":

View file

@ -867,6 +867,80 @@ def test_anthropic_messages_pt_keeps_signed_thinking_block():
assert thinking_blocks[0]["thinking"] == "genuine anthropic reasoning"
@pytest.mark.parametrize("llm_provider", ("anthropic", "vertex_ai"))
def test_anthropic_messages_pt_converts_pdf_image_url_data_uri_to_document(llm_provider: str) -> None:
pdf_data = "JVBERi0xLjQK"
result: Final = anthropic_messages_pt(
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "summarize"},
{"type": "image_url", "image_url": {"url": f"data:application/pdf;base64,{pdf_data}"}},
],
}
],
model="claude-opus-5-5",
llm_provider=llm_provider,
)
assert result[0]["content"][1] == {
"type": "document",
"source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data},
}
@pytest.mark.parametrize("llm_provider", ("anthropic", "vertex_ai"))
def test_anthropic_messages_pt_uses_pdf_format_for_generic_mime_data_uri(llm_provider: str) -> None:
pdf_data = "JVBERi0xLjQK"
result: Final = anthropic_messages_pt(
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "summarize"},
{
"type": "image_url",
"image_url": {
"url": f"data:application/octet-stream;base64,{pdf_data}",
"format": "application/pdf",
},
},
],
}
],
model="claude-opus-5-5",
llm_provider=llm_provider,
)
assert result[0]["content"][1] == {
"type": "document",
"source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data},
}
def test_anthropic_messages_pt_keeps_png_image_url_as_image() -> None:
image_data = "iVBORw0KGgo="
result: Final = anthropic_messages_pt(
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "describe"},
{"type": "image_url", "image_url": f"data:image/png;base64,{image_data}"},
],
}
],
model="claude-opus-5-5",
llm_provider="anthropic",
)
assert result[0]["content"][1] == {
"type": "image",
"source": {"type": "base64", "media_type": "image/png", "data": image_data},
}
EMPTY_USER_MESSAGES: Final = pytest.mark.parametrize(
"empty_user_message",
[{"role": "user", "content": None}, {"role": "user", "content": []}, {"role": "user"}],