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')"),
    }),
  }),
};