@copilotkit/react-core 1.4.8 → 1.5.0-coagents-v0-3.0
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 +14 -0
- package/dist/{chunk-X6ZF5WAX.mjs → chunk-35EN6BG4.mjs} +2 -2
- package/dist/{chunk-FSC4A3JN.mjs → chunk-42N5VKIX.mjs} +23 -5
- package/dist/{chunk-FSC4A3JN.mjs.map → chunk-42N5VKIX.mjs.map} +1 -1
- package/dist/{chunk-AG7FH7OD.mjs → chunk-5FYKUKG3.mjs} +2 -2
- package/dist/{chunk-YUY5ZAST.mjs → chunk-ALR5W5JK.mjs} +17 -8
- package/dist/chunk-ALR5W5JK.mjs.map +1 -0
- package/dist/{chunk-6EMLM6WX.mjs → chunk-BT6WK2JZ.mjs} +43 -6
- package/dist/chunk-BT6WK2JZ.mjs.map +1 -0
- package/dist/{chunk-NTLCOVE5.mjs → chunk-QTDCEDOC.mjs} +141 -70
- package/dist/chunk-QTDCEDOC.mjs.map +1 -0
- package/dist/{chunk-IFTHM7LF.mjs → chunk-QX6V774L.mjs} +6 -8
- package/dist/chunk-QX6V774L.mjs.map +1 -0
- package/dist/{chunk-XQFVXX6R.mjs → chunk-TQN3EZWQ.mjs} +10 -2
- package/dist/chunk-TQN3EZWQ.mjs.map +1 -0
- package/dist/{chunk-UOVONDR6.mjs → chunk-V3PFWGIY.mjs} +2 -2
- package/dist/{chunk-IVYL7JRC.mjs → chunk-VMP6JWBB.mjs} +12 -3
- package/dist/{chunk-IVYL7JRC.mjs.map → chunk-VMP6JWBB.mjs.map} +1 -1
- package/dist/chunk-XERJQUHA.mjs +31 -0
- package/dist/chunk-XERJQUHA.mjs.map +1 -0
- package/dist/components/copilot-provider/copilotkit.js +19 -2
- package/dist/components/copilot-provider/copilotkit.js.map +1 -1
- package/dist/components/copilot-provider/copilotkit.mjs +2 -2
- package/dist/components/copilot-provider/index.js +19 -2
- package/dist/components/copilot-provider/index.js.map +1 -1
- package/dist/components/copilot-provider/index.mjs +2 -2
- package/dist/components/index.js +19 -2
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +2 -2
- package/dist/context/copilot-context.d.ts +8 -2
- package/dist/context/copilot-context.js +9 -1
- package/dist/context/copilot-context.js.map +1 -1
- package/dist/context/copilot-context.mjs +1 -1
- package/dist/context/index.d.ts +1 -1
- package/dist/context/index.js +9 -1
- package/dist/context/index.js.map +1 -1
- package/dist/context/index.mjs +1 -1
- package/dist/hooks/index.d.ts +2 -1
- package/dist/hooks/index.js +264 -95
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +16 -9
- package/dist/hooks/use-chat.d.ts +20 -0
- package/dist/hooks/use-chat.js +171 -77
- package/dist/hooks/use-chat.js.map +1 -1
- package/dist/hooks/use-chat.mjs +2 -1
- package/dist/hooks/use-coagent-state-render.js +9 -1
- package/dist/hooks/use-coagent-state-render.js.map +1 -1
- package/dist/hooks/use-coagent-state-render.mjs +2 -2
- package/dist/hooks/use-coagent.d.ts +14 -1
- package/dist/hooks/use-coagent.js +245 -85
- package/dist/hooks/use-coagent.js.map +1 -1
- package/dist/hooks/use-coagent.mjs +12 -5
- package/dist/hooks/use-copilot-action.d.ts +12 -2
- package/dist/hooks/use-copilot-action.js +24 -7
- package/dist/hooks/use-copilot-action.js.map +1 -1
- package/dist/hooks/use-copilot-action.mjs +2 -2
- package/dist/hooks/use-copilot-chat.d.ts +1 -0
- package/dist/hooks/use-copilot-chat.js +223 -84
- package/dist/hooks/use-copilot-chat.js.map +1 -1
- package/dist/hooks/use-copilot-chat.mjs +5 -4
- package/dist/hooks/use-copilot-readable.js +9 -1
- package/dist/hooks/use-copilot-readable.js.map +1 -1
- package/dist/hooks/use-copilot-readable.mjs +2 -2
- package/dist/hooks/use-make-copilot-document-readable.js +9 -1
- package/dist/hooks/use-make-copilot-document-readable.js.map +1 -1
- package/dist/hooks/use-make-copilot-document-readable.mjs +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +281 -106
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +17 -10
- package/dist/lib/copilot-task.d.ts +1 -1
- package/dist/lib/copilot-task.js +33 -13
- package/dist/lib/copilot-task.js.map +1 -1
- package/dist/lib/copilot-task.mjs +4 -3
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +33 -13
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +4 -3
- package/dist/types/frontend-action.d.ts +21 -2
- package/dist/types/frontend-action.js +34 -0
- package/dist/types/frontend-action.js.map +1 -1
- package/dist/types/frontend-action.mjs +7 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/extract.js.map +1 -1
- package/dist/utils/extract.mjs +2 -2
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +2 -2
- package/package.json +3 -3
- package/src/components/copilot-provider/copilotkit.tsx +10 -0
- package/src/context/copilot-context.tsx +30 -2
- package/src/hooks/index.ts +1 -1
- package/src/hooks/use-chat.ts +196 -88
- package/src/hooks/use-coagent.ts +21 -4
- package/src/hooks/use-copilot-action.ts +38 -10
- package/src/hooks/use-copilot-chat.ts +43 -3
- package/src/lib/copilot-task.ts +2 -8
- package/src/types/frontend-action.ts +55 -2
- package/src/types/index.ts +5 -1
- package/dist/chunk-6EMLM6WX.mjs.map +0 -1
- package/dist/chunk-IFTHM7LF.mjs.map +0 -1
- package/dist/chunk-NTLCOVE5.mjs.map +0 -1
- package/dist/chunk-XQFVXX6R.mjs.map +0 -1
- package/dist/chunk-YUY5ZAST.mjs.map +0 -1
- /package/dist/{chunk-X6ZF5WAX.mjs.map → chunk-35EN6BG4.mjs.map} +0 -0
- /package/dist/{chunk-AG7FH7OD.mjs.map → chunk-5FYKUKG3.mjs.map} +0 -0
- /package/dist/{chunk-UOVONDR6.mjs.map → chunk-V3PFWGIY.mjs.map} +0 -0
package/src/hooks/use-chat.ts
CHANGED
|
@@ -18,10 +18,11 @@ import {
|
|
|
18
18
|
Role,
|
|
19
19
|
CopilotRequestType,
|
|
20
20
|
ActionInputAvailability,
|
|
21
|
+
loadMessagesFromJsonRepresentation,
|
|
21
22
|
} from "@copilotkit/runtime-client-gql";
|
|
22
23
|
|
|
23
24
|
import { CopilotApiConfig } from "../context";
|
|
24
|
-
import { FrontendAction } from "../types/frontend-action";
|
|
25
|
+
import { FrontendAction, processActionsForRuntimeRequest } from "../types/frontend-action";
|
|
25
26
|
import { CoagentState } from "../types/coagent-state";
|
|
26
27
|
import { AgentSession } from "../context/copilot-context";
|
|
27
28
|
import { useToast } from "../components/toast/toast-provider";
|
|
@@ -98,6 +99,27 @@ export type UseChatOptions = {
|
|
|
98
99
|
* setState-powered method to update the agent session
|
|
99
100
|
*/
|
|
100
101
|
setAgentSession: React.Dispatch<React.SetStateAction<AgentSession | null>>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The current thread ID.
|
|
105
|
+
*/
|
|
106
|
+
threadId: string | null;
|
|
107
|
+
/**
|
|
108
|
+
* set the current thread ID
|
|
109
|
+
*/
|
|
110
|
+
setThreadId: (threadId: string | null) => void;
|
|
111
|
+
/**
|
|
112
|
+
* The current run ID.
|
|
113
|
+
*/
|
|
114
|
+
runId: string | null;
|
|
115
|
+
/**
|
|
116
|
+
* set the current run ID
|
|
117
|
+
*/
|
|
118
|
+
setRunId: (runId: string | null) => void;
|
|
119
|
+
/**
|
|
120
|
+
* The global chat abort controller.
|
|
121
|
+
*/
|
|
122
|
+
chatAbortControllerRef: React.MutableRefObject<AbortController | null>;
|
|
101
123
|
};
|
|
102
124
|
|
|
103
125
|
export type UseChatHelpers = {
|
|
@@ -140,19 +162,23 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
140
162
|
coagentStatesRef,
|
|
141
163
|
agentSession,
|
|
142
164
|
setAgentSession,
|
|
165
|
+
threadId,
|
|
166
|
+
setThreadId,
|
|
167
|
+
runId,
|
|
168
|
+
setRunId,
|
|
169
|
+
chatAbortControllerRef,
|
|
143
170
|
} = options;
|
|
144
|
-
|
|
145
|
-
const abortControllerRef = useRef<AbortController>();
|
|
146
|
-
const threadIdRef = useRef<string | null>(null);
|
|
147
|
-
const runIdRef = useRef<string | null>(null);
|
|
148
171
|
const { addGraphQLErrorsToast } = useToast();
|
|
149
|
-
|
|
150
172
|
const runChatCompletionRef = useRef<(previousMessages: Message[]) => Promise<Message[]>>();
|
|
151
173
|
// We need to keep a ref of coagent states and session because of renderAndWait - making sure
|
|
152
174
|
// the latest state is sent to the API
|
|
153
175
|
// This is a workaround and needs to be addressed in the future
|
|
154
176
|
const agentSessionRef = useRef<AgentSession | null>(agentSession);
|
|
155
177
|
agentSessionRef.current = agentSession;
|
|
178
|
+
const threadIdRef = useRef<string | null>(threadId);
|
|
179
|
+
threadIdRef.current = threadId;
|
|
180
|
+
const runIdRef = useRef<string | null>(runId);
|
|
181
|
+
runIdRef.current = runId;
|
|
156
182
|
|
|
157
183
|
const publicApiKey = copilotConfig.publicApiKey;
|
|
158
184
|
|
|
@@ -180,8 +206,8 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
180
206
|
role: Role.Assistant,
|
|
181
207
|
}),
|
|
182
208
|
];
|
|
183
|
-
|
|
184
|
-
|
|
209
|
+
|
|
210
|
+
chatAbortControllerRef.current = new AbortController();
|
|
185
211
|
|
|
186
212
|
setMessages([...previousMessages, ...newMessages]);
|
|
187
213
|
|
|
@@ -189,34 +215,13 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
189
215
|
|
|
190
216
|
const messagesWithContext = [systemMessage, ...(initialMessages || []), ...previousMessages];
|
|
191
217
|
|
|
218
|
+
const isAgentRun = agentSessionRef.current !== null;
|
|
219
|
+
|
|
192
220
|
const stream = runtimeClient.asStream(
|
|
193
221
|
runtimeClient.generateCopilotResponse({
|
|
194
222
|
data: {
|
|
195
223
|
frontend: {
|
|
196
|
-
actions: actions
|
|
197
|
-
.filter(
|
|
198
|
-
(action) =>
|
|
199
|
-
action.available !== ActionInputAvailability.Disabled || !action.disabled,
|
|
200
|
-
)
|
|
201
|
-
.map((action) => {
|
|
202
|
-
let available: ActionInputAvailability | undefined =
|
|
203
|
-
ActionInputAvailability.Enabled;
|
|
204
|
-
if (action.disabled) {
|
|
205
|
-
available = ActionInputAvailability.Disabled;
|
|
206
|
-
} else if (action.available === "disabled") {
|
|
207
|
-
available = ActionInputAvailability.Disabled;
|
|
208
|
-
} else if (action.available === "remote") {
|
|
209
|
-
available = ActionInputAvailability.Remote;
|
|
210
|
-
}
|
|
211
|
-
return {
|
|
212
|
-
name: action.name,
|
|
213
|
-
description: action.description || "",
|
|
214
|
-
jsonSchema: JSON.stringify(
|
|
215
|
-
actionParametersToJsonSchema(action.parameters || []),
|
|
216
|
-
),
|
|
217
|
-
available,
|
|
218
|
-
};
|
|
219
|
-
}),
|
|
224
|
+
actions: processActionsForRuntimeRequest(actions),
|
|
220
225
|
url: window.location.href,
|
|
221
226
|
},
|
|
222
227
|
threadId: threadIdRef.current,
|
|
@@ -254,7 +259,7 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
254
259
|
})),
|
|
255
260
|
},
|
|
256
261
|
properties: copilotConfig.properties,
|
|
257
|
-
signal:
|
|
262
|
+
signal: chatAbortControllerRef.current?.signal,
|
|
258
263
|
}),
|
|
259
264
|
);
|
|
260
265
|
|
|
@@ -263,10 +268,12 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
263
268
|
|
|
264
269
|
const reader = stream.getReader();
|
|
265
270
|
|
|
266
|
-
let actionResults: { [id: string]: string } = {};
|
|
267
271
|
let executedCoAgentStateRenders: string[] = [];
|
|
268
272
|
let followUp: FrontendAction["followUp"] = undefined;
|
|
269
273
|
|
|
274
|
+
let messages: Message[] = [];
|
|
275
|
+
let syncedMessages: Message[] = [];
|
|
276
|
+
|
|
270
277
|
try {
|
|
271
278
|
while (true) {
|
|
272
279
|
let done, value;
|
|
@@ -280,6 +287,9 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
280
287
|
}
|
|
281
288
|
|
|
282
289
|
if (done) {
|
|
290
|
+
if (chatAbortControllerRef.current.signal.aborted) {
|
|
291
|
+
return [];
|
|
292
|
+
}
|
|
283
293
|
break;
|
|
284
294
|
}
|
|
285
295
|
|
|
@@ -290,7 +300,10 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
290
300
|
threadIdRef.current = value.generateCopilotResponse.threadId || null;
|
|
291
301
|
runIdRef.current = value.generateCopilotResponse.runId || null;
|
|
292
302
|
|
|
293
|
-
|
|
303
|
+
setThreadId(threadIdRef.current);
|
|
304
|
+
setRunId(runIdRef.current);
|
|
305
|
+
|
|
306
|
+
messages = convertGqlOutputToMessages(
|
|
294
307
|
filterAdjacentAgentStateMessages(value.generateCopilotResponse.messages),
|
|
295
308
|
);
|
|
296
309
|
|
|
@@ -300,7 +313,7 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
300
313
|
|
|
301
314
|
newMessages = [];
|
|
302
315
|
|
|
303
|
-
// request failed, display error message
|
|
316
|
+
// request failed, display error message and quit
|
|
304
317
|
if (
|
|
305
318
|
value.generateCopilotResponse.status?.__typename === "FailedResponseStatus" &&
|
|
306
319
|
value.generateCopilotResponse.status.reason === "GUARDRAILS_VALIDATION_FAILED"
|
|
@@ -311,57 +324,16 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
311
324
|
content: value.generateCopilotResponse.status.details?.guardrailsReason || "",
|
|
312
325
|
}),
|
|
313
326
|
];
|
|
327
|
+
setMessages([...previousMessages, ...newMessages]);
|
|
328
|
+
break;
|
|
314
329
|
}
|
|
315
330
|
|
|
316
331
|
// add messages to the chat
|
|
317
332
|
else {
|
|
318
|
-
|
|
319
|
-
newMessages.push(message);
|
|
320
|
-
// execute regular action executions
|
|
321
|
-
if (
|
|
322
|
-
message.isActionExecutionMessage() &&
|
|
323
|
-
message.status.code !== MessageStatusCode.Pending &&
|
|
324
|
-
message.scope === "client" &&
|
|
325
|
-
onFunctionCall
|
|
326
|
-
) {
|
|
327
|
-
if (!(message.id in actionResults)) {
|
|
328
|
-
// Do not execute a function call if guardrails are enabled but the status is not known
|
|
329
|
-
if (guardrailsEnabled && value.generateCopilotResponse.status === undefined) {
|
|
330
|
-
break;
|
|
331
|
-
}
|
|
332
|
-
// execute action
|
|
333
|
-
try {
|
|
334
|
-
// We update the message state before calling the handler so that the render
|
|
335
|
-
// function can be called with `executing` state
|
|
336
|
-
setMessages([...previousMessages, ...newMessages]);
|
|
337
|
-
|
|
338
|
-
const action = actions.find((action) => action.name === message.name);
|
|
339
|
-
|
|
340
|
-
if (action) {
|
|
341
|
-
followUp = action.followUp;
|
|
342
|
-
}
|
|
333
|
+
newMessages = [...messages];
|
|
343
334
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
name: message.name,
|
|
347
|
-
args: message.arguments,
|
|
348
|
-
});
|
|
349
|
-
actionResults[message.id] = result;
|
|
350
|
-
} catch (e) {
|
|
351
|
-
actionResults[message.id] = `Failed to execute action ${message.name}`;
|
|
352
|
-
console.error(`Failed to execute action ${message.name}: ${e}`);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
// add the result message
|
|
356
|
-
newMessages.push(
|
|
357
|
-
new ResultMessage({
|
|
358
|
-
result: ResultMessage.encodeResult(actionResults[message.id]),
|
|
359
|
-
actionExecutionId: message.id,
|
|
360
|
-
actionName: message.name,
|
|
361
|
-
}),
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
// execute coagent actions
|
|
335
|
+
for (const message of messages) {
|
|
336
|
+
// execute onCoAgentStateRender handler
|
|
365
337
|
if (
|
|
366
338
|
message.isAgentStateMessage() &&
|
|
367
339
|
!message.active &&
|
|
@@ -387,6 +359,14 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
387
359
|
.find((message) => message.isAgentStateMessage());
|
|
388
360
|
|
|
389
361
|
if (lastAgentStateMessage) {
|
|
362
|
+
if (
|
|
363
|
+
lastAgentStateMessage.state.messages &&
|
|
364
|
+
lastAgentStateMessage.state.messages.length > 0
|
|
365
|
+
) {
|
|
366
|
+
syncedMessages = loadMessagesFromJsonRepresentation(
|
|
367
|
+
lastAgentStateMessage.state.messages,
|
|
368
|
+
);
|
|
369
|
+
}
|
|
390
370
|
setCoagentStatesWithRef((prevAgentStates) => ({
|
|
391
371
|
...prevAgentStates,
|
|
392
372
|
[lastAgentStateMessage.agentName]: {
|
|
@@ -416,14 +396,88 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
416
396
|
setMessages([...previousMessages, ...newMessages]);
|
|
417
397
|
}
|
|
418
398
|
}
|
|
399
|
+
const finalMessages = constructFinalMessages(syncedMessages, previousMessages, newMessages);
|
|
400
|
+
|
|
401
|
+
let didExecuteAction = false;
|
|
402
|
+
|
|
403
|
+
// execute regular action executions that are specific to the frontend (last actions)
|
|
404
|
+
if (onFunctionCall) {
|
|
405
|
+
// Find consecutive action execution messages at the end
|
|
406
|
+
const lastMessages = [];
|
|
407
|
+
for (let i = finalMessages.length - 1; i >= 0; i--) {
|
|
408
|
+
const message = finalMessages[i];
|
|
409
|
+
if (
|
|
410
|
+
message.isActionExecutionMessage() &&
|
|
411
|
+
message.status.code !== MessageStatusCode.Pending
|
|
412
|
+
) {
|
|
413
|
+
lastMessages.unshift(message);
|
|
414
|
+
} else {
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
for (const message of lastMessages) {
|
|
420
|
+
// We update the message state before calling the handler so that the render
|
|
421
|
+
// function can be called with `executing` state
|
|
422
|
+
setMessages(finalMessages);
|
|
423
|
+
|
|
424
|
+
const action = actions.find((action) => action.name === message.name);
|
|
425
|
+
|
|
426
|
+
if (action) {
|
|
427
|
+
followUp = action.followUp;
|
|
428
|
+
let result: any;
|
|
429
|
+
try {
|
|
430
|
+
result = await Promise.race([
|
|
431
|
+
onFunctionCall({
|
|
432
|
+
messages: previousMessages,
|
|
433
|
+
name: message.name,
|
|
434
|
+
args: message.arguments,
|
|
435
|
+
}),
|
|
436
|
+
new Promise((resolve) =>
|
|
437
|
+
chatAbortControllerRef.current?.signal.addEventListener("abort", () =>
|
|
438
|
+
resolve("Operation was aborted by the user"),
|
|
439
|
+
),
|
|
440
|
+
),
|
|
441
|
+
// if the user stopped generation, we also abort consecutive actions
|
|
442
|
+
new Promise((resolve) => {
|
|
443
|
+
if (chatAbortControllerRef.current?.signal.aborted) {
|
|
444
|
+
resolve("Operation was aborted by the user");
|
|
445
|
+
}
|
|
446
|
+
}),
|
|
447
|
+
]);
|
|
448
|
+
} catch (e) {
|
|
449
|
+
result = `Failed to execute action ${message.name}`;
|
|
450
|
+
console.error(`Failed to execute action ${message.name}: ${e}`);
|
|
451
|
+
}
|
|
452
|
+
didExecuteAction = true;
|
|
453
|
+
const messageIndex = finalMessages.findIndex((msg) => msg.id === message.id);
|
|
454
|
+
finalMessages.splice(
|
|
455
|
+
messageIndex + 1,
|
|
456
|
+
0,
|
|
457
|
+
new ResultMessage({
|
|
458
|
+
id: "result-" + message.id,
|
|
459
|
+
result: ResultMessage.encodeResult(result),
|
|
460
|
+
actionExecutionId: message.id,
|
|
461
|
+
actionName: message.name,
|
|
462
|
+
}),
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
setMessages(finalMessages);
|
|
468
|
+
}
|
|
419
469
|
|
|
420
470
|
if (
|
|
421
471
|
// if followUp is not explicitly false
|
|
422
472
|
followUp !== false &&
|
|
423
|
-
//
|
|
424
|
-
(
|
|
425
|
-
//
|
|
426
|
-
(
|
|
473
|
+
// and we executed an action
|
|
474
|
+
(didExecuteAction ||
|
|
475
|
+
// the last message is a server side result
|
|
476
|
+
(!isAgentRun &&
|
|
477
|
+
finalMessages.length &&
|
|
478
|
+
finalMessages[finalMessages.length - 1].isResultMessage())) &&
|
|
479
|
+
// the user did not stop generation
|
|
480
|
+
!chatAbortControllerRef.current?.signal.aborted
|
|
427
481
|
) {
|
|
428
482
|
// run the completion again and return the result
|
|
429
483
|
|
|
@@ -431,7 +485,32 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
431
485
|
// - tried using react-dom's flushSync, but it did not work
|
|
432
486
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
433
487
|
|
|
434
|
-
return await runChatCompletionRef.current!(
|
|
488
|
+
return await runChatCompletionRef.current!(finalMessages);
|
|
489
|
+
} else if (chatAbortControllerRef.current?.signal.aborted) {
|
|
490
|
+
// filter out all the action execution messages that do not have a consecutive matching result message
|
|
491
|
+
const repairedMessages = finalMessages.filter((message, actionExecutionIndex) => {
|
|
492
|
+
if (message.isActionExecutionMessage()) {
|
|
493
|
+
return finalMessages.find(
|
|
494
|
+
(msg, resultIndex) =>
|
|
495
|
+
msg.isResultMessage() &&
|
|
496
|
+
msg.actionExecutionId === message.id &&
|
|
497
|
+
resultIndex === actionExecutionIndex + 1,
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
return true;
|
|
501
|
+
});
|
|
502
|
+
const repairedMessageIds = repairedMessages.map((message) => message.id);
|
|
503
|
+
setMessages(repairedMessages);
|
|
504
|
+
|
|
505
|
+
if (agentSessionRef.current?.nodeName) {
|
|
506
|
+
setAgentSession({
|
|
507
|
+
threadId: agentSessionRef.current.threadId,
|
|
508
|
+
agentName: agentSessionRef.current.agentName,
|
|
509
|
+
nodeName: "__end__",
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
// only return new messages that were not filtered out
|
|
513
|
+
return newMessages.filter((message) => repairedMessageIds.includes(message.id));
|
|
435
514
|
} else {
|
|
436
515
|
return newMessages.slice();
|
|
437
516
|
}
|
|
@@ -496,7 +575,7 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
496
575
|
}, [isLoading, messages, setMessages, runChatCompletionAndHandleFunctionCall]);
|
|
497
576
|
|
|
498
577
|
const stop = (): void => {
|
|
499
|
-
|
|
578
|
+
chatAbortControllerRef.current?.abort("Stop was called");
|
|
500
579
|
};
|
|
501
580
|
|
|
502
581
|
return {
|
|
@@ -506,3 +585,32 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
|
|
|
506
585
|
runChatCompletion: () => runChatCompletionRef.current!(messages),
|
|
507
586
|
};
|
|
508
587
|
}
|
|
588
|
+
|
|
589
|
+
function constructFinalMessages(
|
|
590
|
+
syncedMessages: Message[],
|
|
591
|
+
previousMessages: Message[],
|
|
592
|
+
newMessages: Message[],
|
|
593
|
+
): Message[] {
|
|
594
|
+
const finalMessages =
|
|
595
|
+
syncedMessages.length > 0 ? [...syncedMessages] : [...previousMessages, ...newMessages];
|
|
596
|
+
|
|
597
|
+
if (syncedMessages.length > 0) {
|
|
598
|
+
const messagesWithAgentState = [...previousMessages, ...newMessages];
|
|
599
|
+
|
|
600
|
+
let previousMessageId: string | undefined = undefined;
|
|
601
|
+
|
|
602
|
+
for (const message of messagesWithAgentState) {
|
|
603
|
+
if (message.isAgentStateMessage()) {
|
|
604
|
+
// insert this message into finalMessages after the position of previousMessageId
|
|
605
|
+
const index = finalMessages.findIndex((msg) => msg.id === previousMessageId);
|
|
606
|
+
if (index !== -1) {
|
|
607
|
+
finalMessages.splice(index + 1, 0, message);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
previousMessageId = message.id;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
return finalMessages;
|
|
616
|
+
}
|
package/src/hooks/use-coagent.ts
CHANGED
|
@@ -265,7 +265,11 @@ export function useCoAgent<T = any>(options: UseCoagentOptions<T>): UseCoagentRe
|
|
|
265
265
|
} else if (coagentStates[name] === undefined) {
|
|
266
266
|
setState(options.initialState === undefined ? {} : options.initialState);
|
|
267
267
|
}
|
|
268
|
-
}, [
|
|
268
|
+
}, [
|
|
269
|
+
isExternalStateManagement(options) ? JSON.stringify(options.state) : undefined,
|
|
270
|
+
// reset initialstate on reset
|
|
271
|
+
coagentStates[name] === undefined,
|
|
272
|
+
]);
|
|
269
273
|
|
|
270
274
|
const runAgentCallback = useAsyncCallback(
|
|
271
275
|
async (hint?: HintFunction) => {
|
|
@@ -288,23 +292,36 @@ export function useCoAgent<T = any>(options: UseCoagentOptions<T>): UseCoagentRe
|
|
|
288
292
|
};
|
|
289
293
|
}
|
|
290
294
|
|
|
291
|
-
function startAgent(name: string, context: CopilotContextParams) {
|
|
295
|
+
export function startAgent(name: string, context: CopilotContextParams) {
|
|
292
296
|
const { setAgentSession } = context;
|
|
293
297
|
setAgentSession({
|
|
294
298
|
agentName: name,
|
|
295
299
|
});
|
|
296
300
|
}
|
|
297
301
|
|
|
298
|
-
function stopAgent(name: string, context: CopilotContextParams) {
|
|
302
|
+
export function stopAgent(name: string, context: CopilotContextParams) {
|
|
299
303
|
const { agentSession, setAgentSession } = context;
|
|
300
304
|
if (agentSession && agentSession.agentName === name) {
|
|
301
305
|
setAgentSession(null);
|
|
306
|
+
context.setCoagentStates((prevAgentStates) => {
|
|
307
|
+
return {
|
|
308
|
+
...prevAgentStates,
|
|
309
|
+
[name]: {
|
|
310
|
+
...prevAgentStates[name],
|
|
311
|
+
running: false,
|
|
312
|
+
active: false,
|
|
313
|
+
threadId: undefined,
|
|
314
|
+
nodeName: undefined,
|
|
315
|
+
runId: undefined,
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
});
|
|
302
319
|
} else {
|
|
303
320
|
console.warn(`No agent session found for ${name}`);
|
|
304
321
|
}
|
|
305
322
|
}
|
|
306
323
|
|
|
307
|
-
async function runAgent(
|
|
324
|
+
export async function runAgent(
|
|
308
325
|
name: string,
|
|
309
326
|
context: CopilotContextParams & CopilotMessagesContextParams,
|
|
310
327
|
appendMessage: (message: Message) => Promise<void>,
|
|
@@ -71,6 +71,15 @@
|
|
|
71
71
|
* );
|
|
72
72
|
* },
|
|
73
73
|
* });
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* // Catch all action allows you to render actions that are not defined in the frontend
|
|
77
|
+
* useCopilotAction({
|
|
78
|
+
* name: "*",
|
|
79
|
+
* render: ({ name, args, status, result, handler, respond }) => {
|
|
80
|
+
* return <div>Rendering action: {name}</div>;
|
|
81
|
+
* },
|
|
82
|
+
* });
|
|
74
83
|
*/
|
|
75
84
|
|
|
76
85
|
/**
|
|
@@ -129,6 +138,7 @@ import {
|
|
|
129
138
|
ActionRenderProps,
|
|
130
139
|
ActionRenderPropsNoArgsWait,
|
|
131
140
|
ActionRenderPropsWait,
|
|
141
|
+
CatchAllFrontendAction,
|
|
132
142
|
FrontendAction,
|
|
133
143
|
} from "../types/frontend-action";
|
|
134
144
|
|
|
@@ -142,7 +152,7 @@ import {
|
|
|
142
152
|
// useCallback, useMemo or other memoization techniques are not suitable here,
|
|
143
153
|
// because they will cause a infinite rerender loop.
|
|
144
154
|
export function useCopilotAction<const T extends Parameter[] | [] = []>(
|
|
145
|
-
action: FrontendAction<T
|
|
155
|
+
action: FrontendAction<T> | CatchAllFrontendAction,
|
|
146
156
|
dependencies?: any[],
|
|
147
157
|
): void {
|
|
148
158
|
const { setAction, removeAction, actions, chatComponentsCache } = useCopilotContext();
|
|
@@ -152,9 +162,14 @@ export function useCopilotAction<const T extends Parameter[] | [] = []>(
|
|
|
152
162
|
// clone the action to avoid mutating the original object
|
|
153
163
|
action = { ...action };
|
|
154
164
|
|
|
155
|
-
// If the developer provides a
|
|
165
|
+
// If the developer provides a renderAndWaitForResponse function, we transform the action
|
|
156
166
|
// to use a promise internally, so that we can treat it like a normal action.
|
|
157
|
-
if (
|
|
167
|
+
if (
|
|
168
|
+
// renderAndWaitForResponse is not available for catch all actions
|
|
169
|
+
isFrontendAction(action) &&
|
|
170
|
+
// check if renderAndWaitForResponse is set
|
|
171
|
+
(action.renderAndWait || action.renderAndWaitForResponse)
|
|
172
|
+
) {
|
|
158
173
|
const renderAndWait = action.renderAndWait || action.renderAndWaitForResponse;
|
|
159
174
|
// remove the renderAndWait function from the action
|
|
160
175
|
action.renderAndWait = undefined;
|
|
@@ -212,10 +227,15 @@ export function useCopilotAction<const T extends Parameter[] | [] = []>(
|
|
|
212
227
|
// This ensures that any captured variables in the handler are up to date.
|
|
213
228
|
if (dependencies === undefined) {
|
|
214
229
|
if (actions[idRef.current]) {
|
|
215
|
-
actions
|
|
230
|
+
// catch all actions don't have a handler
|
|
231
|
+
if (isFrontendAction(action)) {
|
|
232
|
+
actions[idRef.current].handler = action.handler as any;
|
|
233
|
+
}
|
|
216
234
|
if (typeof action.render === "function") {
|
|
217
235
|
if (chatComponentsCache.current !== null) {
|
|
218
|
-
|
|
236
|
+
// TODO: using as any here because the type definitions are getting to tricky
|
|
237
|
+
// not wasting time on this now - we know the types are compatible
|
|
238
|
+
chatComponentsCache.current.actions[action.name] = action.render as any;
|
|
219
239
|
}
|
|
220
240
|
}
|
|
221
241
|
}
|
|
@@ -224,23 +244,25 @@ export function useCopilotAction<const T extends Parameter[] | [] = []>(
|
|
|
224
244
|
useEffect(() => {
|
|
225
245
|
setAction(idRef.current, action as any);
|
|
226
246
|
if (chatComponentsCache.current !== null && action.render !== undefined) {
|
|
227
|
-
|
|
247
|
+
// see comment about type safety above
|
|
248
|
+
chatComponentsCache.current.actions[action.name] = action.render as any;
|
|
228
249
|
}
|
|
229
250
|
return () => {
|
|
230
251
|
// NOTE: For now, we don't remove the chatComponentsCache entry when the action is removed.
|
|
231
252
|
// This is because we currently don't have access to the messages array in CopilotContext.
|
|
253
|
+
// UPDATE: We now have access, we should remove the entry if not referenced by any message.
|
|
232
254
|
removeAction(idRef.current);
|
|
233
255
|
};
|
|
234
256
|
}, [
|
|
235
257
|
setAction,
|
|
236
258
|
removeAction,
|
|
237
|
-
action.description,
|
|
259
|
+
isFrontendAction(action) ? action.description : undefined,
|
|
238
260
|
action.name,
|
|
239
|
-
action.disabled,
|
|
240
|
-
action.available,
|
|
261
|
+
isFrontendAction(action) ? action.disabled : undefined,
|
|
262
|
+
isFrontendAction(action) ? action.available : undefined,
|
|
241
263
|
// This should be faster than deep equality checking
|
|
242
264
|
// In addition, all major JS engines guarantee the order of object keys
|
|
243
|
-
JSON.stringify(action.parameters),
|
|
265
|
+
JSON.stringify(isFrontendAction(action) ? action.parameters : []),
|
|
244
266
|
// include render only if it's a string
|
|
245
267
|
typeof action.render === "string" ? action.render : undefined,
|
|
246
268
|
// dependencies set by the developer
|
|
@@ -248,6 +270,12 @@ export function useCopilotAction<const T extends Parameter[] | [] = []>(
|
|
|
248
270
|
]);
|
|
249
271
|
}
|
|
250
272
|
|
|
273
|
+
function isFrontendAction<T extends Parameter[]>(
|
|
274
|
+
action: FrontendAction<T> | CatchAllFrontendAction,
|
|
275
|
+
): action is FrontendAction<T> {
|
|
276
|
+
return action.name !== "*";
|
|
277
|
+
}
|
|
278
|
+
|
|
251
279
|
interface RenderAndWaitForResponse {
|
|
252
280
|
promise: Promise<any>;
|
|
253
281
|
resolve: (result: any) => void;
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* ```
|
|
40
40
|
*/
|
|
41
41
|
import { useRef, useEffect, useCallback } from "react";
|
|
42
|
-
import { useCopilotContext } from "../context/copilot-context";
|
|
42
|
+
import { AgentSession, useCopilotContext } from "../context/copilot-context";
|
|
43
43
|
import { Message, Role, TextMessage } from "@copilotkit/runtime-client-gql";
|
|
44
44
|
import { SystemMessageFunction } from "../types";
|
|
45
45
|
import { useChat } from "./use-chat";
|
|
@@ -79,6 +79,7 @@ export interface UseCopilotChatReturn {
|
|
|
79
79
|
deleteMessage: (messageId: string) => void;
|
|
80
80
|
reloadMessages: () => Promise<void>;
|
|
81
81
|
stopGeneration: () => void;
|
|
82
|
+
reset: () => void;
|
|
82
83
|
isLoading: boolean;
|
|
83
84
|
runChatCompletion: () => Promise<Message[]>;
|
|
84
85
|
}
|
|
@@ -100,6 +101,12 @@ export function useCopilotChat({
|
|
|
100
101
|
coAgentStateRenders,
|
|
101
102
|
agentSession,
|
|
102
103
|
setAgentSession,
|
|
104
|
+
agentLock,
|
|
105
|
+
threadId,
|
|
106
|
+
setThreadId,
|
|
107
|
+
runId,
|
|
108
|
+
setRunId,
|
|
109
|
+
chatAbortControllerRef,
|
|
103
110
|
} = useCopilotContext();
|
|
104
111
|
const { messages, setMessages } = useCopilotMessagesContext();
|
|
105
112
|
|
|
@@ -158,11 +165,16 @@ export function useCopilotChat({
|
|
|
158
165
|
setCoagentStatesWithRef,
|
|
159
166
|
agentSession,
|
|
160
167
|
setAgentSession,
|
|
168
|
+
threadId,
|
|
169
|
+
setThreadId,
|
|
170
|
+
runId,
|
|
171
|
+
setRunId,
|
|
172
|
+
chatAbortControllerRef,
|
|
161
173
|
});
|
|
162
174
|
|
|
163
|
-
// this is a workaround born out of a bug that Athena
|
|
175
|
+
// this is a workaround born out of a bug that Athena incessantly ran into.
|
|
164
176
|
// We could not find the origin of the bug, however, it was clear that an outdated version of the append function was being used somehow --
|
|
165
|
-
// it
|
|
177
|
+
// it referenced the old state of the messages array, and not the latest one.
|
|
166
178
|
//
|
|
167
179
|
// We want to make copilotkit as abuse-proof as possible, so we are adding this workaround to ensure that the latest version of the append function is always used.
|
|
168
180
|
//
|
|
@@ -207,12 +219,40 @@ export function useCopilotChat({
|
|
|
207
219
|
return await latestRunChatCompletion.current!();
|
|
208
220
|
}, [latestRunChatCompletion]);
|
|
209
221
|
|
|
222
|
+
const reset = useCallback(() => {
|
|
223
|
+
latestStopFunc();
|
|
224
|
+
setMessages([]);
|
|
225
|
+
setThreadId(null);
|
|
226
|
+
setRunId(null);
|
|
227
|
+
setCoagentStatesWithRef({});
|
|
228
|
+
let initialAgentSession: AgentSession | null = null;
|
|
229
|
+
if (agentLock) {
|
|
230
|
+
initialAgentSession = {
|
|
231
|
+
agentName: agentLock,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
setAgentSession(initialAgentSession);
|
|
235
|
+
}, [
|
|
236
|
+
latestStopFunc,
|
|
237
|
+
setMessages,
|
|
238
|
+
setThreadId,
|
|
239
|
+
setCoagentStatesWithRef,
|
|
240
|
+
setAgentSession,
|
|
241
|
+
agentLock,
|
|
242
|
+
]);
|
|
243
|
+
|
|
244
|
+
const latestReset = useUpdatedRef(reset);
|
|
245
|
+
const latestResetFunc = useCallback(() => {
|
|
246
|
+
return latestReset.current();
|
|
247
|
+
}, [latestReset]);
|
|
248
|
+
|
|
210
249
|
return {
|
|
211
250
|
visibleMessages: messages,
|
|
212
251
|
appendMessage: latestAppendFunc,
|
|
213
252
|
setMessages: latestSetMessagesFunc,
|
|
214
253
|
reloadMessages: latestReloadFunc,
|
|
215
254
|
stopGeneration: latestStopFunc,
|
|
255
|
+
reset: latestResetFunc,
|
|
216
256
|
deleteMessage: latestDeleteFunc,
|
|
217
257
|
runChatCompletion: latestRunChatCompletionFunc,
|
|
218
258
|
isLoading,
|