mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
test: mirror PDF tool-result tests under tests/test_litellm
Duplicate the three Bedrock and three Anthropic tool-result tests into tests/test_litellm/ so they're picked up by `make test-unit` (and its coverage report). The originals in tests/llm_translation/ stay — they run under integration and remain the canonical translation-suite regression cases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
50eba8a3e2
commit
5b5363cd54
2 changed files with 236 additions and 0 deletions
|
|
@ -2476,3 +2476,112 @@ def test_bedrock_tools_pt_passes_ttl_for_claude_4_5():
|
|||
cache_blocks_old = [b for b in result_old if "cachePoint" in b]
|
||||
assert len(cache_blocks_old) == 1
|
||||
assert "ttl" not in cache_blocks_old[0]["cachePoint"]
|
||||
|
||||
|
||||
def test_convert_to_anthropic_tool_result_openai_file_pdf_becomes_document():
|
||||
"""
|
||||
OpenAI `{type: "file", file: {file_data: "data:application/pdf;..."}}` inside
|
||||
a tool-message content list should translate to an Anthropic document block
|
||||
inside the tool_result content. Reuses anthropic_process_openai_file_message,
|
||||
which already handles this for user messages.
|
||||
"""
|
||||
from litellm.litellm_core_utils.prompt_templates.factory import (
|
||||
convert_to_anthropic_tool_result,
|
||||
)
|
||||
|
||||
pdf_b64 = "JVBERi0xLjQKJeLjz9MK"
|
||||
message = {
|
||||
"tool_call_id": "toolu_pdf_1",
|
||||
"role": "tool",
|
||||
"name": "fetch_document",
|
||||
"content": [
|
||||
{
|
||||
"type": "file",
|
||||
"file": {
|
||||
"file_data": f"data:application/pdf;base64,{pdf_b64}",
|
||||
"filename": "summary.pdf",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
result = convert_to_anthropic_tool_result(message)
|
||||
|
||||
assert result["type"] == "tool_result"
|
||||
assert result["tool_use_id"] == "toolu_pdf_1"
|
||||
content = result["content"]
|
||||
assert isinstance(content, list) and len(content) == 1
|
||||
block = content[0]
|
||||
assert block["type"] == "document"
|
||||
assert block["source"]["type"] == "base64"
|
||||
assert block["source"]["media_type"] == "application/pdf"
|
||||
assert block["source"]["data"] == pdf_b64
|
||||
|
||||
|
||||
def test_convert_to_anthropic_tool_result_image_url_pdf_data_uri_becomes_document():
|
||||
"""
|
||||
Regression: a PDF sent as an `image_url` data URI on the tool-result path
|
||||
must translate to an Anthropic document block (not an image block — Anthropic
|
||||
rejects image blocks whose media_type is a non-image like application/pdf).
|
||||
"""
|
||||
from litellm.litellm_core_utils.prompt_templates.factory import (
|
||||
convert_to_anthropic_tool_result,
|
||||
)
|
||||
|
||||
pdf_b64 = "JVBERi0xLjQKJeLjz9MK"
|
||||
message = {
|
||||
"tool_call_id": "toolu_pdf_img_1",
|
||||
"role": "tool",
|
||||
"name": "fetch_document",
|
||||
"content": [
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": f"data:application/pdf;base64,{pdf_b64}",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
result = convert_to_anthropic_tool_result(message)
|
||||
|
||||
content = result["content"]
|
||||
assert isinstance(content, list) and len(content) == 1
|
||||
block = content[0]
|
||||
assert block["type"] == "document"
|
||||
assert block["source"]["media_type"] == "application/pdf"
|
||||
assert block["source"]["data"] == pdf_b64
|
||||
|
||||
|
||||
def test_convert_to_anthropic_tool_result_image_url_png_still_becomes_image():
|
||||
"""
|
||||
Regression: image_url with a real image mime type must continue to translate
|
||||
to an Anthropic image block. Locks in existing behavior after the
|
||||
data-URI-mime-type branching for PDFs.
|
||||
"""
|
||||
from litellm.litellm_core_utils.prompt_templates.factory import (
|
||||
convert_to_anthropic_tool_result,
|
||||
)
|
||||
|
||||
png_b64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGNgYGBgAAAABQABXvMqOgAAAABJRU5ErkJggg=="
|
||||
message = {
|
||||
"tool_call_id": "toolu_png_1",
|
||||
"role": "tool",
|
||||
"name": "fetch_image",
|
||||
"content": [
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": f"data:image/png;base64,{png_b64}",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
result = convert_to_anthropic_tool_result(message)
|
||||
|
||||
content = result["content"]
|
||||
assert isinstance(content, list) and len(content) == 1
|
||||
block = content[0]
|
||||
assert block["type"] == "image"
|
||||
assert block["source"]["media_type"] == "image/png"
|
||||
|
|
|
|||
|
|
@ -4146,3 +4146,130 @@ def test_transform_response_finish_reason_stop_when_json_mode_filters_all_tools(
|
|||
|
||||
# finish_reason must be "stop", not "tool_calls"
|
||||
assert result.choices[0].finish_reason == "stop"
|
||||
|
||||
|
||||
def test_bedrock_tool_message_openai_file_pdf_becomes_document():
|
||||
"""
|
||||
OpenAI Chat Completions `{type: "file", file: {file_data: "data:application/pdf;...", filename}}`
|
||||
inside a tool message content list should translate to a Bedrock
|
||||
toolResult.content[].document block.
|
||||
"""
|
||||
from litellm.litellm_core_utils.prompt_templates.factory import (
|
||||
_bedrock_converse_messages_pt,
|
||||
)
|
||||
|
||||
pdf_b64 = "JVBERi0xLjQKJeLjz9MK" # tiny "%PDF-1.4\n" header
|
||||
messages = [
|
||||
{"role": "user", "content": "Summarize the attached PDF."},
|
||||
{
|
||||
"tool_call_id": "tooluse_pdf_1",
|
||||
"role": "tool",
|
||||
"name": "fetch_document",
|
||||
"content": [
|
||||
{
|
||||
"type": "file",
|
||||
"file": {
|
||||
"file_data": f"data:application/pdf;base64,{pdf_b64}",
|
||||
"filename": "summary.pdf",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
translated_msg = _bedrock_converse_messages_pt(
|
||||
messages=messages, model="", llm_provider=""
|
||||
)
|
||||
|
||||
tool_result = translated_msg[-1]["content"][-1]["toolResult"]
|
||||
assert tool_result["toolUseId"] == "tooluse_pdf_1"
|
||||
assert len(tool_result["content"]) == 1
|
||||
block = tool_result["content"][0]
|
||||
assert "document" in block, f"expected document block, got {block}"
|
||||
assert block["document"]["format"] == "pdf"
|
||||
assert block["document"]["source"]["bytes"] == pdf_b64
|
||||
assert block["document"]["name"].startswith("DocumentPDFmessages_")
|
||||
assert block["document"]["name"].endswith("_pdf")
|
||||
|
||||
|
||||
def test_bedrock_tool_message_image_url_pdf_data_uri_becomes_document():
|
||||
"""
|
||||
Regression for the processor-returns-document-but-wrapper-drops-it bug:
|
||||
when a caller sends a PDF as an `image_url` data URI on the tool-result path,
|
||||
BedrockImageProcessor correctly returns a {"document": ...} block, but the
|
||||
tool-result wrapper only appended the "image" case, silently dropping documents.
|
||||
"""
|
||||
from litellm.litellm_core_utils.prompt_templates.factory import (
|
||||
_bedrock_converse_messages_pt,
|
||||
)
|
||||
|
||||
pdf_b64 = "JVBERi0xLjQKJeLjz9MK"
|
||||
messages = [
|
||||
{"role": "user", "content": "Summarize the attached PDF."},
|
||||
{
|
||||
"tool_call_id": "tooluse_pdf_img_1",
|
||||
"role": "tool",
|
||||
"name": "fetch_document",
|
||||
"content": [
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": f"data:application/pdf;base64,{pdf_b64}",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
translated_msg = _bedrock_converse_messages_pt(
|
||||
messages=messages, model="", llm_provider=""
|
||||
)
|
||||
|
||||
tool_result = translated_msg[-1]["content"][-1]["toolResult"]
|
||||
assert tool_result["toolUseId"] == "tooluse_pdf_img_1"
|
||||
assert len(tool_result["content"]) == 1
|
||||
block = tool_result["content"][0]
|
||||
assert "document" in block, f"expected document block, got {block}"
|
||||
assert block["document"]["format"] == "pdf"
|
||||
assert block["document"]["source"]["bytes"] == pdf_b64
|
||||
|
||||
|
||||
def test_bedrock_tool_message_image_url_png_still_becomes_image():
|
||||
"""
|
||||
Regression: image_url with an image mime type must continue to translate
|
||||
to a Bedrock image block (not document). Locks in existing behavior after
|
||||
the document-passthrough fix.
|
||||
"""
|
||||
from litellm.litellm_core_utils.prompt_templates.factory import (
|
||||
_bedrock_converse_messages_pt,
|
||||
)
|
||||
|
||||
png_b64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGNgYGBgAAAABQABXvMqOgAAAABJRU5ErkJggg=="
|
||||
messages = [
|
||||
{"role": "user", "content": "Describe the attached image."},
|
||||
{
|
||||
"tool_call_id": "tooluse_png_1",
|
||||
"role": "tool",
|
||||
"name": "fetch_image",
|
||||
"content": [
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": f"data:image/png;base64,{png_b64}",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
translated_msg = _bedrock_converse_messages_pt(
|
||||
messages=messages, model="", llm_provider=""
|
||||
)
|
||||
|
||||
tool_result = translated_msg[-1]["content"][-1]["toolResult"]
|
||||
assert len(tool_result["content"]) == 1
|
||||
block = tool_result["content"][0]
|
||||
assert "image" in block, f"expected image block, got {block}"
|
||||
assert "document" not in block
|
||||
assert block["image"]["format"] == "png"
|
||||
assert block["image"]["source"]["bytes"] == png_b64
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue