forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuse-frontend-tool.ts
More file actions
113 lines (96 loc) · 3.15 KB
/
Copy pathuse-frontend-tool.ts
File metadata and controls
113 lines (96 loc) · 3.15 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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import React, { useEffect, useMemo, useRef } from "react";
import { ActionRenderProps, FrontendAction } from "../types/frontend-action";
import {
Parameter,
getZodParameters,
MappedParameterTypes,
} from "@copilotkit/shared";
import { parseJson } from "@copilotkit/shared";
import { ToolCallStatus } from "@copilotkit/core";
import {
type ReactFrontendTool,
useFrontendTool as useFrontendToolVNext,
} from "../v2";
type FrontendToolOptions<T extends Parameter[] | []> = ReactFrontendTool<
MappedParameterTypes<T>
>;
type FrontendToolRenderArgs<T extends Parameter[] | []> =
| {
name: string;
args: Partial<MappedParameterTypes<T>>;
status: ToolCallStatus.InProgress;
result: undefined;
}
| {
name: string;
args: MappedParameterTypes<T>;
status: ToolCallStatus.Executing;
result: undefined;
}
| {
name: string;
args: MappedParameterTypes<T>;
status: ToolCallStatus.Complete;
result: string;
};
export type UseFrontendToolArgs<T extends Parameter[] | [] = []> = {
available?: "disabled" | "enabled";
} & Pick<
FrontendAction<T>,
"name" | "description" | "parameters" | "handler" | "followUp" | "render"
>;
export function useFrontendTool<const T extends Parameter[] = []>(
tool: UseFrontendToolArgs<T>,
dependencies?: any[],
) {
const { name, description, parameters, render, followUp, available } = tool;
const zodParameters = getZodParameters(parameters);
const renderRef = useRef<typeof render>(render);
useEffect(() => {
renderRef.current = render;
}, [render, ...(dependencies ?? [])]);
const normalizedRender: FrontendToolOptions<T>["render"] | undefined =
useMemo(() => {
if (typeof render === "undefined") {
return undefined;
}
return ((args: FrontendToolRenderArgs<T>) => {
const currentRender = renderRef.current;
if (typeof currentRender === "undefined") {
return null;
}
if (typeof currentRender === "string") {
return React.createElement(React.Fragment, null, currentRender);
}
const renderArgs = {
...args,
result:
typeof args.result === "string"
? parseJson(args.result, args.result)
: args.result,
} as ActionRenderProps<T>;
const rendered = currentRender(renderArgs);
if (typeof rendered === "string") {
return React.createElement(React.Fragment, null, rendered);
}
return rendered ?? null;
}) as FrontendToolOptions<T>["render"];
}, []);
// Handler ref to avoid stale closures
const handlerRef = useRef<typeof tool.handler>(tool.handler);
useEffect(() => {
handlerRef.current = tool.handler;
}, [tool.handler, ...(dependencies ?? [])]);
const normalizedHandler = tool.handler
? (args: MappedParameterTypes<T>) => handlerRef.current?.(args)
: undefined;
useFrontendToolVNext<MappedParameterTypes<T>>({
name,
description,
parameters: zodParameters,
handler: normalizedHandler,
followUp,
render: normalizedRender,
available: available === undefined ? undefined : available !== "disabled",
});
}