File size: 3,684 Bytes
f6678ab 8fc8501 f6678ab 8fc8501 f6678ab | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | import { tool } from "ai";
import { z } from "zod";
/**
* Tools for the Embed Studio AI. These return instructions that the
* frontend executes on the EmbedStore (Y.Map). The backend never
* touches the Yjs document directly.
*/
export const embedTools = {
createEmbed: tool({
description:
"Create or fully replace an HTML embed chart. Use when building a new chart from scratch " +
"or when changes are so extensive that a full rewrite is cleaner than patching. " +
"The HTML must be a self-contained fragment following D3 embed conventions " +
"(root div + scoped style + IIFE script).",
inputSchema: z.object({
html: z
.string()
.describe(
"Complete self-contained HTML fragment (root div + scoped style + IIFE script)",
),
title: z
.string()
.optional()
.describe("Short descriptive title for the chart"),
source: z
.string()
.optional()
.describe("Data source attribution"),
filename: z
.string()
.optional()
.describe(
"Descriptive file name slug for this chart, WITHOUT extension " +
"(e.g. 'attention-heatmap', 'model-accuracy', 'sales-by-region'). " +
"Use kebab-case, ASCII only, concise (2-4 words, <40 chars). " +
"REQUIRED on the first createEmbed call for a new chart - the " +
"client will slugify it, ensure uniqueness, and rename the file " +
"automatically. Omit on subsequent calls that only update the " +
"existing chart (the current name will be kept).",
),
}),
}),
patchEmbed: tool({
description:
"Replace a specific block of code in the current chart HTML. " +
"Use for targeted edits (color change, label update, data tweak, bug fix). " +
"Always prefer this over createEmbed when modifying an existing chart. " +
"The search string must be an exact verbatim excerpt from the current HTML " +
"(whitespace included). Call readEmbed first if unsure of the exact content.",
inputSchema: z.object({
search: z
.string()
.describe(
"Exact verbatim excerpt from the current chart HTML to find and replace. " +
"Must be long enough to be unique (at least 3-5 lines of context).",
),
replace: z
.string()
.describe("The new code that replaces the search block"),
}),
}),
readEmbed: tool({
description:
"Read the full current chart HTML. Use before calling patchEmbed " +
"to verify the exact content, or to understand the current structure before editing.",
inputSchema: z.object({}),
}),
listDataFiles: tool({
description:
"List all data files (CSV, TSV, JSON, NDJSON, TXT) the user has " +
"attached to this document. Returns one line per file with name, " +
"extension, size, row count, and column names when available. " +
"Call this when the user mentions data, a dataset, or wants to " +
"visualize content from a specific file.",
inputSchema: z.object({}),
}),
readDataFile: tool({
description:
"Read the full raw content of a data file previously listed by " +
"listDataFiles. Use this to inspect the exact structure, sample " +
"values, or to embed small datasets directly into the chart. Large " +
"files may be truncated - prefer loading via fetch from " +
"/data/<name> for charts that rely on the full dataset.",
inputSchema: z.object({
name: z
.string()
.describe("Exact file name as returned by listDataFiles (e.g. 'sales.csv')"),
}),
}),
};
|