/docsfor

Tools and prompts

The provider-agnostic widget tools, their inputs and results, adapters, and the guidance text the model reads.

generative-frame/tools defines the tools a model calls to draw widgets. Each tool is a plain object, independent of any provider SDK:

type ToolDefinition<Input, Output> = {
  name: string;
  description: string;
  inputSchema: JsonSchema;
  execute(input: Input): Promise<Output>;
};

Create the tools

import { previewWidget } from "generative-frame";
import { createWidgetTools } from "generative-frame/tools";

const tools = createWidgetTools({ preview: previewWidget });
// { read_me, show_widget, edit_widget, preview_widget }

extraTools adds tools to the returned object as they are, and modules adds modules to read_me. Spec mode uses both to add render_spec:

import { previewWidget } from "generative-frame";
import { createWidgetTools } from "generative-frame/tools";
import { createSpecTools, specGuidanceModule } from "generative-frame/spec/tools";

const tools = createWidgetTools({
  preview: previewWidget,
  extraTools: createSpecTools(catalog),
  modules: [specGuidanceModule(catalog)],
});
CreateWidgetToolsOptions
registryWidgetRegistry= createWidgetRegistry()

Where the latest code per widget title is kept. Share it with the host to render or edit the same widgets.

guidance?Omit<GuidanceOptions, "modules" | "platform">

Options for the guidance `read_me` returns, such as `tokens`, `cdnOrigins`, and `hostApi`. The model picks the modules and platform.

preview?(code, { width?, appearance? }) => Promise<PreviewResult>

Renders code offscreen for `preview_widget`, typically `previewWidget` from the core entry. Without it, `preview_widget` returns an error result.

modules?GuidanceModule[]

Extra `read_me` modules, each `{ name, summary, guidance() }`, such as `specGuidanceModule(catalog)`.

extraTools?Record<string, ToolDefinition>

Tools merged into the returned object, such as `createSpecTools(catalog)`.

read_me

Returns the rules for writing widgets plus guidance for the requested modules. The description tells the model to call it silently before its first widget.

InputTypeNotes
modulesstring[]Any of diagram, chart, data_viz, interactive, mockup, elicitation, art, plus the names of modules passed in modules, such as spec.
platform"desktop" | "mobile"Defaults to desktop.

The result is a string.

show_widget

Shows a widget and streams it as the model writes. Showing a title that already exists replaces that widget.

InputTypeNotes
titlestringRequired. A short snake_case identifier, unique in the conversation.
loading_messagesstring[]Up to four short status lines shown while the widget streams.
widget_codestringRequired. An HTML fragment, or code starting with <svg.

Result: { ok: true, title, kind, version }, or { ok: false, title, error } when widget_code is empty. execute stores the code in the registry; rendering is up to the host, which reads widget_code from the streamed arguments.

edit_widget

Applies exact string replacements, in order, to the latest code of a widget.

InputTypeNotes
titlestringRequired. A title already shown.
edits{ old_string, new_string }[]Required, at least one.

Each old_string must occur exactly once in the code as it stands after the previous edits. If any edit fails (not found, ambiguous, empty, or a no-op), none is applied. Result: { ok: true, title, version, applied }, or { ok: false, title, error } with an error that says what to copy or how to make the match unique.

applyWidgetEdits(code, edits) exposes the same logic.

preview_widget

Renders complete code offscreen without showing it.

InputTypeNotes
widget_codestringRequired. Complete code.
widthnumber240 to 1600. Defaults to 680.
appearance"light" | "dark"Defaults to light.

The result is a PreviewResult: { ok, kind, width, height, blank, errors, console, screenshot?, screenshotError? }. See previewWidget.

render_spec

createSpecTools(catalog, { specs? }) from generative-frame/spec/tools returns { render_spec }. It validates and records a spec built from your components. Pass specs, a Map<string, { spec, version }>, to share the latest spec per title with the host. The description tells the model to follow the spec guidance it was given, either the spec module of read_me or the system prompt.

InputTypeNotes
titlestringRequired.
patchesstringJSONL patch operations, one per line. Applied onto the title's previous spec, if there is one.
specobjectA complete spec, replacing the previous one.

Result: { ok, title, version, elementCount, issues, feedback }. issues come from validateSpec plus one invalid-patch issue per skipped line, and feedback is the same list as text for a repair round.

Use them with a provider

toAISDKTools converts the tools to the Vercel AI SDK shape. You pass the SDK's own jsonSchema helper, so ai is not a dependency of this package:

import { jsonSchema, streamText } from "ai";
import { createWidgetTools, toAISDKTools } from "generative-frame/tools";

const tools = createWidgetTools();
const result = streamText({
  model,
  messages,
  tools: toAISDKTools(tools, { jsonSchema }),
});

For any other provider, map name, description, inputSchema, and execute yourself.

getToolDeclarations(tools) returns each tool's description and inputSchema without execute, for declaring tools to a model where they do not run, such as a server that forwards calls to tools executing in the browser.

generative-frame/spec/tools exports toAISDKTools and getToolDeclarations too, so a spec-only app does not import the widget tools.

System prompt section

import { buildWidgetInstructions } from "generative-frame/tools";

const system = `${basePrompt}\n\n${await buildWidgetInstructions(tools)}`;

buildWidgetInstructions(tools, { preload }) returns a short section that tells the model when to show a widget, to call read_me first, and to change widgets with edit_widget. With preload: { modules, platform }, it appends the read_me output for those modules.

Guidance

read_me returns the output of buildWidgetGuidance from generative-frame/prompts. The output depends only on its options, so you can cache and diff it.

import { readThemeTokens } from "generative-frame";
import { buildWidgetGuidance } from "generative-frame/prompts";

const text = buildWidgetGuidance({
  modules: ["chart", "interactive"],
  platform: "desktop",
  tokens: readThemeTokens(),
  hostApi: { prompt: true, callTool: false },
});

It contains the base rules (fragment format, streaming order, theme tokens, allowed libraries and network, the host API) followed by an index of modules and the requested modules.

ModuleCovers
diagramSVG flowcharts, architecture and sequence diagrams, cycles, hierarchies
chartbar, line, area, scatter, and pie charts of a dataset
data_vizbespoke visualizations, D3, dashboards, stat tiles, tables, heatmaps
interactivecalculators, simulations, explorable explanations, controls that update output
mockupUI mockups, wireframes, app and web screens
elicitationa form that collects several answers from the user at once
artillustrations, generative art, decorative or explanatory drawings

Keep the guidance consistent with the frame: if you pass csp: { cdnOrigins, connectOrigins, allowEval } to createWidget, pass the same values to buildWidgetGuidance, and set hostApi.callTool only when the host handles onCallTool. buildModuleGuidance(module, options) returns one module on its own.

The registry

The registry tracks the latest code of each widget by title, so edit_widget applies to what the user sees.

import { createWidgetRegistry } from "generative-frame/tools";

const registry = createWidgetRegistry([{ title: "q3_revenue", code }]);
registry.subscribe((record) => console.log(record.title, record.version));
const tools = createWidgetTools({ registry });

It has get(title), set(title, code), delete(title), list(), and subscribe(listener). Each record is { title, code, version }, and version increases on every show or edit of that title.