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
2
3
4
5
Press 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.json

Usage

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

  1. 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 as name: type (with =default for optional ones).
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. Final answer. Stage 5 shows the natural-language answer the model would send back, framed by a success-coloured left border.
  7. 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.
  8. 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

PropTypeDefaultDescription
schemasreadonly ToolSchemaVizSchema[]4 built-insTool schemas rendered in the grid.
scenariosreadonly ToolSchemaVizScenario[]3 built-insOverride the scenario set.
scenarioIdstringControlled scenario id. Pair with onScenarioChange.
defaultScenarioIdstringfirst scenarioUncontrolled initial scenario id.
onScenarioChange(id: string) => voidFires when the viewer picks a different scenario.
onComplete(summary) => voidFires when the pipeline reaches its final stage.
typingSpeedMsnumber22Per-character delay (ms) for the JSON output typewriter.
transitionTransitionSPRINGS.snapOverride the spring used for stage transitions.
classNamestringMerged 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 via aria-pressed.
  • The schema grid is a labelled role="group" listing all available tools, and each card carries data-cb-schema-match so test runners can assert which tool won the round.
  • The pipeline strip exposes aria-label describing total progress and marks the active step with aria-current="step".
  • Every interactive control meets the 36 px hit-target minimum and exposes a visible focus-visible ring.
  • 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 to Widget / useWidgetHistory / ModeStrip / ChallengeBtn / FeedbackBadge / ScoreDots primitives 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. Inline SPRINGS.snappy / SPRINGS.gentle / MICRO.tap / TIMING.* are re-keyed to canonical SPRINGS.snap / EASINGS.out / DURATIONS.* from @craft-bits/core/motion. The hard-coded SCHEMAS / SCENARIOS are now props with the originals exposed as TOOL_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-300ms rules out the longer 700 ms pulse. forwardRef + cn() + ...props spread were added; lessonId / SvgLabel / ChallengeBtn lesson chrome was stripped.