Skip to content

feat(charts): render_chart tool and artifact delivery - #29

Merged
IBJunior merged 2 commits into
mainfrom
feat/render-chart-tool
Sep 12, 2026
Merged

IBJunior merged 2 commits into
mainfrom
feat/render-chart-tool

Conversation

@IBJunior

Copy link
Copy Markdown
Member

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_chart runs the query through analyticsRepository (the one sanctioned raw-SQL site) and returns two payloads to two consumers via responseFormat: "content_and_artifact":

Goes to Carries
content the model {"ok":true,"chartType":"bar","points":5,"x":"category","y":"total"} — 69 bytes
artifact the client the resolved rows, the spec, generatedAt

That is why there is no truncation machinery here: query_transactions caps 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

pumpToolCalls only ever forwarded content. The plan assumed the artifact sat beside it on the tool-call stream; a probe against a real agent run showed call.output resolves to the content string alone, so a field access there would have silently forwarded nothing.

The artifact lives on the ToolMessages in run.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_skilldescribe_finance_schemarun_sqlrender_chart, zero errors, chart rendered.

  • Row data in the model's payload: none
  • After reload, /api/agent/history returns the artifact for render_chart and omits it for the other three tools — the frozen snapshot replaying from the checkpointer, with no query re-run

185 tests, tsc clean.

🤖 Generated with Claude Code

IBJunior and others added 2 commits September 12, 2026 13:52
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>
@IBJunior
IBJunior merged commit 928bea9 into main Sep 12, 2026
1 check passed
@IBJunior
IBJunior deleted the feat/render-chart-tool branch September 12, 2026 15:36
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.

1 participant