Tool Schema Viz
An interactive walkthrough of how a language model picks a tool from a schema list and dispatches a structured call. Four schemas (get_weather, search_docs, calculator, send_email) sit in a 2×2 grid; three scenarios drive a different one to win the match. Press Next Step to advance one stage — the matched schema scales up while the unmatched ones dim, the JSON output typewrites a character at a time, and a trailing warning calls out the schemas the model never invoked.
Scenario
get_weather
Get current weather for a city
city: string
search_docs
Search internal documentation
query: stringlimit: integer=5
calculator
Evaluate a math expression
expression: string
send_email
Send an email message
to: stringsubject: stringbody: string
1
User message2
Schema matching3
Tool call4
Execution5
Final answerPress Next Step to begin the pipeline
Pick a scenario and step through the function-calling pipeline. Each stage shows what happens between the model and your code.
Customize
Tool schema
weather
22 ms / char
Installation
npx shadcn@latest add https://craftbits.dev/r/tool-schema-viz.jsonUsage
import { ToolSchemaViz } from "@craft-bits/viz/tool-schema-viz";
<ToolSchemaViz />Drive it externally (controlled scenario):
<ToolSchemaViz
scenarioId={scenarioId}
onScenarioChange={setScenarioId}
onComplete={({ toolName }) => {
/* record which tool the run dispatched */
}}
/>Replace the default schemas and scenarios with your own:
<ToolSchemaViz
schemas={[
{
name: "create_ticket",
description: "Open a new support ticket",
params: [
{ name: "title", type: "string", required: true },
{ name: "priority", type: "string", required: false, default: "normal" },
],
},
]}
scenarios={[
{
id: "ticket",
label: "New ticket",
userMessage: "File a ticket about the login bug",
toolName: "create_ticket",
toolCallJson:
'{\n "name": "create_ticket",\n "arguments": {\n "title": "Login bug"\n }\n}',
result: "Ticket #4291 created",
finalAnswer: "I opened ticket #4291 for the login bug.",
},
]}
/>Understanding the component
- Schema grid. Four tool schemas render in a 2×2 grid (4-up on
sm). Each card shows the function name, one-line description, and a chip strip of parameters typed asname: type(with=defaultfor optional ones). - Pipeline strip. Five numbered pills with chevron connectors show the run progress:
User message → Schema matching → Tool call → Execution → Final answer. Past stages turn success-green with a checkmark; the active stage scales the pill and recolours to accent. - Match highlight. Once stage 2 reaches schema matching, the picked schema scales to 1.04, gains an accent-coloured left border, and a single 300 ms shimmer sweeps across it. Every other schema fades to 25 % opacity so the matched one is the only thing in focus.
- Tool call typewriter. Stage 3 reveals the JSON tool call character by character. The per-character delay is configurable (
typingSpeedMs), slow on newlines and{, fast on commas and colons. The cursor at the tail blinks until typing completes. The JSON is syntax-highlighted: keys in accent, string values in success-green, braces in muted foreground. - Execution result. Stage 4 renders the function's return value inside a success-tinted block, with a circled checkmark whose stroke path animates in from
pathLength: 0. - Final answer. Stage 5 shows the natural-language answer the model would send back, framed by a success-coloured left border.
- Unused-schema warning. When the run completes, a warning chip explains that the schemas the model never called were just sitting in the prompt the whole time — tool inclusion is not the same as tool invocation.
- Reduced motion. Under
prefers-reduced-motion: reduce, every entrance, the shimmer sweep, the typewriter cadence, the cursor blink, and the checkmark stroke draw collapse to instant transitions.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
schemas | readonly ToolSchemaVizSchema[] | 4 built-ins | Tool schemas rendered in the grid. |
scenarios | readonly ToolSchemaVizScenario[] | 3 built-ins | Override the scenario set. |
scenarioId | string | — | Controlled scenario id. Pair with onScenarioChange. |
defaultScenarioId | string | first scenario | Uncontrolled initial scenario id. |
onScenarioChange | (id: string) => void | — | Fires when the viewer picks a different scenario. |
onComplete | (summary) => void | — | Fires when the pipeline reaches its final stage. |
typingSpeedMs | number | 22 | Per-character delay (ms) for the JSON output typewriter. |
transition | Transition | SPRINGS.snap | Override the spring used for stage transitions. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The narration paragraph lives in a polite live region (
aria-live="polite",aria-atomic) so screen-reader users hear the stage explanation update as the run advances. - The scenario selector is a labelled
role="group"; each button reports its selection viaaria-pressed. - The schema grid is a labelled
role="group"listing all available tools, and each card carriesdata-cb-schema-matchso test runners can assert which tool won the round. - The pipeline strip exposes
aria-labeldescribing total progress and marks the active step witharia-current="step". - Every interactive control meets the 36 px hit-target minimum and exposes a visible
focus-visiblering. - Decorative SVGs (chevrons, icons, syntax punctuation) are marked
aria-hidden. - Colour is paired with text labels at every signal — schema names, tool name, and result text are all readable without colour.
- Motion respects
prefers-reduced-motion: reduce— every entrance, the shimmer sweep, the typewriter cadence, the cursor blink, and the checkmark stroke draw collapse to instant transitions.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/systems/ToolSchemaViz.tsx). The source was a 1600-line lesson component wired toWidget/useWidgetHistory/ModeStrip/ChallengeBtn/FeedbackBadge/ScoreDotsprimitives from@/lessons/primitives/chrome/*, bundled a five-round predict quiz, a four-round challenge quiz, an undo/redo history stack, mode tabs (explore/predict/challenge), the lessonId prop, and CA palette tokens (--color-accent-400,--color-success-400,--color-warn-400,--color-fail-400,--color-ink-*,--color-surface-*,ca-narration). The viz extract keeps only the interactive Explore pipeline — the multiple-choice quizzes were curriculum-specific and live in the lesson source. InlineSPRINGS.snappy/SPRINGS.gentle/MICRO.tap/TIMING.*are re-keyed to canonicalSPRINGS.snap/EASINGS.out/DURATIONS.*from@craft-bits/core/motion. The hard-codedSCHEMAS/SCENARIOSare now props with the originals exposed asTOOL_SCHEMA_VIZ_DEFAULT_SCHEMAS/TOOL_SCHEMA_VIZ_DEFAULT_SCENARIOS. The infinite shimmer loop on the matched schema was tightened to a single 300 ms sweep —craft-bits/duration-max-300msrules out the longer 700 ms pulse.forwardRef+cn()+...propsspread were added;lessonId/SvgLabel/ChallengeBtnlesson chrome was stripped.