* fix: extended typescript checking to main and fixed all typescript errors * fix: removed unnecessary type --------- Co-authored-by: hugo <hugoms@me.com>
163 lines
7.4 KiB
TypeScript
163 lines
7.4 KiB
TypeScript
/**
|
|
* A2UI render tools — rich UI surfaces in the chat.
|
|
*
|
|
* These tools produce { success: true } as their execute result.
|
|
* The actual A2UI message generation happens in chat.ts via
|
|
* `experimental_onToolCallFinish`, which calls `generateFromToolCall()`.
|
|
*
|
|
* Zod schemas here are the single source of truth for the tool parameter shapes.
|
|
*/
|
|
|
|
import { z } from 'zod';
|
|
import { tool } from 'ai';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Shared sub-schemas
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const segmentSchema = z.object({
|
|
label: z.string().describe('Segment/column label'),
|
|
value: z.number().describe('Segment value'),
|
|
});
|
|
|
|
const seriesItemSchema = z.object({
|
|
label: z.string().describe('Data point label'),
|
|
value: z.number().describe('Data point value'),
|
|
segments: z.array(segmentSchema).optional()
|
|
.describe('Segments within this data point. Required for stacked-bar and heatmap charts.'),
|
|
});
|
|
|
|
const chartTypeEnum = z.enum(['bar', 'stacked-bar', 'line', 'area', 'pie', 'donut', 'heatmap']);
|
|
|
|
const tabContentItemSchema = z.object({
|
|
type: z.enum(['text', 'metric', 'list', 'chart', 'table']).describe('Content type'),
|
|
// text
|
|
text: z.string().optional().describe('Text content (for type text)'),
|
|
// metric
|
|
label: z.string().optional().describe('Label (for type metric)'),
|
|
value: z.string().optional().describe('Display value (for type metric)'),
|
|
// list, chart, table
|
|
title: z.string().optional().describe('Title (for type list, chart, or table)'),
|
|
items: z.array(z.string()).optional().describe('Items (for type list)'),
|
|
// chart
|
|
chartType: chartTypeEnum.optional().describe('Chart type (for type chart)'),
|
|
series: z.array(seriesItemSchema).optional().describe('Data series (for type chart)'),
|
|
// table
|
|
columns: z.array(z.string()).optional().describe('Column headers (for type table)'),
|
|
rows: z.array(z.array(z.string())).optional().describe('Table rows (for type table)'),
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Tool factory
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function buildA2UITools() {
|
|
return {
|
|
render_chart: tool({
|
|
description: 'Render an interactive chart in the chat UI. Use this when the user asks for a chart, graph, or data visualization.',
|
|
inputSchema: z.object({
|
|
chartType: chartTypeEnum.describe('The type of chart to render. Use stacked-bar for multi-segment bars. Use heatmap for grid/matrix visualizations.'),
|
|
title: z.string().optional().describe('Optional chart title'),
|
|
series: z.array(seriesItemSchema).describe('Array of data points.'),
|
|
}),
|
|
execute: async () => ({ success: true }),
|
|
}),
|
|
|
|
render_table: tool({
|
|
description: 'Render a data table in the chat UI. Use this when the user asks for tabular data, comparisons, or structured information.',
|
|
inputSchema: z.object({
|
|
title: z.string().optional().describe('Optional table title'),
|
|
columns: z.array(z.string()).describe('Column header names'),
|
|
rows: z.array(z.array(z.string())).describe('Table rows, each row is an array of cell values'),
|
|
}),
|
|
execute: async () => ({ success: true }),
|
|
}),
|
|
|
|
render_form: tool({
|
|
description: 'Render an interactive form in the chat UI. Use this when you need to collect structured input from the user.',
|
|
inputSchema: z.object({
|
|
title: z.string().optional().describe('Optional form title'),
|
|
fields: z.array(z.object({
|
|
key: z.string().describe('Field identifier'),
|
|
label: z.string().describe('Field label shown to user'),
|
|
inputType: z.enum(['text', 'textarea', 'select', 'checkbox', 'date', 'number']).describe('Type of input control'),
|
|
placeholder: z.string().optional().describe('Placeholder text'),
|
|
defaultValue: z.union([z.string(), z.number(), z.boolean()]).optional().describe('Default value'),
|
|
options: z.array(z.object({
|
|
label: z.string(),
|
|
value: z.string(),
|
|
})).optional().describe('Options for select fields'),
|
|
required: z.boolean().optional().describe('Whether the field is required'),
|
|
})).describe('Form fields to display'),
|
|
submitLabel: z.string().describe('Label for the submit button'),
|
|
submitAction: z.string().optional().describe('Action to dispatch on submit'),
|
|
}),
|
|
execute: async () => ({ success: true }),
|
|
}),
|
|
|
|
render_card: tool({
|
|
description: 'Render an information card in the chat UI. Use this for displaying a summary, highlight, or actionable item.',
|
|
inputSchema: z.object({
|
|
title: z.string().describe('Card title'),
|
|
body: z.string().describe('Card body text (supports markdown)'),
|
|
subtitle: z.string().optional().describe('Optional subtitle'),
|
|
actions: z.array(z.object({
|
|
label: z.string().describe('Button label'),
|
|
action: z.string().describe('Action name to dispatch'),
|
|
payload: z.record(z.string(), z.unknown()).optional().describe('Optional action payload'),
|
|
})).optional().describe('Optional action buttons on the card'),
|
|
}),
|
|
execute: async () => ({ success: true }),
|
|
}),
|
|
|
|
render_metric: tool({
|
|
description: 'Render a single metric/KPI display in the chat UI. Use this for showing a single important value with a label.',
|
|
inputSchema: z.object({
|
|
label: z.string().describe('Metric label'),
|
|
value: z.string().describe('Metric value (displayed prominently)'),
|
|
}),
|
|
execute: async () => ({ success: true }),
|
|
}),
|
|
|
|
render_list: tool({
|
|
description: 'Render a list of items in the chat UI. Use this for displaying bullet-point style lists, checklists, or simple enumerations.',
|
|
inputSchema: z.object({
|
|
title: z.string().optional().describe('Optional list title'),
|
|
items: z.array(z.string()).describe('List items'),
|
|
}),
|
|
execute: async () => ({ success: true }),
|
|
}),
|
|
|
|
render_tabs: tool({
|
|
description: 'Render a tabbed interface in the chat UI. Use this to organize information into multiple tabs that the user can switch between.',
|
|
inputSchema: z.object({
|
|
tabs: z.array(z.object({
|
|
label: z.string().describe('Tab label'),
|
|
content: z.array(tabContentItemSchema).describe('Content items within the tab'),
|
|
})).describe('Array of tabs'),
|
|
}),
|
|
execute: async () => ({ success: true }),
|
|
}),
|
|
|
|
render_mindmap: tool({
|
|
description: 'Render a mind map diagram in the chat UI. Use this when the user asks for a mind map, concept map, topic tree, brainstorming diagram, or hierarchical overview of ideas.',
|
|
inputSchema: z.object({
|
|
title: z.string().optional().describe('Optional mind map title'),
|
|
nodes: z.array(z.object({
|
|
id: z.string().describe('Unique node identifier'),
|
|
label: z.string().describe('Node label text'),
|
|
children: z.array(z.string()).optional().describe('IDs of child nodes'),
|
|
})).describe('Flat array of nodes. The first node is the root. Each node references children by ID.'),
|
|
}),
|
|
execute: async () => ({ success: true }),
|
|
}),
|
|
};
|
|
}
|
|
|
|
export function createA2UITools(): ReturnType<typeof buildA2UITools> {
|
|
return buildA2UITools();
|
|
}
|
|
|
|
/** The return type of createA2UITools — useful for typing tool maps. */
|
|
export type A2UITools = ReturnType<typeof createA2UITools>;
|