@copilotkit/vue 1.68.3-canary.1787220377 → 1.69.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/PARITY.md +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{use-render-activity-message-DljRBrXN.js → use-render-activity-message-BQADVWev.js} +2242 -2193
- package/dist/use-render-activity-message-BQADVWev.js.map +1 -0
- package/dist/{use-render-activity-message-bkrPa-92.cjs → use-render-activity-message-DYb_lsQs.cjs} +22 -22
- package/dist/use-render-activity-message-DYb_lsQs.cjs.map +1 -0
- package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts +16 -40
- package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts +21 -0
- package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts +10 -0
- package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts.map +1 -0
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.mjs +1 -1
- package/package.json +6 -6
- package/src/v2/components/chat/CopilotChatToolCallItem.vue +86 -0
- package/src/v2/components/chat/CopilotChatToolCallsView.vue +24 -136
- package/src/v2/components/chat/__tests__/CopilotChatToolCallsView.test.ts +302 -1
- package/src/v2/components/chat/__tests__/CopilotThreadsDrawer.ssr.test.ts +1 -1
- package/src/v2/components/chat/copilotChatToolCallItemRender.ts +126 -0
- package/dist/use-render-activity-message-DljRBrXN.js.map +0 -1
- package/dist/use-render-activity-message-bkrPa-92.cjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { defineComponent, h } from "vue";
|
|
2
|
+
import { defineComponent, h, nextTick, ref } from "vue";
|
|
3
3
|
import { mount } from "@vue/test-utils";
|
|
4
4
|
import type { AssistantMessage, Message, ToolMessage } from "@ag-ui/core";
|
|
5
5
|
import CopilotKitProvider from "../../../providers/CopilotKitProvider.vue";
|
|
@@ -252,6 +252,307 @@ describe("CopilotChatToolCallsView", () => {
|
|
|
252
252
|
);
|
|
253
253
|
});
|
|
254
254
|
|
|
255
|
+
it("updates generic consumer-provided tool-call slots when parent state changes", async () => {
|
|
256
|
+
const message = baseAssistantMessage();
|
|
257
|
+
const label = ref("first");
|
|
258
|
+
const Harness = defineComponent({
|
|
259
|
+
setup() {
|
|
260
|
+
return () => {
|
|
261
|
+
const currentLabel = label.value;
|
|
262
|
+
return h(
|
|
263
|
+
CopilotChatToolCallsView,
|
|
264
|
+
{ message, messages: [message] },
|
|
265
|
+
{
|
|
266
|
+
"tool-call": () =>
|
|
267
|
+
h("div", { "data-testid": "slot-label" }, currentLabel),
|
|
268
|
+
},
|
|
269
|
+
);
|
|
270
|
+
};
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const wrapper = mount(CopilotKitProvider, {
|
|
275
|
+
props: { runtimeUrl: "/api/copilotkit" },
|
|
276
|
+
slots: {
|
|
277
|
+
default: () =>
|
|
278
|
+
h(
|
|
279
|
+
CopilotChatConfigurationProvider,
|
|
280
|
+
{ threadId: "thread-1", agentId: "default" },
|
|
281
|
+
{ default: () => h(Harness) },
|
|
282
|
+
),
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
expect(wrapper.get("[data-testid='slot-label']").text()).toBe("first");
|
|
287
|
+
label.value = "second";
|
|
288
|
+
await nextTick();
|
|
289
|
+
expect(wrapper.get("[data-testid='slot-label']").text()).toBe("second");
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("updates exact consumer-provided tool-call slots when parent state changes", async () => {
|
|
293
|
+
const message = baseAssistantMessage();
|
|
294
|
+
const label = ref("first");
|
|
295
|
+
const Harness = defineComponent({
|
|
296
|
+
setup() {
|
|
297
|
+
return () => {
|
|
298
|
+
const currentLabel = label.value;
|
|
299
|
+
return h(
|
|
300
|
+
CopilotChatToolCallsView,
|
|
301
|
+
{ message, messages: [message] },
|
|
302
|
+
{
|
|
303
|
+
"tool-call-search_docs": () =>
|
|
304
|
+
h("div", { "data-testid": "slot-label" }, currentLabel),
|
|
305
|
+
},
|
|
306
|
+
);
|
|
307
|
+
};
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
const wrapper = mount(CopilotKitProvider, {
|
|
312
|
+
props: { runtimeUrl: "/api/copilotkit" },
|
|
313
|
+
slots: {
|
|
314
|
+
default: () =>
|
|
315
|
+
h(
|
|
316
|
+
CopilotChatConfigurationProvider,
|
|
317
|
+
{ threadId: "thread-1", agentId: "default" },
|
|
318
|
+
{ default: () => h(Harness) },
|
|
319
|
+
),
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
expect(wrapper.get("[data-testid='slot-label']").text()).toBe("first");
|
|
324
|
+
label.value = "second";
|
|
325
|
+
await nextTick();
|
|
326
|
+
expect(wrapper.get("[data-testid='slot-label']").text()).toBe("second");
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("forwards a consumer-provided tool-call slot when slot membership changes", async () => {
|
|
330
|
+
const message = baseAssistantMessage();
|
|
331
|
+
const showCustom = ref(false);
|
|
332
|
+
const Harness = defineComponent({
|
|
333
|
+
components: { CopilotChatToolCallsView },
|
|
334
|
+
setup() {
|
|
335
|
+
return { message, showCustom };
|
|
336
|
+
},
|
|
337
|
+
template: `
|
|
338
|
+
<CopilotChatToolCallsView :message="message" :messages="[message]">
|
|
339
|
+
<template v-if="showCustom" #tool-call-search_docs>
|
|
340
|
+
<div data-testid="custom-tool-slot">CUSTOM</div>
|
|
341
|
+
</template>
|
|
342
|
+
</CopilotChatToolCallsView>
|
|
343
|
+
`,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
const wrapper = mount(CopilotKitProvider, {
|
|
347
|
+
props: { runtimeUrl: "/api/copilotkit" },
|
|
348
|
+
slots: {
|
|
349
|
+
default: () =>
|
|
350
|
+
h(
|
|
351
|
+
CopilotChatConfigurationProvider,
|
|
352
|
+
{ threadId: "thread-1", agentId: "default" },
|
|
353
|
+
{ default: () => h(Harness) },
|
|
354
|
+
),
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
expect(wrapper.find("[data-testid='custom-tool-slot']").exists()).toBe(
|
|
359
|
+
false,
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
showCustom.value = true;
|
|
363
|
+
await nextTick();
|
|
364
|
+
expect(wrapper.find("[data-testid='custom-tool-slot']").text()).toBe(
|
|
365
|
+
"CUSTOM",
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
showCustom.value = false;
|
|
369
|
+
await nextTick();
|
|
370
|
+
expect(wrapper.find("[data-testid='custom-tool-slot']").exists()).toBe(
|
|
371
|
+
false,
|
|
372
|
+
);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it("skips unchanged fallback renderers when an unrelated parent value changes", async () => {
|
|
376
|
+
const message = baseAssistantMessage();
|
|
377
|
+
const label = ref("first");
|
|
378
|
+
let renderCount = 0;
|
|
379
|
+
const ToolRenderer = defineComponent({
|
|
380
|
+
setup() {
|
|
381
|
+
return () => {
|
|
382
|
+
renderCount += 1;
|
|
383
|
+
return h("div", { "data-testid": "core-render-tool" }, "rendered");
|
|
384
|
+
};
|
|
385
|
+
},
|
|
386
|
+
});
|
|
387
|
+
const RegisterRenderer = defineComponent({
|
|
388
|
+
setup() {
|
|
389
|
+
const { copilotkit } = useCopilotKit();
|
|
390
|
+
copilotkit.value.setRenderToolCalls([
|
|
391
|
+
defineToolCallRenderer({
|
|
392
|
+
name: "search_docs",
|
|
393
|
+
args: undefined,
|
|
394
|
+
render: ToolRenderer,
|
|
395
|
+
}),
|
|
396
|
+
]);
|
|
397
|
+
return () => null;
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
const Harness = defineComponent({
|
|
401
|
+
setup() {
|
|
402
|
+
return () =>
|
|
403
|
+
h("div", [
|
|
404
|
+
h("span", { "data-testid": "unrelated-value" }, label.value),
|
|
405
|
+
h(RegisterRenderer),
|
|
406
|
+
h(CopilotChatToolCallsView, { message, messages: [message] }),
|
|
407
|
+
]);
|
|
408
|
+
},
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
const wrapper = mount(CopilotKitProvider, {
|
|
412
|
+
props: { runtimeUrl: "/api/copilotkit" },
|
|
413
|
+
slots: {
|
|
414
|
+
default: () =>
|
|
415
|
+
h(
|
|
416
|
+
CopilotChatConfigurationProvider,
|
|
417
|
+
{ threadId: "thread-1", agentId: "default" },
|
|
418
|
+
{ default: () => h(Harness) },
|
|
419
|
+
),
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
expect(renderCount).toBe(1);
|
|
424
|
+
label.value = "second";
|
|
425
|
+
await nextTick();
|
|
426
|
+
expect(wrapper.get("[data-testid='unrelated-value']").text()).toBe(
|
|
427
|
+
"second",
|
|
428
|
+
);
|
|
429
|
+
expect(renderCount).toBe(1);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it("updates a fallback renderer when the tool name changes", async () => {
|
|
433
|
+
const message = baseAssistantMessage();
|
|
434
|
+
const toolCall = message.toolCalls![0];
|
|
435
|
+
const currentMessage = ref(message);
|
|
436
|
+
const ToolRenderer = defineComponent({
|
|
437
|
+
props: {
|
|
438
|
+
name: { type: String, required: true },
|
|
439
|
+
},
|
|
440
|
+
template: `<div data-testid="fallback-tool-name">{{ name }}</div>`,
|
|
441
|
+
});
|
|
442
|
+
const RegisterRenderer = defineComponent({
|
|
443
|
+
setup() {
|
|
444
|
+
const { copilotkit } = useCopilotKit();
|
|
445
|
+
copilotkit.value.setRenderToolCalls([
|
|
446
|
+
defineToolCallRenderer({ name: "*", render: ToolRenderer }),
|
|
447
|
+
]);
|
|
448
|
+
return () => null;
|
|
449
|
+
},
|
|
450
|
+
});
|
|
451
|
+
const Harness = defineComponent({
|
|
452
|
+
setup() {
|
|
453
|
+
return () =>
|
|
454
|
+
h("div", [
|
|
455
|
+
h(RegisterRenderer),
|
|
456
|
+
h(CopilotChatToolCallsView, {
|
|
457
|
+
message: currentMessage.value,
|
|
458
|
+
messages: [currentMessage.value],
|
|
459
|
+
}),
|
|
460
|
+
]);
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
const wrapper = mount(CopilotKitProvider, {
|
|
465
|
+
props: { runtimeUrl: "/api/copilotkit" },
|
|
466
|
+
slots: {
|
|
467
|
+
default: () =>
|
|
468
|
+
h(
|
|
469
|
+
CopilotChatConfigurationProvider,
|
|
470
|
+
{ threadId: "thread-1", agentId: "default" },
|
|
471
|
+
{ default: () => h(Harness) },
|
|
472
|
+
),
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
expect(wrapper.get("[data-testid='fallback-tool-name']").text()).toBe(
|
|
477
|
+
"search_docs",
|
|
478
|
+
);
|
|
479
|
+
currentMessage.value = {
|
|
480
|
+
...message,
|
|
481
|
+
toolCalls: [
|
|
482
|
+
{
|
|
483
|
+
...toolCall,
|
|
484
|
+
function: { ...toolCall.function, name: "other_tool" },
|
|
485
|
+
},
|
|
486
|
+
],
|
|
487
|
+
};
|
|
488
|
+
await nextTick();
|
|
489
|
+
expect(wrapper.get("[data-testid='fallback-tool-name']").text()).toBe(
|
|
490
|
+
"other_tool",
|
|
491
|
+
);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it("updates a fallback renderer when agent-specific selection changes", async () => {
|
|
495
|
+
const message = baseAssistantMessage();
|
|
496
|
+
const agentId = ref("agent-a");
|
|
497
|
+
const AgentARenderer = defineComponent({
|
|
498
|
+
template: `<div data-testid="agent-renderer">agent-a</div>`,
|
|
499
|
+
});
|
|
500
|
+
const AgentBRenderer = defineComponent({
|
|
501
|
+
template: `<div data-testid="agent-renderer">agent-b</div>`,
|
|
502
|
+
});
|
|
503
|
+
const RegisterRenderer = defineComponent({
|
|
504
|
+
setup() {
|
|
505
|
+
const { copilotkit } = useCopilotKit();
|
|
506
|
+
copilotkit.value.setRenderToolCalls([
|
|
507
|
+
defineToolCallRenderer({
|
|
508
|
+
name: "search_docs",
|
|
509
|
+
agentId: "agent-a",
|
|
510
|
+
render: AgentARenderer,
|
|
511
|
+
}),
|
|
512
|
+
defineToolCallRenderer({
|
|
513
|
+
name: "search_docs",
|
|
514
|
+
agentId: "agent-b",
|
|
515
|
+
render: AgentBRenderer,
|
|
516
|
+
}),
|
|
517
|
+
]);
|
|
518
|
+
return () => null;
|
|
519
|
+
},
|
|
520
|
+
});
|
|
521
|
+
const Harness = defineComponent({
|
|
522
|
+
setup() {
|
|
523
|
+
return () =>
|
|
524
|
+
h(
|
|
525
|
+
CopilotChatConfigurationProvider,
|
|
526
|
+
{ threadId: "thread-1", agentId: agentId.value },
|
|
527
|
+
{
|
|
528
|
+
default: () =>
|
|
529
|
+
h("div", [
|
|
530
|
+
h(RegisterRenderer),
|
|
531
|
+
h(CopilotChatToolCallsView, {
|
|
532
|
+
message,
|
|
533
|
+
messages: [message],
|
|
534
|
+
}),
|
|
535
|
+
]),
|
|
536
|
+
},
|
|
537
|
+
);
|
|
538
|
+
},
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
const wrapper = mount(CopilotKitProvider, {
|
|
542
|
+
props: { runtimeUrl: "/api/copilotkit" },
|
|
543
|
+
slots: { default: () => h(Harness) },
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
expect(wrapper.get("[data-testid='agent-renderer']").text()).toBe(
|
|
547
|
+
"agent-a",
|
|
548
|
+
);
|
|
549
|
+
agentId.value = "agent-b";
|
|
550
|
+
await nextTick();
|
|
551
|
+
expect(wrapper.get("[data-testid='agent-renderer']").text()).toBe(
|
|
552
|
+
"agent-b",
|
|
553
|
+
);
|
|
554
|
+
});
|
|
555
|
+
|
|
255
556
|
it("prefers current-agent renderers over unscoped and wildcard renderers", () => {
|
|
256
557
|
const message = baseAssistantMessage("search_docs");
|
|
257
558
|
const RegisterRenderer = defineComponent({
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { Message, ToolCall, ToolMessage } from "@ag-ui/core";
|
|
2
|
+
import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
|
|
3
|
+
import { ToolCallStatus } from "@copilotkit/core";
|
|
4
|
+
import { partialJSONParse } from "@copilotkit/shared";
|
|
5
|
+
import type { CopilotChatToolCallRenderSlotProps } from "./types";
|
|
6
|
+
import type {
|
|
7
|
+
VueToolCallRenderer,
|
|
8
|
+
VueToolCallRendererRenderProps,
|
|
9
|
+
} from "../../types";
|
|
10
|
+
|
|
11
|
+
export function findToolMessage(
|
|
12
|
+
messages: Message[],
|
|
13
|
+
toolCallId: string,
|
|
14
|
+
): ToolMessage | undefined {
|
|
15
|
+
return messages.find(
|
|
16
|
+
(message) =>
|
|
17
|
+
message.role === "tool" &&
|
|
18
|
+
(message as ToolMessage).toolCallId === toolCallId,
|
|
19
|
+
) as ToolMessage | undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getToolCallSlotName(toolName: string): `tool-call-${string}` {
|
|
23
|
+
return `tool-call-${toolName}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getCoreRenderConfig(
|
|
27
|
+
toolCall: ToolCall,
|
|
28
|
+
renderToolCalls: readonly VueToolCallRenderer<unknown>[],
|
|
29
|
+
agentId: string,
|
|
30
|
+
): VueToolCallRenderer<unknown> | undefined {
|
|
31
|
+
const exactMatches = renderToolCalls.filter(
|
|
32
|
+
(renderConfig) => renderConfig.name === toolCall.function.name,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
exactMatches.find((renderConfig) => renderConfig.agentId === agentId) ??
|
|
37
|
+
exactMatches.find((renderConfig) => !renderConfig.agentId) ??
|
|
38
|
+
exactMatches[0] ??
|
|
39
|
+
renderToolCalls.find((renderConfig) => renderConfig.name === "*")
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getToolCallRenderProps(
|
|
44
|
+
toolCall: ToolCall,
|
|
45
|
+
messages: Message[],
|
|
46
|
+
executingToolCallIds: ReadonlySet<string>,
|
|
47
|
+
): CopilotChatToolCallRenderSlotProps {
|
|
48
|
+
const toolMessage = findToolMessage(messages, toolCall.id);
|
|
49
|
+
const parsedArgs = partialJSONParse(toolCall.function.arguments);
|
|
50
|
+
|
|
51
|
+
if (toolMessage) {
|
|
52
|
+
return {
|
|
53
|
+
name: toolCall.function.name,
|
|
54
|
+
args: parsedArgs,
|
|
55
|
+
status: ToolCallStatus.Complete,
|
|
56
|
+
result: toolMessage.content,
|
|
57
|
+
toolCall,
|
|
58
|
+
toolMessage,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const isExecuting = executingToolCallIds.has(toolCall.id);
|
|
63
|
+
return {
|
|
64
|
+
name: toolCall.function.name,
|
|
65
|
+
args: parsedArgs,
|
|
66
|
+
status: isExecuting ? ToolCallStatus.Executing : ToolCallStatus.InProgress,
|
|
67
|
+
result: undefined,
|
|
68
|
+
toolCall,
|
|
69
|
+
toolMessage: undefined,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function getCoreRenderProps(
|
|
74
|
+
toolCall: ToolCall,
|
|
75
|
+
messages: Message[],
|
|
76
|
+
executingToolCallIds: ReadonlySet<string>,
|
|
77
|
+
): VueToolCallRendererRenderProps<unknown> {
|
|
78
|
+
const toolMessage = findToolMessage(messages, toolCall.id);
|
|
79
|
+
const parsedArgs = partialJSONParse(toolCall.function.arguments);
|
|
80
|
+
|
|
81
|
+
if (toolMessage) {
|
|
82
|
+
return {
|
|
83
|
+
name: toolCall.function.name,
|
|
84
|
+
toolCallId: toolCall.id,
|
|
85
|
+
args: parsedArgs,
|
|
86
|
+
status: ToolCallStatus.Complete,
|
|
87
|
+
result: toolMessage.content,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const isExecuting = executingToolCallIds.has(toolCall.id);
|
|
92
|
+
if (isExecuting) {
|
|
93
|
+
return {
|
|
94
|
+
name: toolCall.function.name,
|
|
95
|
+
toolCallId: toolCall.id,
|
|
96
|
+
args: parsedArgs,
|
|
97
|
+
status: ToolCallStatus.Executing,
|
|
98
|
+
result: undefined,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
name: toolCall.function.name,
|
|
104
|
+
toolCallId: toolCall.id,
|
|
105
|
+
args: parsedArgs && typeof parsedArgs === "object" ? parsedArgs : {},
|
|
106
|
+
status: ToolCallStatus.InProgress,
|
|
107
|
+
result: undefined,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function getToolCallMemoDeps(
|
|
112
|
+
toolCall: ToolCall,
|
|
113
|
+
messages: Message[],
|
|
114
|
+
executingToolCallIds: ReadonlySet<string>,
|
|
115
|
+
renderToolCalls: readonly VueToolCallRenderer<unknown>[],
|
|
116
|
+
agentId: string = DEFAULT_AGENT_ID,
|
|
117
|
+
): unknown[] {
|
|
118
|
+
return [
|
|
119
|
+
toolCall.id,
|
|
120
|
+
toolCall.function.name,
|
|
121
|
+
toolCall.function.arguments,
|
|
122
|
+
findToolMessage(messages, toolCall.id)?.content,
|
|
123
|
+
executingToolCallIds.has(toolCall.id),
|
|
124
|
+
getCoreRenderConfig(toolCall, renderToolCalls, agentId)?.render,
|
|
125
|
+
];
|
|
126
|
+
}
|