@copilotkit/vue 2.0.0-rc.0 → 2.0.0-rc.2
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 +13 -12
- package/README.md +1 -2
- package/dist/components/copilot-provider/CopilotKit.vue.d.ts +3 -1
- package/dist/components/copilot-provider/CopilotKit.vue.d.ts.map +1 -1
- package/dist/index.cjs +43 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +10170 -7506
- package/dist/index.mjs.map +1 -1
- package/dist/v2/components/A2UIMessageRenderer.d.ts +7 -0
- package/dist/v2/components/A2UIMessageRenderer.d.ts.map +1 -1
- package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts.map +1 -1
- package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts +11 -2
- package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts.map +1 -1
- package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts +136 -0
- package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts.map +1 -0
- package/dist/v2/components/chat/BasicMarkdown.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChat.vue.d.ts +1 -0
- package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts +3 -1
- package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatAssistantToolbarButton.vue.d.ts +16 -0
- package/dist/v2/components/chat/CopilotChatAssistantToolbarButton.vue.d.ts.map +1 -0
- 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/CopilotChatAudioRecorder.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts +22 -0
- package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +69 -16
- package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatInspectorButton.vue.d.ts +12 -0
- package/dist/v2/components/chat/CopilotChatInspectorButton.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatLightbox.vue.d.ts +22 -0
- package/dist/v2/components/chat/CopilotChatLightbox.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts +20 -0
- 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/CopilotChatSuggestionPill.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatSuggestionView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToggleButton.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts +32 -0
- package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts +11 -1
- package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatUserMessage.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatView.vue.d.ts +1 -0
- package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts +5 -0
- package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotModalHeader.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotModalHeaderCloseButton.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotModalHeaderTitle.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotPopup.vue.d.ts +1 -0
- package/dist/v2/components/chat/CopilotPopup.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotPopupView.vue.d.ts +1 -0
- package/dist/v2/components/chat/CopilotPopupView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts +1 -0
- package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotPopupWelcomeScreen.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotSidebar.vue.d.ts +2 -0
- package/dist/v2/components/chat/CopilotSidebar.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts +2 -0
- package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts +2 -0
- package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotSidebarWelcomeScreen.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/StreamingMarkdownDefault.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/index.d.ts +17 -0
- package/dist/v2/components/chat/index.d.ts.map +1 -1
- package/dist/v2/components/chat/inspector-shortcuts.d.ts +12 -0
- package/dist/v2/components/chat/inspector-shortcuts.d.ts.map +1 -0
- package/dist/v2/components/chat/message-feedback.d.ts +89 -0
- package/dist/v2/components/chat/message-feedback.d.ts.map +1 -0
- package/dist/v2/components/chat/tool-call-capture.d.ts +39 -0
- package/dist/v2/components/chat/tool-call-capture.d.ts.map +1 -0
- package/dist/v2/components/chat/tool-call-display-context.d.ts +39 -0
- package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -0
- package/dist/v2/components/chat/tool-call-group-parts.d.ts +144 -0
- package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -0
- package/dist/v2/components/chat/tool-call-layout.d.ts +115 -0
- package/dist/v2/components/chat/tool-call-layout.d.ts.map +1 -0
- package/dist/v2/components/chat/turn-work.d.ts +35 -0
- package/dist/v2/components/chat/turn-work.d.ts.map +1 -0
- package/dist/v2/components/chat/types.d.ts +104 -4
- package/dist/v2/components/chat/types.d.ts.map +1 -1
- package/dist/v2/components/chat/use-lightbox.d.ts +18 -0
- package/dist/v2/components/chat/use-lightbox.d.ts.map +1 -0
- package/dist/v2/components/icons/index.d.ts +1 -1
- package/dist/v2/components/icons/index.d.ts.map +1 -1
- package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts +55 -0
- package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts.map +1 -0
- package/dist/v2/components/ui/CopilotPopover.vue.d.ts +40 -0
- package/dist/v2/components/ui/CopilotPopover.vue.d.ts.map +1 -0
- package/dist/v2/components/ui/CopilotTooltip.vue.d.ts +37 -0
- package/dist/v2/components/ui/CopilotTooltip.vue.d.ts.map +1 -0
- package/dist/v2/components/ui/dropdown-menu.d.ts +14 -0
- package/dist/v2/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/v2/components/ui/floating.d.ts +84 -0
- package/dist/v2/components/ui/floating.d.ts.map +1 -0
- package/dist/v2/components/ui/presence.d.ts +9 -0
- package/dist/v2/components/ui/presence.d.ts.map +1 -0
- package/dist/v2/components/ui/use-id.d.ts +9 -0
- package/dist/v2/components/ui/use-id.d.ts.map +1 -0
- package/dist/v2/hooks/use-default-render-tool.d.ts +6 -0
- package/dist/v2/hooks/use-default-render-tool.d.ts.map +1 -1
- package/dist/v2/hooks/use-render-tool.d.ts +4 -0
- package/dist/v2/hooks/use-render-tool.d.ts.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.d.cts +3 -0
- package/dist/v2/index.d.mts +3 -0
- package/dist/v2/index.d.ts +3 -0
- package/dist/v2/index.d.ts.map +1 -1
- package/dist/v2/index.mjs +165 -162
- package/dist/v2/lib/message-feedback.d.ts +33 -0
- package/dist/v2/lib/message-feedback.d.ts.map +1 -0
- package/dist/v2/providers/CopilotChatConfigurationProvider.vue.d.ts.map +1 -1
- package/dist/v2/providers/CopilotKitProvider.types.d.ts +56 -1
- package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts +5 -1
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
- package/dist/v2/providers/index.d.ts +1 -1
- package/dist/v2/providers/index.d.ts.map +1 -1
- package/dist/v2/providers/keys.d.ts +8 -0
- package/dist/v2/providers/keys.d.ts.map +1 -1
- package/dist/v2/providers/types.d.ts +9 -0
- package/dist/v2/providers/types.d.ts.map +1 -1
- package/dist/v2/styles/CopilotKitStyles.d.ts +18 -0
- package/dist/v2/styles/CopilotKitStyles.d.ts.map +1 -0
- package/dist/v2/types/defineToolCallRenderer.d.ts +2 -0
- package/dist/v2/types/defineToolCallRenderer.d.ts.map +1 -1
- package/dist/v2/types/vue-tool-call-renderer.d.ts +7 -0
- package/dist/v2/types/vue-tool-call-renderer.d.ts.map +1 -1
- package/package.json +11 -23
- package/src/components/copilot-provider/CopilotKit.vue +4 -1
- package/src/v2/__tests__/exports.test.ts +4 -2
- package/src/v2/components/A2UIMessageRenderer.ts +55 -67
- package/src/v2/components/A2UISurfaceActivityRenderer.vue +11 -23
- package/src/v2/components/__tests__/A2UIBasicCatalogColors.test.ts +8 -9
- package/src/v2/components/__tests__/A2UIMessageRenderer.test.ts +63 -36
- package/src/v2/components/a2ui/A2UIBuiltInToolCallRenderer.ts +14 -257
- package/src/v2/components/a2ui/A2UIRecoveryStates.ts +444 -0
- package/src/v2/components/a2ui/__tests__/A2UIRecoveryStates.test.ts +175 -0
- package/src/v2/components/chat/BasicMarkdown.vue +8 -6
- package/src/v2/components/chat/CopilotChat.vue +7 -0
- package/src/v2/components/chat/CopilotChatAssistantMessage.vue +267 -67
- package/src/v2/components/chat/CopilotChatAssistantToolbarButton.vue +46 -0
- package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +141 -168
- package/src/v2/components/chat/CopilotChatAttachmentRenderer.vue +67 -18
- package/src/v2/components/chat/CopilotChatAudioRecorder.vue +2 -2
- package/src/v2/components/chat/CopilotChatFeedbackCommentPopover.vue +240 -0
- package/src/v2/components/chat/CopilotChatInput.vue +210 -161
- package/src/v2/components/chat/CopilotChatInspectorButton.vue +331 -0
- package/src/v2/components/chat/CopilotChatLightbox.vue +105 -0
- package/src/v2/components/chat/CopilotChatMessageView.vue +261 -18
- package/src/v2/components/chat/CopilotChatReasoningMessage.vue +21 -22
- package/src/v2/components/chat/CopilotChatSuggestionPill.vue +5 -14
- package/src/v2/components/chat/CopilotChatSuggestionView.vue +1 -4
- package/src/v2/components/chat/CopilotChatToggleButton.vue +3 -9
- package/src/v2/components/chat/CopilotChatToggleButtonCloseIcon.ts +1 -1
- package/src/v2/components/chat/CopilotChatToggleButtonOpenIcon.ts +1 -1
- package/src/v2/components/chat/CopilotChatToolCallGroup.vue +330 -0
- package/src/v2/components/chat/CopilotChatToolCallItem.vue +21 -11
- package/src/v2/components/chat/CopilotChatToolCallsView.vue +192 -21
- package/src/v2/components/chat/CopilotChatUserMessage.vue +105 -42
- package/src/v2/components/chat/CopilotChatView.vue +33 -38
- package/src/v2/components/chat/CopilotChatWorkSummary.vue +85 -0
- package/src/v2/components/chat/CopilotModalHeader.vue +10 -8
- package/src/v2/components/chat/CopilotModalHeaderCloseButton.ts +7 -6
- package/src/v2/components/chat/CopilotModalHeaderTitle.ts +1 -4
- package/src/v2/components/chat/CopilotPopup.vue +3 -0
- package/src/v2/components/chat/CopilotPopupView.vue +1 -0
- package/src/v2/components/chat/CopilotPopupViewInternal.vue +9 -19
- package/src/v2/components/chat/CopilotPopupWelcomeScreen.vue +5 -8
- package/src/v2/components/chat/CopilotSidebar.vue +5 -0
- package/src/v2/components/chat/CopilotSidebarView.vue +2 -0
- package/src/v2/components/chat/CopilotSidebarViewInternal.vue +24 -17
- package/src/v2/components/chat/CopilotSidebarWelcomeScreen.vue +7 -10
- package/src/v2/components/chat/StreamingMarkdownDefault.vue +3 -5
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.inspector.test.ts +269 -0
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.learningFeedback.test.ts +760 -0
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.slots.e2e.test.ts +3 -5
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.test.ts +2 -2
- package/src/v2/components/chat/__tests__/CopilotChatInput.addMenu.test.ts +328 -0
- package/src/v2/components/chat/__tests__/CopilotChatInput.slots.e2e.test.ts +6 -3
- package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +22 -13
- package/src/v2/components/chat/__tests__/CopilotChatLightbox.test.ts +160 -0
- package/src/v2/components/chat/__tests__/CopilotChatMessageView.toolCallDisplay.test.ts +735 -0
- package/src/v2/components/chat/__tests__/CopilotChatSuggestionPill.test.ts +1 -1
- package/src/v2/components/chat/__tests__/CopilotChatSuggestionView.slots.e2e.test.ts +3 -1
- package/src/v2/components/chat/__tests__/CopilotChatToolCallsView.learning.test.ts +75 -0
- package/src/v2/components/chat/__tests__/CopilotChatUserMessage.attachments.test.ts +143 -0
- package/src/v2/components/chat/__tests__/CopilotChatView.inputOverlay.test.ts +5 -4
- package/src/v2/components/chat/__tests__/CopilotChatView.rootWidth.test.ts +7 -33
- package/src/v2/components/chat/__tests__/CopilotModalHeader.slots.e2e.test.ts +1 -1
- package/src/v2/components/chat/__tests__/CopilotPopupView.slots.e2e.test.ts +3 -3
- package/src/v2/components/chat/__tests__/CopilotSidebarView.position.test.ts +126 -0
- package/src/v2/components/chat/__tests__/messageFeedbackDefaults.test.ts +33 -0
- package/src/v2/components/chat/__tests__/modalChatDisplayProps.test.ts +44 -0
- package/src/v2/components/chat/index.ts +2 -0
- package/src/v2/components/chat/inspector-shortcuts.ts +34 -0
- package/src/v2/components/chat/message-feedback.ts +291 -0
- package/src/v2/components/chat/tool-call-capture.ts +55 -0
- package/src/v2/components/chat/tool-call-display-context.ts +49 -0
- package/src/v2/components/chat/tool-call-group-parts.ts +292 -0
- package/src/v2/components/chat/tool-call-layout.ts +304 -0
- package/src/v2/components/chat/turn-work.ts +61 -0
- package/src/v2/components/chat/types.ts +113 -3
- package/src/v2/components/chat/use-lightbox.ts +64 -0
- package/src/v2/components/icons/index.ts +4 -0
- package/src/v2/components/ui/CopilotDropdownMenuContent.vue +465 -0
- package/src/v2/components/ui/CopilotPopover.vue +138 -0
- package/src/v2/components/ui/CopilotTooltip.vue +237 -0
- package/src/v2/components/ui/__tests__/CopilotTooltip.test.ts +168 -0
- package/src/v2/components/ui/dropdown-menu.ts +15 -0
- package/src/v2/components/ui/floating.ts +321 -0
- package/src/v2/components/ui/presence.ts +54 -0
- package/src/v2/components/ui/use-id.ts +16 -0
- package/src/v2/hooks/use-default-render-tool.ts +123 -62
- package/src/v2/hooks/use-render-tool.ts +8 -0
- package/src/v2/index.ts +3 -0
- package/src/v2/lib/__tests__/message-feedback.test.ts +152 -0
- package/src/v2/lib/message-feedback.ts +120 -0
- package/src/v2/providers/CopilotChatConfigurationProvider.vue +29 -2
- package/src/v2/providers/CopilotKitProvider.types.ts +57 -0
- package/src/v2/providers/CopilotKitProvider.vue +125 -3
- package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.openThread.test.ts +69 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.learning.test.ts +534 -0
- package/src/v2/providers/index.ts +4 -1
- package/src/v2/providers/keys.ts +12 -0
- package/src/v2/providers/types.ts +11 -0
- package/src/v2/styles/CopilotKitStyles.ts +95 -0
- package/src/v2/styles/__tests__/CopilotKitStyles.client.test.ts +149 -0
- package/src/v2/styles/__tests__/CopilotKitStyles.ssr.test.ts +71 -0
- package/src/v2/types/defineToolCallRenderer.ts +4 -0
- package/src/v2/types/vue-tool-call-renderer.ts +7 -0
- package/vite.config.ts +2 -1
- package/dist/styles.css +0 -2
- package/scripts/scope-preflight.mjs +0 -100
- package/src/styles/globals.css +0 -233
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { CopilotKitCore } from "@copilotkit/core";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
getMessageFeedback,
|
|
5
|
+
subscribeToMessageFeedback,
|
|
6
|
+
MESSAGE_FEEDBACK_EVENT_NAME,
|
|
7
|
+
setMessageFeedback,
|
|
8
|
+
ɵresetMessageFeedbackCache,
|
|
9
|
+
} from "../message-feedback";
|
|
10
|
+
|
|
11
|
+
const emoji = "👍";
|
|
12
|
+
|
|
13
|
+
/** A Core that records what it emits, with its agent-text choice stubbed. */
|
|
14
|
+
function capturingCore({ agentText = true } = {}) {
|
|
15
|
+
const copilotkit = new CopilotKitCore({});
|
|
16
|
+
vi.spyOn(copilotkit, "ɵlearningCapturesAgentText", "get").mockReturnValue(
|
|
17
|
+
agentText,
|
|
18
|
+
);
|
|
19
|
+
const emit = vi
|
|
20
|
+
.spyOn(copilotkit, "emitTrajectoryEvent")
|
|
21
|
+
.mockImplementation(() => {});
|
|
22
|
+
return { copilotkit, emit };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const reply = { id: "m1", role: "assistant" as const, content: "Approved." };
|
|
26
|
+
|
|
27
|
+
const rate = (
|
|
28
|
+
copilotkit: CopilotKitCore,
|
|
29
|
+
feedback: { rating: "up" | "down"; comment?: string } | null,
|
|
30
|
+
message: { id: string; role: "assistant"; content: string } = reply,
|
|
31
|
+
) =>
|
|
32
|
+
setMessageFeedback({
|
|
33
|
+
copilotkit,
|
|
34
|
+
agentId: "agent",
|
|
35
|
+
threadId: "thread",
|
|
36
|
+
message,
|
|
37
|
+
feedback,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("message feedback store", () => {
|
|
41
|
+
beforeEach(() => ɵresetMessageFeedbackCache());
|
|
42
|
+
afterEach(() => vi.restoreAllMocks());
|
|
43
|
+
|
|
44
|
+
it("cuts long text without splitting a character", () => {
|
|
45
|
+
const { copilotkit, emit } = capturingCore();
|
|
46
|
+
|
|
47
|
+
rate(
|
|
48
|
+
copilotkit,
|
|
49
|
+
{ rating: "up", comment: emoji.repeat(1200) },
|
|
50
|
+
{ id: "m1", role: "assistant", content: emoji.repeat(600) },
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const [name, value] = emit.mock.calls[0]!;
|
|
54
|
+
expect(name).toBe(MESSAGE_FEEDBACK_EVENT_NAME);
|
|
55
|
+
expect(value).toMatchObject({
|
|
56
|
+
response: emoji.repeat(500),
|
|
57
|
+
comment: emoji.repeat(1000),
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("keeps the rating and sends each change, a removal as null", () => {
|
|
62
|
+
const { copilotkit, emit } = capturingCore();
|
|
63
|
+
|
|
64
|
+
rate(copilotkit, { rating: "up" });
|
|
65
|
+
expect(getMessageFeedback("agent", "thread", "m1")).toEqual({
|
|
66
|
+
rating: "up",
|
|
67
|
+
});
|
|
68
|
+
rate(copilotkit, null);
|
|
69
|
+
|
|
70
|
+
expect(getMessageFeedback("agent", "thread", "m1")).toBeUndefined();
|
|
71
|
+
expect(emit.mock.calls.map(([, value]) => value)).toEqual([
|
|
72
|
+
expect.objectContaining({ feedbackId: "thread:m1", rating: "up" }),
|
|
73
|
+
expect.objectContaining({ feedbackId: "thread:m1", rating: null }),
|
|
74
|
+
]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("trims a comment and drops a blank one", () => {
|
|
78
|
+
const { copilotkit } = capturingCore();
|
|
79
|
+
|
|
80
|
+
rate(copilotkit, { rating: "down", comment: " " });
|
|
81
|
+
expect(getMessageFeedback("agent", "thread", "m1")).toEqual({
|
|
82
|
+
rating: "down",
|
|
83
|
+
});
|
|
84
|
+
rate(copilotkit, { rating: "down", comment: " padded " });
|
|
85
|
+
|
|
86
|
+
expect(getMessageFeedback("agent", "thread", "m1")).toEqual({
|
|
87
|
+
rating: "down",
|
|
88
|
+
comment: "padded",
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("tells only the rated Thread's listeners", () => {
|
|
93
|
+
const { copilotkit } = capturingCore();
|
|
94
|
+
const rated = vi.fn();
|
|
95
|
+
const other = vi.fn();
|
|
96
|
+
subscribeToMessageFeedback("agent", "thread", rated);
|
|
97
|
+
subscribeToMessageFeedback("agent", "other", other);
|
|
98
|
+
|
|
99
|
+
rate(copilotkit, { rating: "up" });
|
|
100
|
+
|
|
101
|
+
expect(rated).toHaveBeenCalledTimes(1);
|
|
102
|
+
expect(other).not.toHaveBeenCalled();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("keeps the object of a rating that did not change", () => {
|
|
106
|
+
const { copilotkit } = capturingCore();
|
|
107
|
+
rate(copilotkit, { rating: "up" });
|
|
108
|
+
const before = getMessageFeedback("agent", "thread", "m1");
|
|
109
|
+
|
|
110
|
+
rate(
|
|
111
|
+
copilotkit,
|
|
112
|
+
{ rating: "down" },
|
|
113
|
+
{ id: "m2", role: "assistant", content: "No." },
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
expect(getMessageFeedback("agent", "thread", "m1")).toBe(before);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("treats message ids like object keys as plain ids", () => {
|
|
120
|
+
const { copilotkit } = capturingCore();
|
|
121
|
+
|
|
122
|
+
rate(
|
|
123
|
+
copilotkit,
|
|
124
|
+
{ rating: "up" },
|
|
125
|
+
{ id: "__proto__", role: "assistant", content: "" },
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(getMessageFeedback("agent", "thread", "__proto__")).toEqual({
|
|
129
|
+
rating: "up",
|
|
130
|
+
});
|
|
131
|
+
expect(
|
|
132
|
+
getMessageFeedback("agent", "thread", "constructor"),
|
|
133
|
+
).toBeUndefined();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("leaves out the reply text when capture omits agent text", () => {
|
|
137
|
+
const { copilotkit, emit } = capturingCore({ agentText: false });
|
|
138
|
+
|
|
139
|
+
rate(copilotkit, { rating: "up" });
|
|
140
|
+
|
|
141
|
+
expect(emit.mock.calls[0]![1]).not.toHaveProperty("response");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("names the reply's run when Core knows it", () => {
|
|
145
|
+
const { copilotkit, emit } = capturingCore();
|
|
146
|
+
vi.spyOn(copilotkit, "getRunIdForMessage").mockReturnValue("run-9");
|
|
147
|
+
|
|
148
|
+
rate(copilotkit, { rating: "up" });
|
|
149
|
+
|
|
150
|
+
expect(emit.mock.calls[0]![1]).toMatchObject({ runId: "run-9" });
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { AssistantMessage } from "@ag-ui/core";
|
|
2
|
+
import type { CopilotKitCore } from "@copilotkit/core";
|
|
3
|
+
|
|
4
|
+
/** Trajectory event name for a rating of one assistant message. */
|
|
5
|
+
export const MESSAGE_FEEDBACK_EVENT_NAME = "message.feedback";
|
|
6
|
+
|
|
7
|
+
/** Longest comment kept, in characters; the rest is cut so the event stays small. */
|
|
8
|
+
export const MESSAGE_FEEDBACK_MAX_COMMENT_LENGTH = 1000;
|
|
9
|
+
|
|
10
|
+
/** Longest excerpt of the rated response sent with the rating, in characters. */
|
|
11
|
+
const RESPONSE_EXCERPT_LENGTH = 500;
|
|
12
|
+
|
|
13
|
+
export type MessageFeedbackRating = "up" | "down";
|
|
14
|
+
|
|
15
|
+
/** The rating this page gave one assistant message. */
|
|
16
|
+
export interface MessageFeedback {
|
|
17
|
+
readonly rating: MessageFeedbackRating;
|
|
18
|
+
readonly comment?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Ratings given on this page, by message. Kept outside Vue, so a rated
|
|
22
|
+
// message that scrolls out of a virtual list still shows its rating when it
|
|
23
|
+
// comes back. Intelligence holds the ratings across page loads.
|
|
24
|
+
const ratings = new Map<string, MessageFeedback>();
|
|
25
|
+
const listeners = new Map<string, Set<() => void>>();
|
|
26
|
+
|
|
27
|
+
const threadKey = (agentId: string, threadId: string) =>
|
|
28
|
+
JSON.stringify([agentId, threadId]);
|
|
29
|
+
|
|
30
|
+
const messageKey = (agentId: string, threadId: string, messageId: string) =>
|
|
31
|
+
JSON.stringify([agentId, threadId, messageId]);
|
|
32
|
+
|
|
33
|
+
/** Cuts `text` to `max` characters without splitting a surrogate pair. */
|
|
34
|
+
const clip = (text: string, max: number): string => {
|
|
35
|
+
const characters = Array.from(text);
|
|
36
|
+
return characters.length > max ? characters.slice(0, max).join("") : text;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** The comment as kept and sent: trimmed and capped, or none. */
|
|
40
|
+
const normalizeComment = (comment: unknown): string | undefined => {
|
|
41
|
+
if (typeof comment !== "string") return undefined;
|
|
42
|
+
const text = clip(comment.trim(), MESSAGE_FEEDBACK_MAX_COMMENT_LENGTH);
|
|
43
|
+
return text === "" ? undefined : text;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** The rating this page gave one message, if any. */
|
|
47
|
+
export function getMessageFeedback(
|
|
48
|
+
agentId: string,
|
|
49
|
+
threadId: string,
|
|
50
|
+
messageId: string,
|
|
51
|
+
): MessageFeedback | undefined {
|
|
52
|
+
return ratings.get(messageKey(agentId, threadId, messageId));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Calls `listener` whenever a rating in this Thread changes. */
|
|
56
|
+
export function subscribeToMessageFeedback(
|
|
57
|
+
agentId: string,
|
|
58
|
+
threadId: string,
|
|
59
|
+
listener: () => void,
|
|
60
|
+
): () => void {
|
|
61
|
+
const key = threadKey(agentId, threadId);
|
|
62
|
+
let set = listeners.get(key);
|
|
63
|
+
if (set === undefined) {
|
|
64
|
+
set = new Set();
|
|
65
|
+
listeners.set(key, set);
|
|
66
|
+
}
|
|
67
|
+
set.add(listener);
|
|
68
|
+
return () => {
|
|
69
|
+
set.delete(listener);
|
|
70
|
+
if (set.size === 0) listeners.delete(key);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Sets (or, with `null`, clears) this page's rating of one message and sends
|
|
76
|
+
* it to the active Trajectory. Every change is a new event; the newest event
|
|
77
|
+
* for a `feedbackId` is the current one. Like any Trajectory event, a change
|
|
78
|
+
* made while no Trajectory captures, for example during a reconnect, is not
|
|
79
|
+
* sent; the page still shows it.
|
|
80
|
+
*/
|
|
81
|
+
export function setMessageFeedback(params: {
|
|
82
|
+
copilotkit: CopilotKitCore;
|
|
83
|
+
agentId: string;
|
|
84
|
+
threadId: string;
|
|
85
|
+
message: AssistantMessage;
|
|
86
|
+
feedback: MessageFeedback | null;
|
|
87
|
+
}): void {
|
|
88
|
+
const { copilotkit, agentId, threadId, message } = params;
|
|
89
|
+
const comment = normalizeComment(params.feedback?.comment);
|
|
90
|
+
const feedback: MessageFeedback | null = params.feedback && {
|
|
91
|
+
rating: params.feedback.rating,
|
|
92
|
+
...(comment ? { comment } : {}),
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const key = messageKey(agentId, threadId, message.id);
|
|
96
|
+
if (feedback) ratings.set(key, feedback);
|
|
97
|
+
else ratings.delete(key);
|
|
98
|
+
for (const listener of listeners.get(threadKey(agentId, threadId)) ?? [])
|
|
99
|
+
listener();
|
|
100
|
+
|
|
101
|
+
const runId = copilotkit.getRunIdForMessage(agentId, threadId, message.id);
|
|
102
|
+
copilotkit.emitTrajectoryEvent(MESSAGE_FEEDBACK_EVENT_NAME, {
|
|
103
|
+
feedbackId: `${threadId}:${message.id}`,
|
|
104
|
+
threadId,
|
|
105
|
+
messageId: message.id,
|
|
106
|
+
agentId,
|
|
107
|
+
...(runId ? { runId } : {}),
|
|
108
|
+
rating: feedback?.rating ?? null,
|
|
109
|
+
...(feedback?.comment ? { comment: feedback.comment } : {}),
|
|
110
|
+
// A custom sink set to omit agent text gets no excerpt of the reply.
|
|
111
|
+
...(copilotkit.ɵlearningCapturesAgentText
|
|
112
|
+
? { response: clip(message.content ?? "", RESPONSE_EXCERPT_LENGTH) }
|
|
113
|
+
: {}),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** @internal Forgets every rating, so each test starts empty. */
|
|
118
|
+
export function ɵresetMessageFeedbackCache(): void {
|
|
119
|
+
ratings.clear();
|
|
120
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, inject, provide, ref } from "vue";
|
|
2
|
+
import { computed, inject, onMounted, provide, ref, watch } from "vue";
|
|
3
3
|
import type { ComputedRef } from "vue";
|
|
4
4
|
import { DEFAULT_AGENT_ID, randomUUID } from "@copilotkit/shared";
|
|
5
|
-
import {
|
|
5
|
+
import type { OpenThreadRegistration } from "@copilotkit/core";
|
|
6
|
+
import { CopilotChatConfigurationKey, CopilotKitKey } from "./keys";
|
|
6
7
|
import { CopilotChatDefaultLabels } from "./types";
|
|
7
8
|
import type { CopilotChatConfigurationValue, CopilotChatLabels } from "./types";
|
|
8
9
|
import type { CopilotChatConfigurationProviderProps } from "./CopilotChatConfigurationProvider.types";
|
|
@@ -61,6 +62,32 @@ const resolvedThreadId = computed(() => {
|
|
|
61
62
|
return fallbackThreadId;
|
|
62
63
|
});
|
|
63
64
|
|
|
65
|
+
// Tell Core which Thread this view shows, so captured interactions can link
|
|
66
|
+
// to it. This provider also works without a CopilotKitProvider, and then there
|
|
67
|
+
// is nothing to register with. Registration starts on mount, so a server
|
|
68
|
+
// render never registers a Thread that it can't unregister.
|
|
69
|
+
const copilotKitContext = inject(CopilotKitKey, null);
|
|
70
|
+
let openThread: OpenThreadRegistration | null = null;
|
|
71
|
+
onMounted(() => {
|
|
72
|
+
watch(
|
|
73
|
+
() => [copilotKitContext?.copilotkit.value, resolvedAgentId.value] as const,
|
|
74
|
+
([core, agentId], _previous, onCleanup) => {
|
|
75
|
+
if (!core) return;
|
|
76
|
+
const registration = core.registerOpenThread({
|
|
77
|
+
agentId,
|
|
78
|
+
threadId: resolvedThreadId.value,
|
|
79
|
+
});
|
|
80
|
+
openThread = registration;
|
|
81
|
+
onCleanup(() => {
|
|
82
|
+
registration.unregister();
|
|
83
|
+
openThread = null;
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
{ immediate: true },
|
|
87
|
+
);
|
|
88
|
+
watch(resolvedThreadId, (threadId) => openThread?.update(threadId));
|
|
89
|
+
});
|
|
90
|
+
|
|
64
91
|
const resolvedHasExplicitThreadId = computed(() => {
|
|
65
92
|
if (propIsAuthoritative.value) return true;
|
|
66
93
|
const own = activeThreadOverride.value
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { A2UIRecoveryRendererOptions } from "../components/a2ui/A2UIRecoveryStates";
|
|
1
2
|
import type { AbstractAgent } from "@ag-ui/client";
|
|
2
3
|
import type {
|
|
3
4
|
CopilotKitCoreErrorCode,
|
|
4
5
|
CopilotKitHeadersSource,
|
|
5
6
|
CopilotKitMessageFilter,
|
|
7
|
+
LearningConfig,
|
|
6
8
|
} from "@copilotkit/core";
|
|
7
9
|
import type { DebugConfig } from "@copilotkit/shared";
|
|
8
10
|
import type {
|
|
@@ -26,6 +28,17 @@ export interface CopilotKitProviderProps {
|
|
|
26
28
|
*/
|
|
27
29
|
headers?: CopilotKitHeadersSource;
|
|
28
30
|
credentials?: RequestCredentials;
|
|
31
|
+
/**
|
|
32
|
+
* CSP nonce for the `<style>` tag CopilotKit adds. Set it when your
|
|
33
|
+
* Content-Security-Policy limits `style-src` to a nonce.
|
|
34
|
+
*/
|
|
35
|
+
nonce?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Set to `false` when you import `@copilotkit/core/styles.css` yourself, so
|
|
38
|
+
* browsers cache the CSS as a file. CopilotKit then adds no `<style>` tag.
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
injectStyles?: boolean;
|
|
29
42
|
/**
|
|
30
43
|
* Rewrites the message list sent to runtime agents on every run.
|
|
31
44
|
*
|
|
@@ -107,6 +120,12 @@ export interface CopilotKitProviderProps {
|
|
|
107
120
|
* `a2ui_params["guidelines"]["composition_guide"]` in Python).
|
|
108
121
|
*/
|
|
109
122
|
includeSchema?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Options for the A2UI error-recovery status UI: how long before the
|
|
125
|
+
* transient "Retrying…" hint appears, after how many attempts, and how
|
|
126
|
+
* much retry/debug detail to surface. When omitted, defaults apply.
|
|
127
|
+
*/
|
|
128
|
+
recovery?: A2UIRecoveryRendererOptions;
|
|
110
129
|
};
|
|
111
130
|
/** Configuration for the built-in MCP Apps renderer. */
|
|
112
131
|
mcpApps?: {
|
|
@@ -137,4 +156,42 @@ export interface CopilotKitProviderProps {
|
|
|
137
156
|
* - `{ events?: boolean; lifecycle?: boolean; verbose?: boolean }` for granular control.
|
|
138
157
|
*/
|
|
139
158
|
debug?: DebugConfig;
|
|
159
|
+
/**
|
|
160
|
+
* Turns on interaction capture (`@copilotkit/learning`). `learning` (short
|
|
161
|
+
* for `:learning="true"`) records with the default options; pass an object
|
|
162
|
+
* for options. `false` or omitting the prop turns capture off and cancels
|
|
163
|
+
* startup. Without a sink, Core authenticates with the runtime and sends
|
|
164
|
+
* browser events to Intelligence; a custom sink keeps the standalone
|
|
165
|
+
* collector behavior. Updated settings apply to the next Trajectory.
|
|
166
|
+
*
|
|
167
|
+
* Capture starts after mount. Set `trajectoryId` to use your own ID;
|
|
168
|
+
* otherwise the provider generates one. The generated ID stays the same
|
|
169
|
+
* across rerenders, reconnects and option changes; turning capture off,
|
|
170
|
+
* setting `autoStart: false` or supplying a `trajectoryId` ends it, and the
|
|
171
|
+
* next generated Trajectory gets a new ID. Read the active ID from
|
|
172
|
+
* `copilotkit.trajectoryId`.
|
|
173
|
+
*
|
|
174
|
+
* Set `autoStart: false` to start capture yourself with `startTrajectory()`.
|
|
175
|
+
* Capture stops on unmount, including manually started Trajectories.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* <CopilotKitProvider runtime-url="/api/copilotkit" learning>
|
|
179
|
+
*/
|
|
180
|
+
learning?: boolean | CopilotKitLearningOptions;
|
|
140
181
|
}
|
|
182
|
+
|
|
183
|
+
/** The object form of the `learning` prop. */
|
|
184
|
+
export type CopilotKitLearningOptions = LearningConfig & {
|
|
185
|
+
/**
|
|
186
|
+
* Starts a Trajectory after mount. Defaults to `true`; set `false` to call
|
|
187
|
+
* `startTrajectory()` yourself.
|
|
188
|
+
*/
|
|
189
|
+
autoStart?: boolean;
|
|
190
|
+
trajectoryId?: string;
|
|
191
|
+
/**
|
|
192
|
+
* Used only by explicit custom sinks. Authenticated capture warns and ignores
|
|
193
|
+
* this option; assign Learning Spaces on the server with
|
|
194
|
+
* `getTrajectoryLearningContainerIds` on `CopilotKitIntelligence`.
|
|
195
|
+
*/
|
|
196
|
+
learningContainerIds?: string[];
|
|
197
|
+
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
GENERATE_SANDBOXED_UI_DESCRIPTION,
|
|
4
|
+
randomUUID,
|
|
5
|
+
} from "@copilotkit/shared";
|
|
3
6
|
import {
|
|
4
7
|
computed,
|
|
5
8
|
h,
|
|
@@ -43,7 +46,12 @@ import {
|
|
|
43
46
|
MCPAppsActivityRenderer,
|
|
44
47
|
MCPAppsActivityType,
|
|
45
48
|
} from "../components/MCPAppsActivityRenderer";
|
|
46
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
CopilotKitKey,
|
|
51
|
+
CopilotStyleOptionsKey,
|
|
52
|
+
InspectorKey,
|
|
53
|
+
SandboxFunctionsKey,
|
|
54
|
+
} from "./keys";
|
|
47
55
|
import type { VueInspectorOpenRequest } from "./keys";
|
|
48
56
|
import { MARKDOWN_RENDERER_KEY } from "./markdown-renderer";
|
|
49
57
|
import {
|
|
@@ -53,7 +61,10 @@ import {
|
|
|
53
61
|
import type { LicenseContextValue } from "./license-context";
|
|
54
62
|
import CopilotKitInspector from "../components/CopilotKitInspector.vue";
|
|
55
63
|
import LicenseWarningBanner from "../components/LicenseWarningBanner.vue";
|
|
56
|
-
import type {
|
|
64
|
+
import type {
|
|
65
|
+
CopilotKitLearningOptions,
|
|
66
|
+
CopilotKitProviderProps,
|
|
67
|
+
} from "./CopilotKitProvider.types";
|
|
57
68
|
import type {
|
|
58
69
|
SandboxFunction,
|
|
59
70
|
VueActivityMessageRenderer,
|
|
@@ -114,8 +125,17 @@ const props = withDefaults(defineProps<CopilotKitProviderProps>(), {
|
|
|
114
125
|
a2ui: undefined,
|
|
115
126
|
mcpApps: undefined,
|
|
116
127
|
enableInspector: undefined,
|
|
128
|
+
nonce: undefined,
|
|
129
|
+
injectStyles: true,
|
|
130
|
+
// A missing boolean prop would otherwise read as `false`.
|
|
131
|
+
learning: undefined,
|
|
117
132
|
});
|
|
118
133
|
|
|
134
|
+
provide(
|
|
135
|
+
CopilotStyleOptionsKey,
|
|
136
|
+
computed(() => ({ nonce: props.nonce, inject: props.injectStyles })),
|
|
137
|
+
);
|
|
138
|
+
|
|
119
139
|
const shouldRenderInspector = ref(false);
|
|
120
140
|
|
|
121
141
|
function updateInspectorVisibility(): void {
|
|
@@ -471,6 +491,7 @@ const builtInActivityRenderers = computed<
|
|
|
471
491
|
theme: props.a2ui?.theme ?? viewerTheme,
|
|
472
492
|
catalog: props.a2ui?.catalog,
|
|
473
493
|
loadingComponent: props.a2ui?.loadingComponent,
|
|
494
|
+
recovery: props.a2ui?.recovery,
|
|
474
495
|
}),
|
|
475
496
|
);
|
|
476
497
|
}
|
|
@@ -495,6 +516,19 @@ const applyDefaultThrottleMs = (core: CopilotKitCoreVue) => {
|
|
|
495
516
|
core.setDefaultThrottleMs(props.defaultThrottleMs);
|
|
496
517
|
};
|
|
497
518
|
|
|
519
|
+
function toLearningConfig(
|
|
520
|
+
learning: boolean | CopilotKitLearningOptions | undefined,
|
|
521
|
+
): CopilotKitLearningOptions | undefined {
|
|
522
|
+
// `true` uses the default authenticated capture options.
|
|
523
|
+
if (learning === true) return {};
|
|
524
|
+
if (learning === false || learning === undefined) return undefined;
|
|
525
|
+
// `autoStart` only controls the provider; Core never receives it.
|
|
526
|
+
const { autoStart: _autoStart, ...config } = learning;
|
|
527
|
+
return config;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const learningConfig = computed(() => toLearningConfig(props.learning));
|
|
531
|
+
|
|
498
532
|
const createCopilotKit = () => {
|
|
499
533
|
const core = new CopilotKitCoreVue({
|
|
500
534
|
runtimeUrl: chatApiEndpoint.value,
|
|
@@ -514,6 +548,7 @@ const createCopilotKit = () => {
|
|
|
514
548
|
renderActivityMessages: allRenderActivityMessages.value,
|
|
515
549
|
renderCustomMessages: allRenderCustomMessages.value,
|
|
516
550
|
debug: props.debug,
|
|
551
|
+
learning: learningConfig.value,
|
|
517
552
|
});
|
|
518
553
|
// Initialize synchronously so child hooks can read the value on first render.
|
|
519
554
|
applyDefaultThrottleMs(core);
|
|
@@ -696,8 +731,95 @@ onMounted(() => {
|
|
|
696
731
|
runtimeEntitlementRetryPending.value =
|
|
697
732
|
copilotkit.value.runtimeEntitlementRetryPending;
|
|
698
733
|
didMountRef.value = true;
|
|
734
|
+
// After the runtime URL is set, so CopilotKit's own runtime traffic is never
|
|
735
|
+
// captured. Created here, the watchers stop (and stop capture) on unmount.
|
|
736
|
+
watchLearning();
|
|
699
737
|
});
|
|
700
738
|
|
|
739
|
+
const learningEnabled = computed(() => learningConfig.value !== undefined);
|
|
740
|
+
// `true` is the options-free form, so only an object can opt out.
|
|
741
|
+
const learningAutoStart = computed(
|
|
742
|
+
() =>
|
|
743
|
+
learningEnabled.value &&
|
|
744
|
+
(typeof props.learning !== "object" || props.learning.autoStart !== false),
|
|
745
|
+
);
|
|
746
|
+
const suppliedTrajectoryId = computed(() =>
|
|
747
|
+
learningAutoStart.value ? learningConfig.value?.trajectoryId : undefined,
|
|
748
|
+
);
|
|
749
|
+
const generatesTrajectoryId = computed(
|
|
750
|
+
() => learningAutoStart.value && suppliedTrajectoryId.value === undefined,
|
|
751
|
+
);
|
|
752
|
+
|
|
753
|
+
function watchLearning() {
|
|
754
|
+
let learningContainerIds = learningConfig.value?.learningContainerIds;
|
|
755
|
+
|
|
756
|
+
watch(
|
|
757
|
+
[learningConfig, learningAutoStart],
|
|
758
|
+
([config, autoStart]) => {
|
|
759
|
+
copilotkit.value.setLearningConfig(config);
|
|
760
|
+
learningContainerIds = config?.learningContainerIds;
|
|
761
|
+
// Automatic starts report unsupported options in Core. With manual
|
|
762
|
+
// starts, the provider's container option is not part of
|
|
763
|
+
// startTrajectory() options.
|
|
764
|
+
if (
|
|
765
|
+
config?.sink === undefined &&
|
|
766
|
+
!autoStart &&
|
|
767
|
+
config?.learningContainerIds !== undefined
|
|
768
|
+
) {
|
|
769
|
+
console.warn(
|
|
770
|
+
"[CopilotKit] learningContainerIds is supported only with a custom sink. Authenticated Trajectory capture ignores it; assign Learning Spaces on the server with getTrajectoryLearningContainerIds on CopilotKitIntelligence, and remove learningContainerIds from the capture options.",
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
},
|
|
774
|
+
{ immediate: true },
|
|
775
|
+
);
|
|
776
|
+
|
|
777
|
+
// Watched as a boolean so a new inline `learning` object warns once.
|
|
778
|
+
watch(
|
|
779
|
+
() =>
|
|
780
|
+
!learningAutoStart.value &&
|
|
781
|
+
learningConfig.value?.trajectoryId !== undefined,
|
|
782
|
+
(ignoresTrajectoryId) => {
|
|
783
|
+
if (!ignoresTrajectoryId) return;
|
|
784
|
+
console.warn(
|
|
785
|
+
"[CopilotKit] learning.trajectoryId is ignored when autoStart is false; pass the ID to startTrajectory() instead.",
|
|
786
|
+
);
|
|
787
|
+
},
|
|
788
|
+
{ immediate: true },
|
|
789
|
+
);
|
|
790
|
+
|
|
791
|
+
// Runs only when these change, so option changes that keep generating keep
|
|
792
|
+
// the generated Trajectory; generating again starts one with a new ID.
|
|
793
|
+
watch(
|
|
794
|
+
[learningEnabled, generatesTrajectoryId, suppliedTrajectoryId],
|
|
795
|
+
([enabled, generates, supplied], _previous, onCleanup) => {
|
|
796
|
+
if (!enabled) return;
|
|
797
|
+
let disposed = false;
|
|
798
|
+
const core = copilotkit.value;
|
|
799
|
+
const trajectoryId = generates ? randomUUID() : supplied;
|
|
800
|
+
if (trajectoryId !== undefined) {
|
|
801
|
+
void core
|
|
802
|
+
.startTrajectory({ trajectoryId, learningContainerIds })
|
|
803
|
+
.catch(() => {
|
|
804
|
+
// Expected authentication/connection failures resolve with an
|
|
805
|
+
// error result; Core reports them to LearningConfig.onError or
|
|
806
|
+
// warns once. Consume unexpected rejections without logging
|
|
807
|
+
// credentials or errors from a previous start after a new
|
|
808
|
+
// Trajectory has already started.
|
|
809
|
+
if (!disposed) {
|
|
810
|
+
console.warn("[CopilotKit] Failed to start interaction capture.");
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
onCleanup(() => {
|
|
815
|
+
disposed = true;
|
|
816
|
+
core.stopTrajectory();
|
|
817
|
+
});
|
|
818
|
+
},
|
|
819
|
+
{ immediate: true },
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
|
|
701
823
|
const a2uiTheme = computed(() => props.a2ui?.theme);
|
|
702
824
|
const a2uiCatalog = computed(() => props.a2ui?.catalog);
|
|
703
825
|
const a2uiLoadingComponent = computed(() => props.a2ui?.loadingComponent);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { defineComponent, h, nextTick, ref } from "vue";
|
|
3
|
+
import { mount } from "@vue/test-utils";
|
|
4
|
+
import { CopilotKitCore } from "@copilotkit/core";
|
|
5
|
+
import CopilotKitProvider from "../CopilotKitProvider.vue";
|
|
6
|
+
import CopilotChatConfigurationProvider from "../CopilotChatConfigurationProvider.vue";
|
|
7
|
+
|
|
8
|
+
// Core links captured interactions to the Thread that a chat view shows, as
|
|
9
|
+
// React's CopilotChatConfigurationProvider does.
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.restoreAllMocks();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
function spyOnRegistration() {
|
|
16
|
+
const registration = { update: vi.fn(), unregister: vi.fn() };
|
|
17
|
+
const register = vi
|
|
18
|
+
.spyOn(CopilotKitCore.prototype, "registerOpenThread")
|
|
19
|
+
.mockReturnValue(registration);
|
|
20
|
+
return { register, registration };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("CopilotChatConfigurationProvider open Thread", () => {
|
|
24
|
+
it("registers its Thread, follows Thread changes, and unregisters on unmount", async () => {
|
|
25
|
+
const { register, registration } = spyOnRegistration();
|
|
26
|
+
const threadId = ref("t-1");
|
|
27
|
+
const App = defineComponent({
|
|
28
|
+
setup: () => () =>
|
|
29
|
+
h(
|
|
30
|
+
CopilotKitProvider,
|
|
31
|
+
{ runtimeUrl: "/api/copilotkit" },
|
|
32
|
+
{
|
|
33
|
+
default: () =>
|
|
34
|
+
h(
|
|
35
|
+
CopilotChatConfigurationProvider,
|
|
36
|
+
{ agentId: "agent-a", threadId: threadId.value },
|
|
37
|
+
{ default: () => h("div") },
|
|
38
|
+
),
|
|
39
|
+
},
|
|
40
|
+
),
|
|
41
|
+
});
|
|
42
|
+
const wrapper = mount(App);
|
|
43
|
+
|
|
44
|
+
expect(register).toHaveBeenCalledTimes(1);
|
|
45
|
+
expect(register).toHaveBeenCalledWith({
|
|
46
|
+
agentId: "agent-a",
|
|
47
|
+
threadId: "t-1",
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
threadId.value = "t-2";
|
|
51
|
+
await nextTick();
|
|
52
|
+
expect(registration.update).toHaveBeenLastCalledWith("t-2");
|
|
53
|
+
expect(register).toHaveBeenCalledTimes(1);
|
|
54
|
+
|
|
55
|
+
wrapper.unmount();
|
|
56
|
+
expect(registration.unregister).toHaveBeenCalledTimes(1);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("works without a CopilotKitProvider", () => {
|
|
60
|
+
const { register } = spyOnRegistration();
|
|
61
|
+
const wrapper = mount(CopilotChatConfigurationProvider, {
|
|
62
|
+
props: { agentId: "agent-a", threadId: "t-1" },
|
|
63
|
+
slots: { default: () => h("div") },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(register).not.toHaveBeenCalled();
|
|
67
|
+
wrapper.unmount();
|
|
68
|
+
});
|
|
69
|
+
});
|