nanobot/tests/providers/test_anthropic_tool_result.py
Xubin Ren 239e91a4d6 test(anthropic): pin tool_result image_url conversion regression
Adds a focused regression test so the fix for tool_result image
handling cannot silently revert. Two cases:

- list content with an image_url + text block -> image_url is
  translated to a native Anthropic image block, sibling text passes
  through unchanged
- plain string content passes through untouched (the new list branch
  must not alter the string path)

These cover the exact symptom surface (silent image drop with a
"Non-transient LLM error with image content" warning) and the only
two content shapes tool results actually take today.

Made-with: Cursor
2026-04-22 22:10:53 +08:00

58 lines
1.9 KiB
Python

"""Tests for AnthropicProvider._tool_result_block image_url conversion.
Regression for: tool results containing OpenAI-format image_url blocks
(e.g. from read_file on an image file, via build_image_content_blocks)
were passed to Anthropic unconverted, causing silent image drops with a
"Non-transient LLM error with image content, retrying without images"
warning.
"""
from nanobot.providers.anthropic_provider import AnthropicProvider
def test_tool_result_block_converts_image_url_in_list_content():
"""image_url blocks inside tool_result list content must be translated
to Anthropic-native image blocks; sibling text blocks pass through."""
msg = {
"role": "tool",
"tool_call_id": "call_1",
"content": [
{
"type": "image_url",
"image_url": {"url": "data:image/png;base64,AAAA"},
"_meta": {"path": "/tmp/x.png"},
},
{"type": "text", "text": "(Image file: /tmp/x.png)"},
],
}
block = AnthropicProvider._tool_result_block(msg)
assert block["type"] == "tool_result"
assert block["tool_use_id"] == "call_1"
content = block["content"]
assert isinstance(content, list)
assert content[0] == {
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "AAAA",
},
}
assert content[1] == {"type": "text", "text": "(Image file: /tmp/x.png)"}
def test_tool_result_block_preserves_string_content():
"""String content must be passed through unchanged; the image-conversion
path for lists must not affect the string path."""
msg = {
"role": "tool",
"tool_call_id": "call_2",
"content": "plain tool output",
}
block = AnthropicProvider._tool_result_block(msg)
assert block["type"] == "tool_result"
assert block["tool_use_id"] == "call_2"
assert block["content"] == "plain tool output"