diff --git a/tests/providers/test_anthropic_tool_result.py b/tests/providers/test_anthropic_tool_result.py new file mode 100644 index 00000000..5860b8ba --- /dev/null +++ b/tests/providers/test_anthropic_tool_result.py @@ -0,0 +1,57 @@ +"""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"