forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patha2ui_dynamic_schema.ts
More file actions
97 lines (86 loc) · 2.75 KB
/
Copy patha2ui_dynamic_schema.ts
File metadata and controls
97 lines (86 loc) · 2.75 KB
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 { z } from "zod";
import { tool, type ToolRuntime } from "@langchain/core/tools";
import { SystemMessage } from "@langchain/core/messages";
import { ChatOpenAI } from "@langchain/openai";
import {
createSurface,
render,
updateComponents,
updateDataModel,
} from "./a2ui.js";
const CUSTOM_CATALOG_ID = "copilotkit://app-dashboard-catalog";
const renderA2uiSchema = z.object({
surfaceId: z.string(),
catalogId: z.string(),
components: z.array(z.record(z.any())),
data: z.record(z.any()).optional(),
});
const renderA2ui = tool(async () => "rendered", {
name: "render_a2ui",
description:
"Render a dynamic A2UI v0.9 surface. components must be a flat array whose root id is 'root'.",
schema: renderA2uiSchema,
});
const DynamicStateSchema = z.object({
messages: z.array(z.any()).default(() => []),
copilotkit: z
.object({
context: z
.array(z.object({ value: z.string().optional() }).passthrough())
.optional(),
})
.passthrough()
.optional(),
});
export const generate_a2ui = tool(
async (
_input: Record<string, never>,
runtime: ToolRuntime<typeof DynamicStateSchema>,
) => {
const messages = (runtime.state.messages ?? []).slice(0, -1);
const contextEntries = runtime.state.copilotkit?.context ?? [];
const contextText = contextEntries
.map((e) => (e && typeof e === "object" ? (e.value ?? "") : ""))
.filter(Boolean)
.join("\n\n");
const model = new ChatOpenAI({ model: "gpt-4.1" });
const modelWithTool = model.bindTools!([renderA2ui], {
tool_choice: "render_a2ui",
});
const response = await modelWithTool.invoke([
new SystemMessage({ content: contextText }),
...(messages as never[]),
]);
const toolCalls = (
response as { tool_calls?: Array<{ args: Record<string, unknown> }> }
).tool_calls;
if (!toolCalls || toolCalls.length === 0) {
return JSON.stringify({ error: "LLM did not call render_a2ui" });
}
const args = toolCalls[0].args as {
surfaceId?: string;
catalogId?: string;
components?: unknown[];
data?: Record<string, unknown>;
};
const surfaceId = args.surfaceId ?? "dynamic-surface";
const catalogId = args.catalogId ?? CUSTOM_CATALOG_ID;
const components = args.components ?? [];
const data = args.data ?? {};
const ops = [
createSurface(surfaceId, catalogId),
updateComponents(surfaceId, components),
];
if (Object.keys(data).length > 0) {
ops.push(updateDataModel(surfaceId, data));
}
return render(ops);
},
{
name: "generate_a2ui",
description:
"Generate dynamic A2UI components based on the conversation. " +
"A secondary LLM designs the UI schema and data.",
schema: z.object({}),
},
);