Skip to content

grok: degrade tool-result images instead of 400 (L1) + opt-in vision via CCP_GROK_TOOL_IMAGE (L2) - #69

Merged
raine merged 5 commits into
raine:mainfrom
Desicool:release/grok-tool-image
Jul 28, 2026
Merged

grok: degrade tool-result images instead of 400 (L1) + opt-in vision via CCP_GROK_TOOL_IMAGE (L2)#69
raine merged 5 commits into
raine:mainfrom
Desicool:release/grok-tool-image

Conversation

@Desicool

Copy link
Copy Markdown
Contributor

Summary

Grok provider currently hard-fails with HTTP 400 (tool result supports text children only) whenever a tool_result contains an image block — which is what every Claude Code Read(png) produces. Top-level user images fail similarly (unsupported content block: image). This makes any session that reads a screenshot/diagram unrecoverable on the Grok path, even though Codex (and Cursor) already degrade gracefully and the upstream CLI endpoint (cli-chat-proxy.grok.com/v1/responses) accepts input_image fine.

This PR fixes that in two layers, with default behaviour = the safe layer:

L1 — omit (default, mirrors Codex' tool_result_to_string). Image children in tool_result degrade to [image omitted: {media_type}] / [image omitted: url] string fragments joined into the tool output; top-level user images degrade the same way. Malformed blocks still error (strict field validation per house style).

L2 — opt-in vision behind CCP_GROK_TOOL_IMAGE. Set reattach (omit marker in the tool output + images sent as input_image parts in a following user message) or inline (tool output as an untagged array of input_text + input_image parts; string-only outputs serialize byte-identical to before). reject restores the old bail for debugging. Live-verified against cli-chat-proxy.grok.com (model correctly answered visual questions in both modes).

Gates match upstream-verified limits, and any gate failure degrades that single image to an omit fragment with a reason — never a 400 for the whole turn:

  • min side ≥ 8px, min area ≥ 512px (upstream rejects below these)
  • ≤ ~5MB decoded, only the last 4 images per request survive
  • URL-source images always degrade in reattach (can't gate without fetching)
  • data URLs redacted in traffic captures (upstream image_url + anthropic source.data), images counted in token estimates

Implementation notes: mode threaded explicitly through translate_request_with_mode (pure, env-free tests); dimension checks are hand-rolled PNG/JPEG/GIF header parsers — no new dependencies.

Test plan

  • cargo test — 624 passed / 0 failed (10 suites); 26 new grok tests covering all four modes, gates, caps, serialization regression, redaction
  • cargo fmt --check clean
  • Live against cli-chat-proxy.grok.com (via Claude Code Read(png) + follow-up tool call): default omit — upstream 200, omit marker in outbound request, no base64 leakage; reattach/inline — model correctly identified test image contents; 1×1 PNG degrades with reason, no 400

🤖 Generated with Claude Code

@raine
raine force-pushed the release/grok-tool-image branch 2 times, most recently from 5b8eb32 to d9eaadf Compare July 28, 2026 13:29
Desicool added 4 commits July 28, 2026 15:33
Mirror the Codex translator: image children in tool_result become
"[image omitted: <media_type>]" (base64) / "[image omitted: url]"
fragments joined with text children via "\n", and top-level user
image blocks degrade to the same placeholder text rather than
erroring with "unsupported content block: image". Multi-part text
tool_results are newline-joined to match Codex. Malformed image
blocks (bad source, unknown fields) still fail strict validation.
Reattach mode keeps the image omission marker in each tool result and sends gate-passing images in a following user message as input_image data URLs.

Minimum dimensions, decoded-size limits, and the request-wide last-four cap degrade individual failures to reasoned omission markers. URL sources remain omitted because the proxy cannot validate them without fetching.

Traffic captures redact image data URLs and Anthropic source.data payloads. Token estimates charge a flat amount for each attached image. Omit remains the default, and reject retains strict image errors.
Inline mode sends image-bearing function call outputs as untagged arrays of input_text and input_image parts. String and text-only outputs retain their plain-string wire shape.

The same dimension, decoded-size, URL, and request-wide image gates apply as reattach mode. Per-image failures remain visible as omission text within mixed arrays. Top-level user images use input_image parts in both vision modes.

Token counting charges the image estimate for inline parts, and traffic capture redaction preserves array structure without persisting image payloads.
Keep startup mode validation consistent with request parsing and apply conservative image gates before opt-in vision payloads reach Grok.

Redact Anthropic and provider image representations across nested traffic capture shapes while preserving useful JSON structure. Keep unsupported formats and unverifiable URLs on the per-image omission path instead of failing the request.
@raine
raine force-pushed the release/grok-tool-image branch from d9eaadf to 26e3cf1 Compare July 28, 2026 13:36
Use format metadata when estimating decoded image size so ordinary 8-bit
screenshots under the upstream limit retain their pixels. Keep conservative
alpha accounting for formats that can carry transparency.

Redact image source types and data URL markers case-insensitively so malformed
pre-translation captures cannot persist image payloads. Pin omission tests to
their intended mode so developer environment settings do not affect results.

Place the feature notes in the active unreleased changelog section and document
the image policy in the current Grok and configuration documentation.
@raine
raine force-pushed the release/grok-tool-image branch from 26e3cf1 to ed3bf32 Compare July 28, 2026 13:39
@raine
raine merged commit ec41885 into raine:main Jul 28, 2026
@raine

raine commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants