# Charts from a model
URL: /diagrammatic/docs/ai-specs

Let a model emit a chart as JSON, validate it, and draw it with one component, in an assistant-ui thread or anywhere else.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

`diagrammatic/spec` describes every form in the main entry as a serializable spec: the form's `type` plus its data props. A model can write that JSON, `validateSpec` checks it, and `Chart` draws it.

\[interactive preview omitted]

## The spec

```
{
  "v": "dg/1",
  "type": "column",
  "title": "Weekly deploys",
  "unit": "none",
  "items": [
    { "label": "W1", "value": 14 },
    { "label": "W2", "value": 19 }
  ]
}
```

| Field                                  | Description                                                                                                       |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `type`                                 | The form, in kebab case: `"line"`, `"stacked-bar"`, `"sankey"`. `CHART_TYPES` lists all 68                        |
| `v`                                    | The spec shape version, `"dg/1"`. Optional; any other value fails validation                                      |
| `unit`                                 | How printed numbers are formatted: `"percent"`, `"compact"`, or `"none"`. A spec cannot carry a `format` function |
| `title`, `width`, `height`, `fontSize` | The chart's name and its pixel contract                                                                           |
| everything else                        | The form's data props, exactly as the component takes them                                                        |

Event handlers, `style`, refs, and `format` stay out, because a spec is JSON. `diagrammatic/world` is not part of the spec.

## Validate and draw

```
import { Chart, validateSpec } from "diagrammatic/spec";

const spec = validateSpec(JSON.parse(modelOutput));
<Chart spec={spec} />;
```

`validateSpec` checks that the type exists and that the form's required fields are present with the right shape, and throws an error naming the field when they are not. That message is written to be handed back to the model. A spec with an unknown type renders an inert placeholder instead of throwing, so a half-written stream cannot take the page down. `validateSpec` checks structure only; deep field checks stay with TypeScript through the `ChartSpec` type, so a malformed value inside a valid shape (an item that is not an object, say) can still throw while the chart renders. Render untrusted specs inside an error boundary.

## In an assistant-ui thread

Register a tool whose arguments are a spec. The tool runs in the browser, returns the validation error to the model when there is one, and renders the chart in the thread.

```
"use generative";

import { defineToolkit } from "@assistant-ui/react";
import { CHART_TYPES, Chart, validateSpec } from "diagrammatic/spec";
import { z } from "zod";

export default defineToolkit({
  render_chart: {
    description: `Draw a chart from data. "type" is one of: ${CHART_TYPES.join(", ")}.`,
    parameters: z.object({
      spec: z.looseObject({ type: z.string() }),
    }),
    execute: async ({ spec }) => {
      "use client";
      try {
        validateSpec(spec);
        return { drawn: true };
      } catch (error) {
        return { error: (error as Error).message };
      }
    },
    render: ({ args, status }) => {
      if (status.type === "running") return <div className="h-64 animate-pulse" />;
      try {
        return <Chart spec={validateSpec(args.spec)} />;
      } catch {
        return null;
      }
    },
  },
});
```

The tool result carries the validation error back, so the model's next step can repair the spec.