@copilotkit/sdk-js 1.9.2-next.2 → 1.9.2-next.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +119 -0
- package/dist/chunk-Q5S2AURJ.mjs +311 -0
- package/dist/chunk-Q5S2AURJ.mjs.map +1 -0
- package/dist/langchain.js +191 -43
- package/dist/langchain.js.map +1 -1
- package/dist/langchain.mjs +1 -1
- package/dist/langgraph.js +245 -74
- package/dist/langgraph.js.map +1 -1
- package/dist/langgraph.mjs +1 -1
- package/package.json +3 -3
- package/src/__tests__/error-handling.test.ts +207 -0
- package/src/langgraph.ts +265 -68
- package/dist/chunk-PMVIZ7R2.mjs +0 -140
- package/dist/chunk-PMVIZ7R2.mjs.map +0 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { CopilotKitMisuseError } from "@copilotkit/shared";
|
|
2
|
+
import {
|
|
3
|
+
copilotKitInterrupt,
|
|
4
|
+
convertActionToDynamicStructuredTool,
|
|
5
|
+
convertActionsToDynamicStructuredTools,
|
|
6
|
+
copilotkitCustomizeConfig,
|
|
7
|
+
copilotkitEmitMessage,
|
|
8
|
+
copilotkitEmitState,
|
|
9
|
+
copilotkitEmitToolCall,
|
|
10
|
+
copilotkitExit,
|
|
11
|
+
} from "../langgraph";
|
|
12
|
+
|
|
13
|
+
describe("SDK-JS Error Handling", () => {
|
|
14
|
+
describe("copilotKitInterrupt", () => {
|
|
15
|
+
it("should throw CopilotKitMisuseError when neither message nor action provided", () => {
|
|
16
|
+
expect(() => {
|
|
17
|
+
copilotKitInterrupt({});
|
|
18
|
+
}).toThrow(CopilotKitMisuseError);
|
|
19
|
+
|
|
20
|
+
expect(() => {
|
|
21
|
+
copilotKitInterrupt({});
|
|
22
|
+
}).toThrow(
|
|
23
|
+
"Either message or action (and optional arguments) must be provided for copilotKitInterrupt",
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should throw CopilotKitMisuseError when action is not a string", () => {
|
|
28
|
+
expect(() => {
|
|
29
|
+
copilotKitInterrupt({ action: 123 as any });
|
|
30
|
+
}).toThrow(CopilotKitMisuseError);
|
|
31
|
+
|
|
32
|
+
expect(() => {
|
|
33
|
+
copilotKitInterrupt({ action: 123 as any });
|
|
34
|
+
}).toThrow("Action must be a string when provided to copilotKitInterrupt");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should throw CopilotKitMisuseError when message is not a string", () => {
|
|
38
|
+
expect(() => {
|
|
39
|
+
copilotKitInterrupt({ message: 123 as any });
|
|
40
|
+
}).toThrow(CopilotKitMisuseError);
|
|
41
|
+
|
|
42
|
+
expect(() => {
|
|
43
|
+
copilotKitInterrupt({ message: 123 as any });
|
|
44
|
+
}).toThrow("Message must be a string when provided to copilotKitInterrupt");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should throw CopilotKitMisuseError when args is not an object", () => {
|
|
48
|
+
expect(() => {
|
|
49
|
+
copilotKitInterrupt({ action: "test", args: "invalid" as any });
|
|
50
|
+
}).toThrow(CopilotKitMisuseError);
|
|
51
|
+
|
|
52
|
+
expect(() => {
|
|
53
|
+
copilotKitInterrupt({ action: "test", args: "invalid" as any });
|
|
54
|
+
}).toThrow("Args must be an object when provided to copilotKitInterrupt");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("convertActionToDynamicStructuredTool", () => {
|
|
59
|
+
it("should throw CopilotKitMisuseError when actionInput is null/undefined", () => {
|
|
60
|
+
expect(() => {
|
|
61
|
+
convertActionToDynamicStructuredTool(null);
|
|
62
|
+
}).toThrow(CopilotKitMisuseError);
|
|
63
|
+
|
|
64
|
+
expect(() => {
|
|
65
|
+
convertActionToDynamicStructuredTool(null);
|
|
66
|
+
}).toThrow("Action input is required but was not provided");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should throw CopilotKitMisuseError when name is missing", () => {
|
|
70
|
+
expect(() => {
|
|
71
|
+
convertActionToDynamicStructuredTool({ description: "test" });
|
|
72
|
+
}).toThrow(CopilotKitMisuseError);
|
|
73
|
+
|
|
74
|
+
expect(() => {
|
|
75
|
+
convertActionToDynamicStructuredTool({ description: "test" });
|
|
76
|
+
}).toThrow("Action must have a valid 'name' property of type string");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should throw CopilotKitMisuseError when description is missing", () => {
|
|
80
|
+
expect(() => {
|
|
81
|
+
convertActionToDynamicStructuredTool({ name: "test" });
|
|
82
|
+
}).toThrow(CopilotKitMisuseError);
|
|
83
|
+
|
|
84
|
+
expect(() => {
|
|
85
|
+
convertActionToDynamicStructuredTool({ name: "test" });
|
|
86
|
+
}).toThrow("Action 'test' must have a valid 'description' property of type string");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should throw CopilotKitMisuseError when parameters is missing", () => {
|
|
90
|
+
expect(() => {
|
|
91
|
+
convertActionToDynamicStructuredTool({ name: "test", description: "test desc" });
|
|
92
|
+
}).toThrow(CopilotKitMisuseError);
|
|
93
|
+
|
|
94
|
+
expect(() => {
|
|
95
|
+
convertActionToDynamicStructuredTool({ name: "test", description: "test desc" });
|
|
96
|
+
}).toThrow("Action 'test' must have a 'parameters' property");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("convertActionsToDynamicStructuredTools", () => {
|
|
101
|
+
it("should throw CopilotKitMisuseError when actions is not an array", () => {
|
|
102
|
+
expect(() => {
|
|
103
|
+
convertActionsToDynamicStructuredTools("not an array" as any);
|
|
104
|
+
}).toThrow(CopilotKitMisuseError);
|
|
105
|
+
|
|
106
|
+
expect(() => {
|
|
107
|
+
convertActionsToDynamicStructuredTools("not an array" as any);
|
|
108
|
+
}).toThrow("Actions must be an array");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe("copilotkitCustomizeConfig", () => {
|
|
113
|
+
it("should throw CopilotKitMisuseError when baseConfig is not an object", () => {
|
|
114
|
+
expect(() => {
|
|
115
|
+
copilotkitCustomizeConfig("invalid" as any);
|
|
116
|
+
}).toThrow(CopilotKitMisuseError);
|
|
117
|
+
|
|
118
|
+
expect(() => {
|
|
119
|
+
copilotkitCustomizeConfig("invalid" as any);
|
|
120
|
+
}).toThrow("baseConfig must be an object or null/undefined");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should throw CopilotKitMisuseError when options is not an object", () => {
|
|
124
|
+
expect(() => {
|
|
125
|
+
copilotkitCustomizeConfig({}, "invalid" as any);
|
|
126
|
+
}).toThrow(CopilotKitMisuseError);
|
|
127
|
+
|
|
128
|
+
expect(() => {
|
|
129
|
+
copilotkitCustomizeConfig({}, "invalid" as any);
|
|
130
|
+
}).toThrow("options must be an object when provided");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should throw CopilotKitMisuseError when emitIntermediateState is not an array", () => {
|
|
134
|
+
expect(() => {
|
|
135
|
+
copilotkitCustomizeConfig({}, { emitIntermediateState: "invalid" as any });
|
|
136
|
+
}).toThrow(CopilotKitMisuseError);
|
|
137
|
+
|
|
138
|
+
expect(() => {
|
|
139
|
+
copilotkitCustomizeConfig({}, { emitIntermediateState: "invalid" as any });
|
|
140
|
+
}).toThrow("emitIntermediateState must be an array when provided");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("should throw CopilotKitMisuseError when emitIntermediateState item is invalid", () => {
|
|
144
|
+
expect(() => {
|
|
145
|
+
copilotkitCustomizeConfig({}, { emitIntermediateState: [{ invalidKey: "value" }] as any });
|
|
146
|
+
}).toThrow(CopilotKitMisuseError);
|
|
147
|
+
|
|
148
|
+
expect(() => {
|
|
149
|
+
copilotkitCustomizeConfig({}, { emitIntermediateState: [{ invalidKey: "value" }] as any });
|
|
150
|
+
}).toThrow("emitIntermediateState[0] must have a valid 'stateKey' string property");
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe("emit functions", () => {
|
|
155
|
+
const mockConfig = { metadata: {} };
|
|
156
|
+
|
|
157
|
+
it("should throw CopilotKitMisuseError when config is missing for copilotkitExit", async () => {
|
|
158
|
+
await expect(copilotkitExit(null as any)).rejects.toThrow(CopilotKitMisuseError);
|
|
159
|
+
await expect(copilotkitExit(null as any)).rejects.toThrow(
|
|
160
|
+
"LangGraph configuration is required for copilotkitExit",
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should throw CopilotKitMisuseError when config is missing for copilotkitEmitState", async () => {
|
|
165
|
+
await expect(copilotkitEmitState(null as any, {})).rejects.toThrow(CopilotKitMisuseError);
|
|
166
|
+
await expect(copilotkitEmitState(null as any, {})).rejects.toThrow(
|
|
167
|
+
"LangGraph configuration is required for copilotkitEmitState",
|
|
168
|
+
);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("should throw CopilotKitMisuseError when state is undefined for copilotkitEmitState", async () => {
|
|
172
|
+
await expect(copilotkitEmitState(mockConfig, undefined)).rejects.toThrow(
|
|
173
|
+
CopilotKitMisuseError,
|
|
174
|
+
);
|
|
175
|
+
await expect(copilotkitEmitState(mockConfig, undefined)).rejects.toThrow(
|
|
176
|
+
"State is required for copilotkitEmitState",
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("should throw CopilotKitMisuseError when message is invalid for copilotkitEmitMessage", async () => {
|
|
181
|
+
await expect(copilotkitEmitMessage(mockConfig, "" as any)).rejects.toThrow(
|
|
182
|
+
CopilotKitMisuseError,
|
|
183
|
+
);
|
|
184
|
+
await expect(copilotkitEmitMessage(mockConfig, "" as any)).rejects.toThrow(
|
|
185
|
+
"Message must be a non-empty string for copilotkitEmitMessage",
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("should throw CopilotKitMisuseError when tool name is invalid for copilotkitEmitToolCall", async () => {
|
|
190
|
+
await expect(copilotkitEmitToolCall(mockConfig, "", {})).rejects.toThrow(
|
|
191
|
+
CopilotKitMisuseError,
|
|
192
|
+
);
|
|
193
|
+
await expect(copilotkitEmitToolCall(mockConfig, "", {})).rejects.toThrow(
|
|
194
|
+
"Tool name must be a non-empty string for copilotkitEmitToolCall",
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("should throw CopilotKitMisuseError when args is undefined for copilotkitEmitToolCall", async () => {
|
|
199
|
+
await expect(copilotkitEmitToolCall(mockConfig, "testTool", undefined)).rejects.toThrow(
|
|
200
|
+
CopilotKitMisuseError,
|
|
201
|
+
);
|
|
202
|
+
await expect(copilotkitEmitToolCall(mockConfig, "testTool", undefined)).rejects.toThrow(
|
|
203
|
+
"Tool arguments are required for copilotkitEmitToolCall",
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
});
|
package/src/langgraph.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RunnableConfig } from "@langchain/core/runnables";
|
|
2
2
|
import { dispatchCustomEvent } from "@langchain/core/callbacks/dispatch";
|
|
3
|
-
import { convertJsonSchemaToZodSchema, randomId } from "@copilotkit/shared";
|
|
3
|
+
import { convertJsonSchemaToZodSchema, randomId, CopilotKitMisuseError } from "@copilotkit/shared";
|
|
4
4
|
import { Annotation, MessagesAnnotation, interrupt } from "@langchain/langgraph";
|
|
5
5
|
import { DynamicStructuredTool } from "@langchain/core/tools";
|
|
6
6
|
import { AIMessage } from "@langchain/core/messages";
|
|
@@ -90,36 +90,89 @@ export function copilotkitCustomizeConfig(
|
|
|
90
90
|
*/
|
|
91
91
|
options?: OptionsConfig,
|
|
92
92
|
): RunnableConfig {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
metadata["copilotkit:emit-messages"] = options.emitMessages;
|
|
104
|
-
}
|
|
93
|
+
if (baseConfig && typeof baseConfig !== "object") {
|
|
94
|
+
throw new CopilotKitMisuseError({
|
|
95
|
+
message: "baseConfig must be an object or null/undefined",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (options && typeof options !== "object") {
|
|
100
|
+
throw new CopilotKitMisuseError({
|
|
101
|
+
message: "options must be an object when provided",
|
|
102
|
+
});
|
|
105
103
|
}
|
|
106
104
|
|
|
105
|
+
// Validate emitIntermediateState structure
|
|
107
106
|
if (options?.emitIntermediateState) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
107
|
+
if (!Array.isArray(options.emitIntermediateState)) {
|
|
108
|
+
throw new CopilotKitMisuseError({
|
|
109
|
+
message: "emitIntermediateState must be an array when provided",
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
options.emitIntermediateState.forEach((state, index) => {
|
|
114
|
+
if (!state || typeof state !== "object") {
|
|
115
|
+
throw new CopilotKitMisuseError({
|
|
116
|
+
message: `emitIntermediateState[${index}] must be an object`,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!state.stateKey || typeof state.stateKey !== "string") {
|
|
121
|
+
throw new CopilotKitMisuseError({
|
|
122
|
+
message: `emitIntermediateState[${index}] must have a valid 'stateKey' string property`,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
113
125
|
|
|
114
|
-
|
|
126
|
+
if (!state.tool || typeof state.tool !== "string") {
|
|
127
|
+
throw new CopilotKitMisuseError({
|
|
128
|
+
message: `emitIntermediateState[${index}] must have a valid 'tool' string property`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (state.toolArgument && typeof state.toolArgument !== "string") {
|
|
133
|
+
throw new CopilotKitMisuseError({
|
|
134
|
+
message: `emitIntermediateState[${index}].toolArgument must be a string when provided`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
115
138
|
}
|
|
116
139
|
|
|
117
|
-
|
|
140
|
+
try {
|
|
141
|
+
const metadata = baseConfig?.metadata || {};
|
|
118
142
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
143
|
+
if (options?.emitAll) {
|
|
144
|
+
metadata["copilotkit:emit-tool-calls"] = true;
|
|
145
|
+
metadata["copilotkit:emit-messages"] = true;
|
|
146
|
+
} else {
|
|
147
|
+
if (options?.emitToolCalls !== undefined) {
|
|
148
|
+
metadata["copilotkit:emit-tool-calls"] = options.emitToolCalls;
|
|
149
|
+
}
|
|
150
|
+
if (options?.emitMessages !== undefined) {
|
|
151
|
+
metadata["copilotkit:emit-messages"] = options.emitMessages;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (options?.emitIntermediateState) {
|
|
156
|
+
const snakeCaseIntermediateState = options.emitIntermediateState.map((state) => ({
|
|
157
|
+
tool: state.tool,
|
|
158
|
+
tool_argument: state.toolArgument,
|
|
159
|
+
state_key: state.stateKey,
|
|
160
|
+
}));
|
|
161
|
+
|
|
162
|
+
metadata["copilotkit:emit-intermediate-state"] = snakeCaseIntermediateState;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
baseConfig = baseConfig || {};
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
...baseConfig,
|
|
169
|
+
metadata: metadata,
|
|
170
|
+
};
|
|
171
|
+
} catch (error) {
|
|
172
|
+
throw new CopilotKitMisuseError({
|
|
173
|
+
message: `Failed to customize config: ${error instanceof Error ? error.message : String(error)}`,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
123
176
|
}
|
|
124
177
|
/**
|
|
125
178
|
* Exits the current agent after the run completes. Calling copilotkit_exit() will
|
|
@@ -142,7 +195,19 @@ export async function copilotkitExit(
|
|
|
142
195
|
*/
|
|
143
196
|
config: RunnableConfig,
|
|
144
197
|
) {
|
|
145
|
-
|
|
198
|
+
if (!config) {
|
|
199
|
+
throw new CopilotKitMisuseError({
|
|
200
|
+
message: "LangGraph configuration is required for copilotkitExit",
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
await dispatchCustomEvent("copilotkit_exit", {}, config);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
throw new CopilotKitMisuseError({
|
|
208
|
+
message: `Failed to dispatch exit event: ${error instanceof Error ? error.message : String(error)}`,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
146
211
|
}
|
|
147
212
|
/**
|
|
148
213
|
* Emits intermediate state to CopilotKit. Useful if you have a longer running node and you want to
|
|
@@ -169,7 +234,25 @@ export async function copilotkitEmitState(
|
|
|
169
234
|
*/
|
|
170
235
|
state: any,
|
|
171
236
|
) {
|
|
172
|
-
|
|
237
|
+
if (!config) {
|
|
238
|
+
throw new CopilotKitMisuseError({
|
|
239
|
+
message: "LangGraph configuration is required for copilotkitEmitState",
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (state === undefined) {
|
|
244
|
+
throw new CopilotKitMisuseError({
|
|
245
|
+
message: "State is required for copilotkitEmitState",
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
await dispatchCustomEvent("copilotkit_manually_emit_intermediate_state", state, config);
|
|
251
|
+
} catch (error) {
|
|
252
|
+
throw new CopilotKitMisuseError({
|
|
253
|
+
message: `Failed to emit state: ${error instanceof Error ? error.message : String(error)}`,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
173
256
|
}
|
|
174
257
|
/**
|
|
175
258
|
* Manually emits a message to CopilotKit. Useful in longer running nodes to update the user.
|
|
@@ -199,11 +282,29 @@ export async function copilotkitEmitMessage(
|
|
|
199
282
|
*/
|
|
200
283
|
message: string,
|
|
201
284
|
) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
285
|
+
if (!config) {
|
|
286
|
+
throw new CopilotKitMisuseError({
|
|
287
|
+
message: "LangGraph configuration is required for copilotkitEmitMessage",
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!message || typeof message !== "string") {
|
|
292
|
+
throw new CopilotKitMisuseError({
|
|
293
|
+
message: "Message must be a non-empty string for copilotkitEmitMessage",
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
await dispatchCustomEvent(
|
|
299
|
+
"copilotkit_manually_emit_message",
|
|
300
|
+
{ message, message_id: randomId(), role: "assistant" },
|
|
301
|
+
config,
|
|
302
|
+
);
|
|
303
|
+
} catch (error) {
|
|
304
|
+
throw new CopilotKitMisuseError({
|
|
305
|
+
message: `Failed to emit message: ${error instanceof Error ? error.message : String(error)}`,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
207
308
|
}
|
|
208
309
|
/**
|
|
209
310
|
* Manually emits a tool call to CopilotKit.
|
|
@@ -230,22 +331,76 @@ export async function copilotkitEmitToolCall(
|
|
|
230
331
|
*/
|
|
231
332
|
args: any,
|
|
232
333
|
) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
334
|
+
if (!config) {
|
|
335
|
+
throw new CopilotKitMisuseError({
|
|
336
|
+
message: "LangGraph configuration is required for copilotkitEmitToolCall",
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (!name || typeof name !== "string") {
|
|
341
|
+
throw new CopilotKitMisuseError({
|
|
342
|
+
message: "Tool name must be a non-empty string for copilotkitEmitToolCall",
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (args === undefined) {
|
|
347
|
+
throw new CopilotKitMisuseError({
|
|
348
|
+
message: "Tool arguments are required for copilotkitEmitToolCall",
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
await dispatchCustomEvent(
|
|
354
|
+
"copilotkit_manually_emit_tool_call",
|
|
355
|
+
{ name, args, id: randomId() },
|
|
356
|
+
config,
|
|
357
|
+
);
|
|
358
|
+
} catch (error) {
|
|
359
|
+
throw new CopilotKitMisuseError({
|
|
360
|
+
message: `Failed to emit tool call '${name}': ${error instanceof Error ? error.message : String(error)}`,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
238
363
|
}
|
|
239
364
|
|
|
240
365
|
export function convertActionToDynamicStructuredTool(actionInput: any): DynamicStructuredTool<any> {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
366
|
+
if (!actionInput) {
|
|
367
|
+
throw new CopilotKitMisuseError({
|
|
368
|
+
message: "Action input is required but was not provided",
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (!actionInput.name || typeof actionInput.name !== "string") {
|
|
373
|
+
throw new CopilotKitMisuseError({
|
|
374
|
+
message: "Action must have a valid 'name' property of type string",
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (!actionInput.description || typeof actionInput.description !== "string") {
|
|
379
|
+
throw new CopilotKitMisuseError({
|
|
380
|
+
message: `Action '${actionInput.name}' must have a valid 'description' property of type string`,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (!actionInput.parameters) {
|
|
385
|
+
throw new CopilotKitMisuseError({
|
|
386
|
+
message: `Action '${actionInput.name}' must have a 'parameters' property`,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
try {
|
|
391
|
+
return new DynamicStructuredTool({
|
|
392
|
+
name: actionInput.name,
|
|
393
|
+
description: actionInput.description,
|
|
394
|
+
schema: convertJsonSchemaToZodSchema(actionInput.parameters, true),
|
|
395
|
+
func: async () => {
|
|
396
|
+
return "";
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
} catch (error) {
|
|
400
|
+
throw new CopilotKitMisuseError({
|
|
401
|
+
message: `Failed to convert action '${actionInput.name}' to DynamicStructuredTool: ${error instanceof Error ? error.message : String(error)}`,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
249
404
|
}
|
|
250
405
|
/**
|
|
251
406
|
* Use this function to convert a list of actions you get from state
|
|
@@ -265,7 +420,21 @@ export function convertActionsToDynamicStructuredTools(
|
|
|
265
420
|
*/
|
|
266
421
|
actions: any[],
|
|
267
422
|
): DynamicStructuredTool<any>[] {
|
|
268
|
-
|
|
423
|
+
if (!Array.isArray(actions)) {
|
|
424
|
+
throw new CopilotKitMisuseError({
|
|
425
|
+
message: "Actions must be an array",
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return actions.map((action, index) => {
|
|
430
|
+
try {
|
|
431
|
+
return convertActionToDynamicStructuredTool(action);
|
|
432
|
+
} catch (error) {
|
|
433
|
+
throw new CopilotKitMisuseError({
|
|
434
|
+
message: `Failed to convert action at index ${index}: ${error instanceof Error ? error.message : String(error)}`,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
});
|
|
269
438
|
}
|
|
270
439
|
|
|
271
440
|
export function copilotKitInterrupt({
|
|
@@ -278,35 +447,63 @@ export function copilotKitInterrupt({
|
|
|
278
447
|
args?: Record<string, any>;
|
|
279
448
|
}) {
|
|
280
449
|
if (!message && !action) {
|
|
281
|
-
throw new
|
|
450
|
+
throw new CopilotKitMisuseError({
|
|
451
|
+
message:
|
|
452
|
+
"Either message or action (and optional arguments) must be provided for copilotKitInterrupt",
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (action && typeof action !== "string") {
|
|
457
|
+
throw new CopilotKitMisuseError({
|
|
458
|
+
message: "Action must be a string when provided to copilotKitInterrupt",
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (message && typeof message !== "string") {
|
|
463
|
+
throw new CopilotKitMisuseError({
|
|
464
|
+
message: "Message must be a string when provided to copilotKitInterrupt",
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (args && typeof args !== "object") {
|
|
469
|
+
throw new CopilotKitMisuseError({
|
|
470
|
+
message: "Args must be an object when provided to copilotKitInterrupt",
|
|
471
|
+
});
|
|
282
472
|
}
|
|
283
473
|
|
|
284
474
|
let interruptValues = null;
|
|
285
475
|
let interruptMessage = null;
|
|
286
476
|
let answer = null;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
477
|
+
|
|
478
|
+
try {
|
|
479
|
+
if (message) {
|
|
480
|
+
interruptValues = message;
|
|
481
|
+
interruptMessage = new AIMessage({ content: message, id: randomId() });
|
|
482
|
+
} else {
|
|
483
|
+
const toolId = randomId();
|
|
484
|
+
interruptMessage = new AIMessage({
|
|
485
|
+
content: "",
|
|
486
|
+
tool_calls: [{ id: toolId, name: action, args: args ?? {} }],
|
|
487
|
+
});
|
|
488
|
+
interruptValues = {
|
|
489
|
+
action,
|
|
490
|
+
args: args ?? {},
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const response = interrupt({
|
|
495
|
+
__copilotkit_interrupt_value__: interruptValues,
|
|
496
|
+
__copilotkit_messages__: [interruptMessage],
|
|
295
497
|
});
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
498
|
+
answer = response[response.length - 1].content;
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
answer,
|
|
502
|
+
messages: response,
|
|
299
503
|
};
|
|
504
|
+
} catch (error) {
|
|
505
|
+
throw new CopilotKitMisuseError({
|
|
506
|
+
message: `Failed to create interrupt: ${error instanceof Error ? error.message : String(error)}`,
|
|
507
|
+
});
|
|
300
508
|
}
|
|
301
|
-
|
|
302
|
-
const response = interrupt({
|
|
303
|
-
__copilotkit_interrupt_value__: interruptValues,
|
|
304
|
-
__copilotkit_messages__: [interruptMessage],
|
|
305
|
-
});
|
|
306
|
-
answer = response[response.length - 1].content;
|
|
307
|
-
|
|
308
|
-
return {
|
|
309
|
-
answer,
|
|
310
|
-
messages: response,
|
|
311
|
-
};
|
|
312
509
|
}
|