From 29a08df06afd8e7ae7eec6496556a988bc24d18c Mon Sep 17 00:00:00 2001 From: lentan Date: Wed, 22 Apr 2026 11:26:56 +0000 Subject: [PATCH] fix(anthropic): convert image_url blocks inside tool_result content _tool_result_block passed list content through unchanged, so image_url blocks returned by tools (e.g. read_file on an image file, which returns OpenAI-format image_url blocks via build_image_content_blocks) reached the Anthropic API unconverted and were rejected. User-role messages already ran through _convert_user_content at the call site, so inbound Telegram photos worked, but tool results did not. Run _convert_user_content on list content inside _tool_result_block so image_url blocks become native Anthropic image blocks. Required making _convert_user_content a @staticmethod (it did not use self) and calling _convert_image_block via the class to match. Repro: an agent calling read_file on any image file got a "Non-transient LLM error with image content, retrying without images" warning and the image was silently dropped from the conversation. --- nanobot/providers/anthropic_provider.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/nanobot/providers/anthropic_provider.py b/nanobot/providers/anthropic_provider.py index 1d277d7f..f88bba62 100644 --- a/nanobot/providers/anthropic_provider.py +++ b/nanobot/providers/anthropic_provider.py @@ -167,7 +167,9 @@ class AnthropicProvider(LLMProvider): "type": "tool_result", "tool_use_id": msg.get("tool_call_id", ""), } - if isinstance(content, (str, list)): + if isinstance(content, list): + block["content"] = AnthropicProvider._convert_user_content(content) + elif isinstance(content, str): block["content"] = content else: block["content"] = str(content) if content else "" @@ -208,7 +210,8 @@ class AnthropicProvider(LLMProvider): return blocks or [{"type": "text", "text": ""}] - def _convert_user_content(self, content: Any) -> Any: + @staticmethod + def _convert_user_content(content: Any) -> Any: """Convert user message content, translating image_url blocks.""" if isinstance(content, str) or content is None: return content or "(empty)" @@ -221,7 +224,7 @@ class AnthropicProvider(LLMProvider): result.append({"type": "text", "text": str(item)}) continue if item.get("type") == "image_url": - converted = self._convert_image_block(item) + converted = AnthropicProvider._convert_image_block(item) if converted: result.append(converted) continue