Repository navigation
serve: an image in an Anthropic tool_result reaches the encoder (Claude Code's Read of a picture) - #529
serve: an image in an Anthropic tool_result reaches the encoder (Claude Code's Read of a picture)#529sergqwer wants to merge 2 commits into
Conversation
b2050dc to
271beca
Compare
|
Follow-up, found in a review of this change: a tool's picture the server cannot read now became a 400. That happens on a server with no image encoder (every lazy-start config is text-only), and with an encoder that refuses the picture (WebP without Pillow, an error from strata-vision). Clients resend the whole history every turn, so one such picture failed every later request of the conversation. Before this PR the picture was dropped silently. Fixed in 271beca, rebased on v0.1.37:
🤖 Generated with Claude Code |
…de Code's Read of a picture) Claude Code returns a picture it opens with Read as an image block inside a tool_result. The Anthropic path turned every tool_result into its text, so the image never reached the encoder and the model described a picture it had not seen. The tool message now carries the image item, as the OpenAI path's tool messages already did; the chat template renders it inside <tool_response>. An image sent beside tool results in the same user turn is kept too. A text-only tool result is still one string, so text prompts are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
With tool_result pictures reaching the encoder, a server without one (every lazy-start config is text-only) or an encoder that refuses a picture (WebP without Pillow, an error from strata-vision) answered 400. Clients resend the whole history every turn, so one such picture failed every later request of the conversation; before, the picture was dropped silently. Now a tool's picture that cannot be read becomes "[image omitted: <reason>]" in its place; a picture the user sends keeps the 400. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
271beca to
8ef230b
Compare
|
Rebased onto 0.1.39 (8ef230b). The only conflict was |
|
Reopened as #1041, the same branch and commit. GitHub closed this one when my fork was made private by mistake, which took it out of the fork network. |
With tool_result pictures reaching the encoder, a server without one (every lazy-start config is text-only) or an encoder that refuses a picture (WebP without Pillow, an error from strata-vision) answered 400. Clients resend the whole history every turn, so one such picture failed every later request of the conversation; before, the picture was dropped silently. Now a tool's picture that cannot be read becomes "[image omitted: <reason>]" in its place; a picture the user sends keeps the 400. Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio Maintainer: log a left-out tool picture, a tool picture's URL is downloaded once (bytes reused by prepare), tests (#529; #582 and #819 are duplicates) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Merged for 0.1.40: an image in an Anthropic tool_result reaches the encoder. We added a log line when a tool's picture is left out, and a tool picture's URL is downloaded once. |
With tool_result pictures reaching the encoder, a server without one (every lazy-start config is text-only) or an encoder that refuses a picture (WebP without Pillow, an error from strata-vision) answered 400. Clients resend the whole history every turn, so one such picture failed every later request of the conversation; before, the picture was dropped silently. Now a tool's picture that cannot be read becomes "[image omitted: <reason>]" in its place; a picture the user sends keeps the 400. Maintainer: log a left-out tool picture, a tool picture's URL is downloaded once (bytes reused by prepare), tests (#529; #582 and #819 are duplicates)
With tool_result pictures reaching the encoder, a server without one (every lazy-start config is text-only) or an encoder that refuses a picture (WebP without Pillow, an error from strata-vision) answered 400. Clients resend the whole history every turn, so one such picture failed every later request of the conversation; before, the picture was dropped silently. Now a tool's picture that cannot be read becomes "[image omitted: <reason>]" in its place; a picture the user sends keeps the 400. Maintainer: log a left-out tool picture, a tool picture's URL is downloaded once (bytes reused by prepare), tests (Niko1221#529; Niko1221#582 and Niko1221#819 are duplicates)
With tool_result pictures reaching the encoder, a server without one (every lazy-start config is text-only) or an encoder that refuses a picture (WebP without Pillow, an error from strata-vision) answered 400. Clients resend the whole history every turn, so one such picture failed every later request of the conversation; before, the picture was dropped silently. Now a tool's picture that cannot be read becomes "[image omitted: <reason>]" in its place; a picture the user sends keeps the 400. Maintainer: log a left-out tool picture, a tool picture's URL is downloaded once (bytes reused by prepare), tests (Niko1221#529; Niko1221#582 and Niko1221#819 are duplicates)
Claude Code returns a picture it opens with
Read(a screenshot, a diagram) as an image block inside atool_result.anthropic_to_messagesturned everytool_resultinto its text (_text_of), so the image never reached the encoder and the model described a picture it had not seen. A user's own pasted image (an image block at the top level of a user message) already worked; only the tool path lost it.Fix (
serve/frontend.py,anthropic_to_messages):tool_result's content goes through_parts_of, as the OpenAI path'stoolmessages already do; the chat template renders the image item inside<tool_response>(itsrender_contenthandles images in any role);Test:
ImageMarkers.test_anthropic_tool_result_image: an image alone, after text, beside a text tool result, and in both places; it counts the image tokens and checks where they land. It fails on main (0 image tokens instead of 3) and passes here. All serve tests: 159 OK (7 skipped), on 0.1.36.End to end (Windows, RTX 5090, Flash-Next with the CPU encoder, Claude Code asked to
Reada PNG with known text and shapes):🤖 Generated with Claude Code
https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio