Skip to content

Generic tool results: render structured content items (#227)#235

Open
cboos wants to merge 2 commits into
mainfrom
dev/generic-tool-result
Open

Generic tool results: render structured content items (#227)#235
cboos wants to merge 2 commits into
mainfrom
dev/generic-tool-result

Conversation

@cboos

@cboos cboos commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Problem

A tool result for a tool without a specialized output renderer (e.g. ToolSearch) can carry a content list of typed items other than text/image — notably tool_reference blocks. The generic fallback formatter only extracted text and images, so such results rendered nothing on the result side (the tool_use showed, but the result was blank). Fixes #227.

Fix

  • HTML (html/tool_formatters.py, format_tool_result_content_raw): the list-content loop now collects leftover typed items into structured_items, rendered by a new _structured_items_html helper through the existing JSON/Markdown hybrid table — the same machinery used for JSON results. This inherits the depth and per-container breadth caps for free (a 250-item list falls back to a <pre> JSON dump rather than generating a 250-row table). Wired into all tails: the images branch appends it; the no-images branch returns it (alone, or after text). String, text-only, image-only, and error-string paths are unchanged.
  • Markdown (markdown/renderer.py, format_ToolResultContent): list content now renders text items as text and remaining typed items as a JSON block, instead of dumping the whole list verbatim. The TodoWrite special-case and the string path are unchanged.

The content array is the source (it carries the structured items directly), rather than toolUseResult.

is_error + structured items — deliberate choice

When a result is both is_error and carries typed content items, it renders the structured table rather than plain error text. This is intentional and verified safe: the is_error → read-as-text guard only applies to the JSON-string path, and genuine tool_reference (and other typed) items are not error message text. XSS posture is preserved on this path (structured items route through the same escape=True hybrid-table leaves; the breadth-cap fallback HTML-escapes too). Pinned by tests so a future refactor can't reorder the guard ahead of the structured path.

Tests

New test/test_generic_tool_result.py (13 cases): HTML + Markdown coverage, including the original bug (tool_reference-only → non-empty table), mixed text+structured, the is_error+structured pin, XSS escaping, breadth-cap fallback, and regressions on string / text-only / image-only / TodoWrite paths. Verified end-to-end on a real fixture (a ToolSearch result now emits structured tables where it previously rendered nothing). Full just ci green; no snapshot churn.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Structured tool results are now shown instead of being dropped.
    • Mixed text, structured data, and image outputs now render together more consistently.
    • Markdown output now separates plain text from structured JSON content for clearer display.
  • Tests
    • Added coverage for structured tool-result rendering, including mixed content, error cases, and HTML escaping.

cboos and others added 2 commits June 24, 2026 00:33
A tool result without a specialized output parser (e.g. ToolSearch)
can carry a `content` list of typed items other than text/image —
notably `tool_reference` blocks. The generic fallback formatter only
extracted text and images, so such results rendered nothing on the
result side.

Now the leftover typed items are rendered with the same JSON/Markdown
hybrid table used for JSON results (HTML), and as a JSON block in the
Markdown renderer, alongside any text. The `content` array is the
source, as it carries the structured items directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A result that is both is_error and carries typed content items (e.g.
tool_reference) renders the structured table, not error text — the
is_error->read-as-text guard only covers the JSON-string path, and
typed items are not error message text. This is a deliberate, reviewed
choice; pin it (HTML + Markdown) so a future refactor can't reorder
the guard ahead of the structured path and silently swallow it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a483a722-adc8-42e7-a2f8-3be2fa40e331

📥 Commits

Reviewing files that changed from the base of the PR and between 8013b61 and 99dd782.

📒 Files selected for processing (3)
  • claude_code_log/html/tool_formatters.py
  • claude_code_log/markdown/renderer.py
  • test/test_generic_tool_result.py

📝 Walkthrough

Walkthrough

HTML and Markdown tool result renderers are updated to collect non-text/non-image structured content items (e.g., tool_reference) instead of silently dropping them. A new HTML helper converts these into the existing params-style JSON/HTML table, with a pre-block fallback. The Markdown renderer splits text and typed items into separate output sections. A new test module validates both renderers across all relevant cases.

Changes

Structured Tool Result Rendering

Layer / File(s) Summary
HTML helper and format_tool_result_content_raw wiring
claude_code_log/html/tool_formatters.py
Adds _render_structured_items helper (params-style table with JSON pre fallback); updates the item loop to collect non-text/non-image items into structured_items; threads structured_html into both the has-images and no-images output paths.
Markdown split rendering
claude_code_log/markdown/renderer.py
Replaces the single JSON dump of the entire output.content list with separate rendering: type == "text" entries emit as stripped plain text, all other typed entries emit as a fenced JSON block, joined by blank lines.
Tests: HTML and Markdown structured content
test/test_generic_tool_result.py
New test module with _result/_md_message helpers and _TOOL_REFERENCE fixture; TestHtmlStructuredContent covers non-dropped items, multi-reference, mixed text, HTML escaping, error parity, and legacy paths; TestMarkdownStructuredContent covers fenced JSON output, text separation, error parity, plain strings, and the TodoWrite special case.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • daaain/claude-code-log#216: Both PRs modify format_tool_result_content_raw in claude_code_log/html/tool_formatters.py to change how tool result content is rendered into the params-style hybrid JSON/HTML table.

Poem

🐇 Hop hop, what's this I see?
A tool_reference hiding from me!
No more dropped items, they'll all appear,
In tidy JSON tables, crystal clear.
Text splits from structured, each in its place —
The rabbit renders all with style and grace! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: rendering structured generic tool results.
Linked Issues check ✅ Passed The PR addresses #227 by rendering structured content items for generic tools in both HTML and Markdown.
Out of Scope Changes check ✅ Passed The changes stay focused on generic tool-result rendering and tests, with no obvious unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev/generic-tool-result

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cboos

cboos commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

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.

Better generic tool result support

1 participant