@copilotkit/vue 2.0.0-rc.6 → 2.0.0-rc.8
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 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +292 -16949
- package/dist/index.mjs.map +1 -1
- package/dist/use-render-activity-message-C5LCG9a9.js +16451 -0
- package/dist/use-render-activity-message-C5LCG9a9.js.map +1 -0
- package/dist/use-render-activity-message-CPzguixR.cjs +74 -0
- package/dist/use-render-activity-message-CPzguixR.cjs.map +1 -0
- package/dist/v2/components/LicenseWarningBanner.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatAttachmentRenderer.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +6 -270
- package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts +1 -1
- package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatReasoningMessage.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts +0 -1
- package/dist/v2/components/chat/copilotChatToolCallItemRender.d.ts.map +1 -1
- package/dist/v2/components/chat/index.d.ts +0 -1
- package/dist/v2/components/chat/index.d.ts.map +1 -1
- package/dist/v2/components/chat/tool-call-display-context.d.ts +2 -19
- package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -1
- package/dist/v2/components/chat/tool-call-group-parts.d.ts +7 -20
- package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -1
- package/dist/v2/components/chat/turn-work.d.ts +0 -2
- package/dist/v2/components/chat/turn-work.d.ts.map +1 -1
- package/dist/v2/components/chat/types.d.ts +1 -2
- package/dist/v2/components/chat/types.d.ts.map +1 -1
- package/dist/v2/components/chat/use-chat-labels.d.ts +8 -0
- package/dist/v2/components/chat/use-chat-labels.d.ts.map +1 -0
- package/dist/v2/components/icons/index.d.ts +968 -1
- package/dist/v2/components/icons/index.d.ts.map +1 -1
- package/dist/v2/hooks/use-frontend-tool.d.ts +5 -0
- package/dist/v2/hooks/use-frontend-tool.d.ts.map +1 -1
- package/dist/v2/hooks/use-render-activity-message.d.ts +13 -9
- package/dist/v2/hooks/use-render-activity-message.d.ts.map +1 -1
- package/dist/v2/hooks/use-render-custom-messages.d.ts +5 -7
- package/dist/v2/hooks/use-render-custom-messages.d.ts.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.mjs +196 -199
- package/dist/v2/index.mjs.map +1 -1
- package/dist/v2/lib/vue-core.d.ts +0 -1
- package/dist/v2/lib/vue-core.d.ts.map +1 -1
- package/dist/v2/providers/CopilotKitProvider.types.d.ts +0 -5
- package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
- package/dist/v2/providers/types.d.ts +3 -46
- package/dist/v2/providers/types.d.ts.map +1 -1
- package/dist/v2/types/human-in-the-loop.d.ts +8 -29
- package/dist/v2/types/human-in-the-loop.d.ts.map +1 -1
- package/dist/v2/types/interrupt.d.ts +1 -22
- package/dist/v2/types/interrupt.d.ts.map +1 -1
- package/dist/v2/types/vue-activity-message-renderer.d.ts +16 -15
- package/dist/v2/types/vue-activity-message-renderer.d.ts.map +1 -1
- package/dist/v2/types/vue-custom-message-renderer.d.ts +7 -16
- package/dist/v2/types/vue-custom-message-renderer.d.ts.map +1 -1
- package/dist/v2/types/vue-tool-call-renderer.d.ts +16 -35
- package/dist/v2/types/vue-tool-call-renderer.d.ts.map +1 -1
- package/package.json +7 -8
- package/src/__tests__/build-entries.test.ts +27 -0
- package/src/v2/components/LicenseWarningBanner.vue +31 -15
- package/src/v2/components/__tests__/LicenseWarningBanner.test.ts +20 -0
- package/src/v2/components/chat/CopilotChat.vue +19 -40
- package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +14 -10
- package/src/v2/components/chat/CopilotChatAttachmentRenderer.vue +14 -6
- package/src/v2/components/chat/CopilotChatInput.vue +40 -30
- package/src/v2/components/chat/CopilotChatMessageView.vue +72 -140
- package/src/v2/components/chat/CopilotChatReasoningMessage.vue +31 -13
- package/src/v2/components/chat/CopilotChatToolCallGroup.vue +60 -37
- package/src/v2/components/chat/CopilotChatToolCallItem.vue +3 -3
- package/src/v2/components/chat/CopilotChatToolCallsView.vue +15 -13
- package/src/v2/components/chat/CopilotChatView.vue +5 -5
- package/src/v2/components/chat/CopilotChatWorkSummary.vue +44 -18
- package/src/v2/components/chat/__tests__/CopilotChat.labels.test.ts +19 -4
- package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +33 -1
- package/src/v2/components/chat/__tests__/CopilotChatMessageView.test.ts +247 -403
- package/src/v2/components/chat/__tests__/CopilotChatMessageView.toolCallDisplay.test.ts +1 -99
- package/src/v2/components/chat/copilotChatToolCallItemRender.ts +3 -19
- package/src/v2/components/chat/index.ts +0 -1
- package/src/v2/components/chat/tool-call-display-context.ts +2 -23
- package/src/v2/components/chat/tool-call-group-parts.ts +44 -85
- package/src/v2/components/chat/turn-work.ts +0 -9
- package/src/v2/components/chat/types.ts +4 -4
- package/src/v2/components/chat/use-chat-labels.ts +16 -0
- package/src/v2/components/icons/__tests__/icons.test.ts +72 -68
- package/src/v2/components/icons/index.ts +109 -27
- package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.ts +62 -1
- package/src/v2/hooks/__tests__/use-render-activity-message.test.ts +105 -0
- package/src/v2/hooks/__tests__/use-render-tool.test.ts +3 -6
- package/src/v2/hooks/__tests__/zod-regression.test.ts +4 -3
- package/src/v2/hooks/use-frontend-tool.ts +10 -10
- package/src/v2/hooks/use-render-activity-message.ts +27 -48
- package/src/v2/hooks/use-render-custom-messages.ts +14 -35
- package/src/v2/lib/vue-core.ts +3 -5
- package/src/v2/providers/CopilotKitProvider.types.ts +0 -5
- package/src/v2/providers/CopilotKitProvider.vue +29 -80
- package/src/v2/providers/__tests__/CopilotKitProvider.headersAtSendTime.test.ts +3 -3
- package/src/v2/providers/__tests__/CopilotKitProvider.learning.test.ts +3 -1
- package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +17 -33
- package/src/v2/providers/types.ts +3 -49
- package/src/v2/types/__tests__/defineToolCallRenderer.test.ts +28 -33
- package/src/v2/types/defineToolCallRenderer.ts +5 -4
- package/src/v2/types/human-in-the-loop.ts +8 -32
- package/src/v2/types/interrupt.ts +7 -31
- package/src/v2/types/vue-activity-message-renderer.ts +24 -15
- package/src/v2/types/vue-custom-message-renderer.ts +12 -16
- package/src/v2/types/vue-tool-call-renderer.ts +23 -38
- package/vite.config.ts +1 -2
- package/dist/v2/components/chat/BasicMarkdown.vue.d.ts +0 -9
- package/dist/v2/components/chat/BasicMarkdown.vue.d.ts.map +0 -1
- package/dist/v2/components/chat/tool-call-layout.d.ts +0 -115
- package/dist/v2/components/chat/tool-call-layout.d.ts.map +0 -1
- package/dist/v2/lib/transcription-client.d.ts +0 -19
- package/dist/v2/lib/transcription-client.d.ts.map +0 -1
- package/src/v2/components/chat/BasicMarkdown.vue +0 -160
- package/src/v2/components/chat/__tests__/BasicMarkdown.test.ts +0 -69
- package/src/v2/components/chat/tool-call-layout.ts +0 -304
- package/src/v2/lib/__tests__/transcription-client.test.ts +0 -67
- package/src/v2/lib/transcription-client.ts +0 -151
|
@@ -11,12 +11,7 @@ import type { VueToolCallRenderer } from "../../../types";
|
|
|
11
11
|
import { useDefaultRenderTool } from "../../../hooks/use-default-render-tool";
|
|
12
12
|
import CopilotChatMessageView from "../CopilotChatMessageView.vue";
|
|
13
13
|
import CopilotChatToolCallsView from "../CopilotChatToolCallsView.vue";
|
|
14
|
-
import {
|
|
15
|
-
getToolCallLayouts,
|
|
16
|
-
getToolCallRendererNames,
|
|
17
|
-
getTurnWork,
|
|
18
|
-
} from "../tool-call-layout";
|
|
19
|
-
import type { CopilotChatToolCallDisplay } from "../tool-call-layout";
|
|
14
|
+
import type { CopilotChatToolCallDisplay } from "@copilotkit/core";
|
|
20
15
|
|
|
21
16
|
// Ported from React's CopilotChatMessageView.toolCallDisplay tests.
|
|
22
17
|
|
|
@@ -55,13 +50,6 @@ const weather = defineToolCallRenderer({
|
|
|
55
50
|
render: () => h("div", { "data-testid": "weather-card" }, "Weather"),
|
|
56
51
|
});
|
|
57
52
|
|
|
58
|
-
const names = {
|
|
59
|
-
named: new Set(["weather"]),
|
|
60
|
-
groupedNames: new Set<string>(),
|
|
61
|
-
wildcard: true,
|
|
62
|
-
wildcardGroups: true,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
53
|
interface ViewState {
|
|
66
54
|
messages: Ref<Message[]>;
|
|
67
55
|
isRunning: Ref<boolean>;
|
|
@@ -118,92 +106,6 @@ function renderView(
|
|
|
118
106
|
const firstButton = (element: HTMLElement) =>
|
|
119
107
|
within(element).getAllByRole("button")[0]!;
|
|
120
108
|
|
|
121
|
-
describe("getToolCallLayouts", () => {
|
|
122
|
-
it("groups calls made in a row across messages, owned by the first", () => {
|
|
123
|
-
const messages = [
|
|
124
|
-
question,
|
|
125
|
-
callMessage("a"),
|
|
126
|
-
result("a"),
|
|
127
|
-
callMessage("b"),
|
|
128
|
-
result("b"),
|
|
129
|
-
];
|
|
130
|
-
const layouts = getToolCallLayouts(messages, messages, names);
|
|
131
|
-
|
|
132
|
-
const first = layouts.get("m-a")!.segments;
|
|
133
|
-
expect(first).toHaveLength(1);
|
|
134
|
-
expect(first[0]).toMatchObject({ kind: "group", trailing: true });
|
|
135
|
-
expect(
|
|
136
|
-
first[0]!.kind === "group" && first[0]!.calls.map((c) => c.toolCall.id),
|
|
137
|
-
).toEqual(["a", "b"]);
|
|
138
|
-
expect(layouts.get("m-b")!.segments).toEqual([]);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it("starts a new group after text or a call with its own renderer", () => {
|
|
142
|
-
const messages = [
|
|
143
|
-
question,
|
|
144
|
-
callMessage("a"),
|
|
145
|
-
say("t", "Checking more."),
|
|
146
|
-
callMessage("b"),
|
|
147
|
-
callMessage("w", "weather"),
|
|
148
|
-
callMessage("c"),
|
|
149
|
-
];
|
|
150
|
-
const layouts = getToolCallLayouts(messages, messages, names);
|
|
151
|
-
const kinds = ["m-a", "m-b", "m-w", "m-c"].map((id) =>
|
|
152
|
-
layouts.get(id)!.segments.map((segment) => segment.kind),
|
|
153
|
-
);
|
|
154
|
-
expect(kinds).toEqual([["group"], ["group"], ["call"], ["group"]]);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("changes its signature when a result arrives for a call it holds", () => {
|
|
158
|
-
const before = [question, callMessage("a"), callMessage("b")];
|
|
159
|
-
const after = [...before, result("b")];
|
|
160
|
-
const signature = (messages: Message[]) =>
|
|
161
|
-
getToolCallLayouts(messages, messages, names).get("m-a")!.signature;
|
|
162
|
-
expect(signature(after)).not.toBe(signature(before));
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
describe("getTurnWork", () => {
|
|
167
|
-
it("treats everything up to the last call as work, and the rest as the reply", () => {
|
|
168
|
-
const messages = [
|
|
169
|
-
question,
|
|
170
|
-
say("t1", "Looking."),
|
|
171
|
-
callMessage("a"),
|
|
172
|
-
result("a"),
|
|
173
|
-
say("t2", "The answer."),
|
|
174
|
-
];
|
|
175
|
-
const layouts = getToolCallLayouts(messages, messages, names);
|
|
176
|
-
const turn = getTurnWork(messages, layouts).get("u1")!;
|
|
177
|
-
expect([...turn.workMessageIds]).toEqual(["t1", "m-a"]);
|
|
178
|
-
expect(turn.firstMessageId).toBe("t1");
|
|
179
|
-
expect(turn.calls).toHaveLength(1);
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
describe("getToolCallRendererNames", () => {
|
|
184
|
-
const renderers = [
|
|
185
|
-
{ name: "*", group: true },
|
|
186
|
-
{ name: "search", group: true },
|
|
187
|
-
];
|
|
188
|
-
|
|
189
|
-
it("reads the renderers' group opt-in", () => {
|
|
190
|
-
const result = getToolCallRendererNames(renderers);
|
|
191
|
-
expect([...result.groupedNames]).toEqual(["search"]);
|
|
192
|
-
expect(result.wildcardGroups).toBe(true);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it("never groups calls a tool-call slot draws", () => {
|
|
196
|
-
const named = getToolCallRendererNames(renderers, ["tool-call-search"]);
|
|
197
|
-
expect(named.named.has("search")).toBe(true);
|
|
198
|
-
expect(named.groupedNames.has("search")).toBe(false);
|
|
199
|
-
expect(named.wildcardGroups).toBe(true);
|
|
200
|
-
|
|
201
|
-
const wildcard = getToolCallRendererNames(renderers, ["tool-call"]);
|
|
202
|
-
expect(wildcard.groupedNames.size).toBe(0);
|
|
203
|
-
expect(wildcard.wildcardGroups).toBe(false);
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
109
|
describe("CopilotChatMessageView toolCallDisplay", () => {
|
|
208
110
|
const burst = [
|
|
209
111
|
question,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Message, ToolCall, ToolMessage } from "@ag-ui/core";
|
|
2
2
|
import { contentToText } from "@ag-ui/core";
|
|
3
3
|
import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
|
|
4
|
-
import { ToolCallStatus } from "@copilotkit/core";
|
|
4
|
+
import { ToolCallStatus, ɵresolveToolCallRenderer } from "@copilotkit/core";
|
|
5
5
|
import { partialJSONParse } from "@copilotkit/shared";
|
|
6
6
|
import type { CopilotChatToolCallRenderSlotProps } from "./types";
|
|
7
7
|
import type {
|
|
@@ -24,23 +24,6 @@ export function getToolCallSlotName(toolName: string): `tool-call-${string}` {
|
|
|
24
24
|
return `tool-call-${toolName}`;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function getCoreRenderConfig(
|
|
28
|
-
toolCall: ToolCall,
|
|
29
|
-
renderToolCalls: readonly VueToolCallRenderer<unknown>[],
|
|
30
|
-
agentId: string,
|
|
31
|
-
): VueToolCallRenderer<unknown> | undefined {
|
|
32
|
-
const exactMatches = renderToolCalls.filter(
|
|
33
|
-
(renderConfig) => renderConfig.name === toolCall.function.name,
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
exactMatches.find((renderConfig) => renderConfig.agentId === agentId) ??
|
|
38
|
-
exactMatches.find((renderConfig) => !renderConfig.agentId) ??
|
|
39
|
-
exactMatches[0] ??
|
|
40
|
-
renderToolCalls.find((renderConfig) => renderConfig.name === "*")
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
27
|
export function getToolCallRenderProps(
|
|
45
28
|
toolCall: ToolCall,
|
|
46
29
|
messages: Message[],
|
|
@@ -122,6 +105,7 @@ export function getToolCallMemoDeps(
|
|
|
122
105
|
toolCall.function.arguments,
|
|
123
106
|
findToolMessage(messages, toolCall.id)?.content,
|
|
124
107
|
executingToolCallIds.has(toolCall.id),
|
|
125
|
-
|
|
108
|
+
ɵresolveToolCallRenderer(renderToolCalls, toolCall.function.name, agentId)
|
|
109
|
+
?.render,
|
|
126
110
|
];
|
|
127
111
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export * from "./types";
|
|
2
|
-
export { default as BasicMarkdown } from "./BasicMarkdown.vue";
|
|
3
2
|
export { default as StreamingMarkdownDefault } from "./StreamingMarkdownDefault.vue";
|
|
4
3
|
export { default as CopilotChatAssistantMessage } from "./CopilotChatAssistantMessage.vue";
|
|
5
4
|
export { default as CopilotChatAudioRecorder } from "./CopilotChatAudioRecorder.vue";
|
|
@@ -1,34 +1,13 @@
|
|
|
1
1
|
import type { ComputedRef, InjectionKey } from "vue";
|
|
2
|
-
import type {
|
|
3
|
-
CopilotChatToolCallDisplay,
|
|
4
|
-
ToolCallLayout,
|
|
5
|
-
} from "./tool-call-layout";
|
|
2
|
+
import type { ɵToolCallDisplayContextValue } from "@copilotkit/core";
|
|
6
3
|
|
|
7
4
|
/**
|
|
8
5
|
* Provided by the message view so each message's tool calls follow the view's
|
|
9
6
|
* `toolCallDisplay` without every layer passing it along.
|
|
10
7
|
* @internal
|
|
11
8
|
*/
|
|
12
|
-
export interface ToolCallDisplayContextValue {
|
|
13
|
-
display: CopilotChatToolCallDisplay;
|
|
14
|
-
isRunning: boolean;
|
|
15
|
-
/** Each assistant message's tool calls, laid out across the whole list. */
|
|
16
|
-
layouts: ReadonlyMap<string, ToolCallLayout>;
|
|
17
|
-
/**
|
|
18
|
-
* `"turn"` display: for each message in a reply's working phase, whether
|
|
19
|
-
* that work is showing. Messages not in here are the reply itself.
|
|
20
|
-
*/
|
|
21
|
-
workOpen: ReadonlyMap<string, boolean>;
|
|
22
|
-
/**
|
|
23
|
-
* Like `workOpen`, for the messages' text: text that ends the reply stays
|
|
24
|
-
* out of it even when its message also calls a tool.
|
|
25
|
-
*/
|
|
26
|
-
workTextOpen: ReadonlyMap<string, boolean>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** @internal */
|
|
30
9
|
export const ToolCallDisplayContextKey: InjectionKey<
|
|
31
|
-
ComputedRef
|
|
10
|
+
ComputedRef<ɵToolCallDisplayContextValue>
|
|
32
11
|
> = Symbol("CopilotChatToolCallDisplay");
|
|
33
12
|
|
|
34
13
|
/**
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { defineComponent, h, onMounted, ref, watch } from "vue";
|
|
2
2
|
import type { PropType } from "vue";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ɵcountFailedToolCalls,
|
|
5
|
+
ɵformatLabel,
|
|
6
|
+
ɵgetArgumentPreview,
|
|
7
|
+
ɵstringifyToolCallPayload,
|
|
8
|
+
} from "@copilotkit/core";
|
|
9
|
+
import type {
|
|
10
|
+
CopilotChatLabels,
|
|
11
|
+
ToolCallEntry,
|
|
12
|
+
ɵToolCallRowStatus,
|
|
13
|
+
} from "@copilotkit/core";
|
|
4
14
|
import {
|
|
5
15
|
IconCheck,
|
|
6
16
|
IconChevronRight,
|
|
@@ -9,83 +19,25 @@ import {
|
|
|
9
19
|
IconX,
|
|
10
20
|
} from "../icons";
|
|
11
21
|
import { useCopilotId } from "../ui/use-id";
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
/** A call that never got a result after the run ended (Stop, an error) is "stopped". */
|
|
15
|
-
export type ToolCallStatusName = "running" | "done" | "failed" | "stopped";
|
|
22
|
+
import { useChatLabels } from "./use-chat-labels";
|
|
16
23
|
|
|
17
24
|
/**
|
|
18
|
-
*
|
|
19
|
-
* the run has moved on or ended, its result is never coming.
|
|
25
|
+
* The label for a tool call's status ("Running", "Done", ...).
|
|
20
26
|
* @internal
|
|
21
27
|
*/
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
export const statusLabels: Record<ToolCallStatusName, string> = {
|
|
36
|
-
running: "Running",
|
|
37
|
-
done: "Done",
|
|
38
|
-
failed: "Failed",
|
|
39
|
-
stopped: "Stopped",
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/** The first non-empty string argument: usually a command, path or query. */
|
|
43
|
-
export function argumentPreview(parameters: unknown): string | undefined {
|
|
44
|
-
if (!parameters || typeof parameters !== "object") return undefined;
|
|
45
|
-
const value = Object.values(parameters).find(
|
|
46
|
-
(v): v is string => typeof v === "string" && v.trim().length > 0,
|
|
47
|
-
);
|
|
48
|
-
return value?.replace(/\s+/g, " ");
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** @internal */
|
|
52
|
-
export const entryPreview = (entry: ToolCallEntry) =>
|
|
53
|
-
argumentPreview(partialJSONParse(entry.toolCall.function.arguments));
|
|
54
|
-
|
|
55
|
-
/** "run_command ×2, read_file" */
|
|
56
|
-
export function toolNames(calls: ToolCallEntry[]): string {
|
|
57
|
-
const counts = new Map<string, number>();
|
|
58
|
-
for (const { toolCall } of calls) {
|
|
59
|
-
const name = toolCall.function.name;
|
|
60
|
-
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
61
|
-
}
|
|
62
|
-
return [...counts]
|
|
63
|
-
.map(([name, count]) => (count > 1 ? `${name} ×${count}` : name))
|
|
64
|
-
.join(", ");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** @internal */
|
|
68
|
-
export const pluralize = (count: number, noun: string) =>
|
|
69
|
-
`${count} ${noun}${count === 1 ? "" : "s"}`;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Guarded JSON.stringify for the payload blocks. A circular reference would
|
|
73
|
-
* otherwise crash the render.
|
|
74
|
-
*/
|
|
75
|
-
function stringifyPayload(value: unknown): string {
|
|
76
|
-
if (typeof value === "string") return value;
|
|
77
|
-
try {
|
|
78
|
-
return JSON.stringify(value, null, 2);
|
|
79
|
-
} catch (err) {
|
|
80
|
-
console.warn(
|
|
81
|
-
"[CopilotKit] Failed to JSON.stringify tool-call payload; falling back to String():",
|
|
82
|
-
err,
|
|
83
|
-
);
|
|
84
|
-
try {
|
|
85
|
-
return String(value);
|
|
86
|
-
} catch {
|
|
87
|
-
return "[unserializable]";
|
|
88
|
-
}
|
|
28
|
+
export function toolCallStatusLabel(
|
|
29
|
+
labels: Readonly<CopilotChatLabels>,
|
|
30
|
+
status: ɵToolCallRowStatus,
|
|
31
|
+
) {
|
|
32
|
+
switch (status) {
|
|
33
|
+
case "running":
|
|
34
|
+
return labels.toolCallStatusRunningLabel;
|
|
35
|
+
case "done":
|
|
36
|
+
return labels.toolCallStatusDoneLabel;
|
|
37
|
+
case "failed":
|
|
38
|
+
return labels.toolCallStatusFailedLabel;
|
|
39
|
+
case "stopped":
|
|
40
|
+
return labels.toolCallStatusStoppedLabel;
|
|
89
41
|
}
|
|
90
42
|
}
|
|
91
43
|
|
|
@@ -125,7 +77,7 @@ export const ToolCallStatusIcon = defineComponent({
|
|
|
125
77
|
name: "ToolCallStatusIcon",
|
|
126
78
|
props: {
|
|
127
79
|
status: {
|
|
128
|
-
type: String as PropType
|
|
80
|
+
type: String as PropType<ɵToolCallRowStatus>,
|
|
129
81
|
required: true,
|
|
130
82
|
},
|
|
131
83
|
},
|
|
@@ -173,15 +125,14 @@ export const ToolCallFailedCount = defineComponent({
|
|
|
173
125
|
calls: { type: Array as PropType<ToolCallEntry[]>, required: true },
|
|
174
126
|
},
|
|
175
127
|
setup(props) {
|
|
128
|
+
const labels = useChatLabels();
|
|
176
129
|
return () => {
|
|
177
|
-
const failed = props.calls
|
|
178
|
-
(call) => call.toolMessage?.error,
|
|
179
|
-
).length;
|
|
130
|
+
const failed = ɵcountFailedToolCalls(props.calls);
|
|
180
131
|
if (!failed) return null;
|
|
181
132
|
return h(
|
|
182
133
|
"span",
|
|
183
134
|
{ class: "cpk-tool-call-failed-count" },
|
|
184
|
-
|
|
135
|
+
ɵformatLabel(labels.value.toolCallFailedCountLabel, { count: failed }),
|
|
185
136
|
);
|
|
186
137
|
};
|
|
187
138
|
},
|
|
@@ -190,7 +141,11 @@ export const ToolCallFailedCount = defineComponent({
|
|
|
190
141
|
function payload(label: string, value: unknown) {
|
|
191
142
|
return h("div", [
|
|
192
143
|
h("div", { class: "cpk-tool-call-payload-label" }, label),
|
|
193
|
-
h(
|
|
144
|
+
h(
|
|
145
|
+
"pre",
|
|
146
|
+
{ class: "cpk-tool-call-payload-code" },
|
|
147
|
+
ɵstringifyToolCallPayload(value),
|
|
148
|
+
),
|
|
194
149
|
]);
|
|
195
150
|
}
|
|
196
151
|
|
|
@@ -206,7 +161,7 @@ export const ToolCallRow = defineComponent({
|
|
|
206
161
|
parameters: { type: null, default: undefined },
|
|
207
162
|
result: { type: null, default: undefined },
|
|
208
163
|
status: {
|
|
209
|
-
type: String as PropType
|
|
164
|
+
type: String as PropType<ɵToolCallRowStatus>,
|
|
210
165
|
required: true,
|
|
211
166
|
},
|
|
212
167
|
appearance: {
|
|
@@ -217,8 +172,9 @@ export const ToolCallRow = defineComponent({
|
|
|
217
172
|
setup(props) {
|
|
218
173
|
const open = ref(false);
|
|
219
174
|
const detailsId = useCopilotId("cpk-tool-call-row");
|
|
175
|
+
const labels = useChatLabels();
|
|
220
176
|
return () => {
|
|
221
|
-
const preview =
|
|
177
|
+
const preview = ɵgetArgumentPreview(props.parameters);
|
|
222
178
|
// Rows draw their own divider: a wrapper with `display: contents`
|
|
223
179
|
// around each call would escape a parent's divide-y.
|
|
224
180
|
return h(
|
|
@@ -261,7 +217,7 @@ export const ToolCallRow = defineComponent({
|
|
|
261
217
|
"data-testid": "copilot-tool-render-status",
|
|
262
218
|
class: "cpk-tool-call-row-status",
|
|
263
219
|
},
|
|
264
|
-
|
|
220
|
+
toolCallStatusLabel(labels.value, props.status),
|
|
265
221
|
),
|
|
266
222
|
h(IconChevronRight, {
|
|
267
223
|
"aria-hidden": "true",
|
|
@@ -278,9 +234,12 @@ export const ToolCallRow = defineComponent({
|
|
|
278
234
|
"div",
|
|
279
235
|
{ class: "cpk-tool-call-payloads cpk-tool-call-row-payloads" },
|
|
280
236
|
[
|
|
281
|
-
payload(
|
|
237
|
+
payload(
|
|
238
|
+
labels.value.toolCallArgumentsLabel,
|
|
239
|
+
props.parameters ?? {},
|
|
240
|
+
),
|
|
282
241
|
props.result !== undefined
|
|
283
|
-
? payload(
|
|
242
|
+
? payload(labels.value.toolCallResultLabel, props.result)
|
|
284
243
|
: null,
|
|
285
244
|
],
|
|
286
245
|
),
|
|
@@ -3,15 +3,6 @@ import type { FunctionalComponent } from "vue";
|
|
|
3
3
|
import { CopilotChatCollapsible } from "./tool-call-group-parts";
|
|
4
4
|
import { ToolCallDisplayContextKey } from "./tool-call-display-context";
|
|
5
5
|
|
|
6
|
-
/** "45s", "1m 5s", "2m". Used for the live timer and the final duration. */
|
|
7
|
-
export function formatDuration(ms: number): string {
|
|
8
|
-
const seconds = Math.max(0, Math.floor(ms / 1000));
|
|
9
|
-
if (seconds < 60) return `${seconds}s`;
|
|
10
|
-
const minutes = Math.floor(seconds / 60);
|
|
11
|
-
const rest = seconds % 60;
|
|
12
|
-
return rest ? `${minutes}m ${rest}s` : `${minutes}m`;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
6
|
/**
|
|
16
7
|
* Folds its content away with a reply's work in the `"turn"` display. Without
|
|
17
8
|
* `open` (any other display) it renders the content as is.
|
|
@@ -15,13 +15,13 @@ import type {
|
|
|
15
15
|
AttachmentModality,
|
|
16
16
|
InputContentSource,
|
|
17
17
|
} from "@copilotkit/shared";
|
|
18
|
-
import type { CopilotChatLabels } from "../../providers/types";
|
|
19
|
-
import type { InterruptEvent } from "../../types";
|
|
20
|
-
import type { AutoScrollMode } from "./normalize-auto-scroll";
|
|
21
18
|
import type {
|
|
19
|
+
CopilotChatLabels,
|
|
22
20
|
CopilotChatToolCallDisplay,
|
|
23
21
|
ToolCallEntry,
|
|
24
|
-
} from "
|
|
22
|
+
} from "@copilotkit/core";
|
|
23
|
+
import type { InterruptEvent } from "../../types";
|
|
24
|
+
import type { AutoScrollMode } from "./normalize-auto-scroll";
|
|
25
25
|
|
|
26
26
|
export type CopilotChatInputMode = "input" | "transcribe" | "processing";
|
|
27
27
|
export type { Attachment, AttachmentsConfig, AttachmentModality };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { CopilotChatDefaultLabels } from "@copilotkit/core";
|
|
3
|
+
import type { CopilotChatLabels } from "@copilotkit/core";
|
|
4
|
+
import { useCopilotChatConfiguration } from "../../providers/useCopilotChatConfiguration";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The chat labels in effect: the surrounding chat configuration's, or the
|
|
8
|
+
* default English labels outside a chat.
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export function useChatLabels() {
|
|
12
|
+
const config = useCopilotChatConfiguration();
|
|
13
|
+
return computed<Readonly<CopilotChatLabels>>(
|
|
14
|
+
() => config.value?.labels ?? CopilotChatDefaultLabels,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -1,86 +1,90 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { h } from "vue";
|
|
2
3
|
import { mount } from "@vue/test-utils";
|
|
3
|
-
import {
|
|
4
|
-
IconArrowUp,
|
|
5
|
-
IconCheck,
|
|
6
|
-
IconCheckCircle,
|
|
7
|
-
IconChevronDown,
|
|
8
|
-
IconChevronLeft,
|
|
9
|
-
IconChevronRight,
|
|
10
|
-
IconCircle,
|
|
11
|
-
IconCopy,
|
|
12
|
-
IconEdit,
|
|
13
|
-
IconLoader2,
|
|
14
|
-
IconMessageCircle,
|
|
15
|
-
IconMic,
|
|
16
|
-
IconPlus,
|
|
17
|
-
IconRefreshCw,
|
|
18
|
-
IconSquare,
|
|
19
|
-
IconThumbsDown,
|
|
20
|
-
IconThumbsUp,
|
|
21
|
-
IconVolume2,
|
|
22
|
-
IconX,
|
|
23
|
-
} from "../index";
|
|
4
|
+
import { CopilotIcon, IconCircle, IconPlay, IconX } from "../index";
|
|
24
5
|
|
|
25
|
-
describe("
|
|
26
|
-
it("
|
|
27
|
-
const
|
|
28
|
-
IconMessageCircle,
|
|
29
|
-
IconX,
|
|
30
|
-
IconChevronDown,
|
|
31
|
-
IconPlus,
|
|
32
|
-
IconMic,
|
|
33
|
-
IconArrowUp,
|
|
34
|
-
IconCheck,
|
|
35
|
-
IconSquare,
|
|
36
|
-
IconLoader2,
|
|
37
|
-
IconCopy,
|
|
38
|
-
IconEdit,
|
|
39
|
-
IconChevronLeft,
|
|
40
|
-
IconChevronRight,
|
|
41
|
-
IconThumbsUp,
|
|
42
|
-
IconThumbsDown,
|
|
43
|
-
IconVolume2,
|
|
44
|
-
IconRefreshCw,
|
|
45
|
-
IconCheckCircle,
|
|
46
|
-
IconCircle,
|
|
47
|
-
];
|
|
6
|
+
describe("CopilotIcon", () => {
|
|
7
|
+
it("draws the named icon with Lucide's classes and default attributes", () => {
|
|
8
|
+
const svg = mount(CopilotIcon, { props: { name: "X" } }).find("svg");
|
|
48
9
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
10
|
+
expect(svg.classes()).toEqual(["lucide", "lucide-x"]);
|
|
11
|
+
expect(svg.attributes()).toMatchObject({
|
|
12
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
13
|
+
width: "24",
|
|
14
|
+
height: "24",
|
|
15
|
+
viewBox: "0 0 24 24",
|
|
16
|
+
fill: "none",
|
|
17
|
+
stroke: "currentColor",
|
|
18
|
+
"stroke-width": "2",
|
|
19
|
+
"stroke-linecap": "round",
|
|
20
|
+
"stroke-linejoin": "round",
|
|
21
|
+
});
|
|
22
|
+
expect(svg.findAll("path").map((path) => path.attributes("d"))).toEqual([
|
|
23
|
+
"M18 6 6 18",
|
|
24
|
+
"m6 6 12 12",
|
|
25
|
+
]);
|
|
52
26
|
});
|
|
53
27
|
|
|
54
|
-
it("
|
|
55
|
-
const
|
|
56
|
-
const
|
|
28
|
+
it("draws circle and polygon nodes", () => {
|
|
29
|
+
const circle = mount(IconCircle).find("svg").find("circle");
|
|
30
|
+
const polygon = mount(IconPlay).find("svg").find("polygon");
|
|
57
31
|
|
|
58
|
-
expect(
|
|
59
|
-
expect(
|
|
32
|
+
expect(circle.attributes()).toMatchObject({ cx: "12", cy: "12", r: "10" });
|
|
33
|
+
expect(polygon.attributes("points")).toBe("6 3 20 12 6 21 6 3");
|
|
60
34
|
});
|
|
61
35
|
|
|
62
|
-
it("
|
|
63
|
-
const
|
|
36
|
+
it("applies size, color and stroke-width", () => {
|
|
37
|
+
const svg = mount(IconX, {
|
|
38
|
+
props: { size: 18, color: "red", strokeWidth: 1.75 },
|
|
39
|
+
}).find("svg");
|
|
40
|
+
|
|
41
|
+
expect(svg.attributes()).toMatchObject({
|
|
42
|
+
width: "18",
|
|
43
|
+
height: "18",
|
|
44
|
+
stroke: "red",
|
|
45
|
+
"stroke-width": "1.75",
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("passes other attributes to the svg after the Lucide classes", () => {
|
|
50
|
+
const svg = mount(IconX, {
|
|
64
51
|
attrs: {
|
|
65
52
|
class: "icon-class",
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"stroke-width": "1.5",
|
|
69
|
-
role: "img",
|
|
70
|
-
"aria-hidden": "true",
|
|
53
|
+
fill: "currentColor",
|
|
54
|
+
"data-testid": "close-icon",
|
|
71
55
|
style: "color: rgb(255, 0, 0);",
|
|
72
56
|
},
|
|
73
|
-
});
|
|
57
|
+
}).find("svg");
|
|
58
|
+
|
|
59
|
+
expect(svg.classes()).toEqual(["lucide", "lucide-x", "icon-class"]);
|
|
60
|
+
expect(svg.attributes("fill")).toBe("currentColor");
|
|
61
|
+
expect(svg.attributes("data-testid")).toBe("close-icon");
|
|
62
|
+
expect(svg.attributes("style")).toContain("color: rgb(255, 0, 0)");
|
|
63
|
+
});
|
|
74
64
|
|
|
75
|
-
|
|
65
|
+
it("hides an unnamed icon from screen readers", () => {
|
|
66
|
+
const svg = mount(IconX).find("svg");
|
|
76
67
|
|
|
77
|
-
expect(svg.exists()).toBe(true);
|
|
78
|
-
expect(svg.classes()).toContain("icon-class");
|
|
79
|
-
expect(svg.attributes("width")).toBe("18");
|
|
80
|
-
expect(svg.attributes("height")).toBe("20");
|
|
81
|
-
expect(svg.attributes("stroke-width")).toBe("1.5");
|
|
82
|
-
expect(svg.attributes("role")).toBe("img");
|
|
83
68
|
expect(svg.attributes("aria-hidden")).toBe("true");
|
|
84
|
-
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("keeps a named icon visible to screen readers", () => {
|
|
72
|
+
const labelled = mount(IconX, { attrs: { "aria-label": "Close" } }).find(
|
|
73
|
+
"svg",
|
|
74
|
+
);
|
|
75
|
+
const withRole = mount(IconX, { attrs: { role: "img" } }).find("svg");
|
|
76
|
+
|
|
77
|
+
expect(labelled.attributes("aria-hidden")).toBeUndefined();
|
|
78
|
+
expect(labelled.attributes("aria-label")).toBe("Close");
|
|
79
|
+
expect(withRole.attributes("aria-hidden")).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("keeps an icon with slot content visible to screen readers", () => {
|
|
83
|
+
const svg = mount(IconX, {
|
|
84
|
+
slots: { default: () => h("title", "Close") },
|
|
85
|
+
}).find("svg");
|
|
86
|
+
|
|
87
|
+
expect(svg.attributes("aria-hidden")).toBeUndefined();
|
|
88
|
+
expect(svg.find("title").text()).toBe("Close");
|
|
85
89
|
});
|
|
86
90
|
});
|