feat(charts): render_chart tool and artifact delivery - #29
Merged
Merged
Conversation
Takes a spec — a read-only SELECT plus how to encode it — runs the query through analyticsRepository, and returns a two-part result: a short receipt for the model, the resolved rows as an artifact for the renderer. Refuses rather than draws when the result would mislead: a column the query does not return (naming the real ones), a truncated or oversized result, or an empty one that may be a misspelling rather than missing data. Adds callToolWithArtifact, since a plain invoke returns only the content and silently drops the artifact half. Not registered with the agent yet — that is the next commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Binds the tool to the agent and carries its artifact through the stream, so a chart request now renders end to end. The artifact is not on the toolCalls stream — `call.output` resolves to the content string alone — so it is collected from the ToolMessages in `run.values`, keyed by call id, with a fallback to the final state for the case where the snapshot has not arrived by the time the call resolves. A chart renders above the collapsed tool disclosure rather than inside it: it is the answer, not a detail of it. The receipt stays collapsed below. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Slices B1 and B2 of v3 phase 2: the tool that produces chart payloads, and the wiring that gets them to the renderer merged in #28. A chart request now works end to end.
The tool
The agent sends a spec — a read-only SELECT plus how to encode it — and never sees a row.
render_chartruns the query throughanalyticsRepository(the one sanctioned raw-SQL site) and returns two payloads to two consumers viaresponseFormat: "content_and_artifact":content{"ok":true,"chartType":"bar","points":5,"x":"category","y":"total"}— 69 bytesartifactgeneratedAtThat is why there is no truncation machinery here:
query_transactionscaps and warns because the agent must reason over the rows, but a chart of any size costs the agent the same handful of tokens.It refuses rather than draws when the result would mislead: a column the query does not return (naming the real ones back), a truncated or oversized result, or an empty one — which is byte-identical whether a category is missing or misspelled.
Getting the artifact to the client
pumpToolCallsonly ever forwardedcontent. The plan assumed the artifact sat beside it on the tool-call stream; a probe against a real agent run showedcall.outputresolves to the content string alone, so a field access there would have silently forwarded nothing.The artifact lives on the
ToolMessages inrun.values. A third pump collects them by call id, with a fallback to the final state for when the snapshot has not landed by the time the call resolves — the two iterables are independent, so leaving that to ordering would be a race.Presentation
A chart renders above the collapsed tool disclosure rather than inside it — it is the answer, not a detail of it. The receipt stays collapsed below for inspection.
Verified
A real turn against seeded dev data:
load_skill→describe_finance_schema→run_sql→render_chart, zero errors, chart rendered./api/agent/historyreturns the artifact forrender_chartand omits it for the other three tools — the frozen snapshot replaying from the checkpointer, with no query re-run185 tests,
tscclean.🤖 Generated with Claude Code