@copilotkitnext/angular 0.0.1 → 0.0.4
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/README.md +248 -0
- package/dist/README.md +248 -0
- package/dist/components/chat/copilot-chat-assistant-message.component.d.ts +10 -10
- package/dist/components/chat/copilot-chat-message-view.component.d.ts +42 -42
- package/dist/components/chat/copilot-chat-view.component.d.ts +14 -14
- package/dist/esm2022/components/chat/copilot-chat-assistant-message-buttons.component.mjs +384 -0
- package/dist/esm2022/components/chat/copilot-chat-assistant-message-renderer.component.mjs +286 -0
- package/dist/esm2022/components/chat/copilot-chat-assistant-message-toolbar.component.mjs +27 -0
- package/dist/esm2022/components/chat/copilot-chat-assistant-message.component.mjs +433 -0
- package/dist/esm2022/components/chat/copilot-chat-assistant-message.types.mjs +2 -0
- package/dist/esm2022/components/chat/copilot-chat-audio-recorder.component.mjs +202 -0
- package/dist/esm2022/components/chat/copilot-chat-buttons.component.mjs +321 -0
- package/dist/esm2022/components/chat/copilot-chat-input-defaults.mjs +38 -0
- package/dist/esm2022/components/chat/copilot-chat-input.component.mjs +666 -0
- package/dist/esm2022/components/chat/copilot-chat-input.types.mjs +10 -0
- package/dist/esm2022/components/chat/copilot-chat-message-view-cursor.component.mjs +45 -0
- package/dist/esm2022/components/chat/copilot-chat-message-view.component.mjs +296 -0
- package/dist/esm2022/components/chat/copilot-chat-message-view.types.mjs +2 -0
- package/dist/esm2022/components/chat/copilot-chat-textarea.component.mjs +188 -0
- package/dist/esm2022/components/chat/copilot-chat-tool-calls-view.component.mjs +216 -0
- package/dist/esm2022/components/chat/copilot-chat-toolbar.component.mjs +25 -0
- package/dist/esm2022/components/chat/copilot-chat-tools-menu.component.mjs +199 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message-branch-navigation.component.mjs +137 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message-buttons.component.mjs +207 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message-renderer.component.mjs +35 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message-toolbar.component.mjs +34 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message.component.mjs +341 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message.types.mjs +2 -0
- package/dist/esm2022/components/chat/copilot-chat-view-disclaimer.component.mjs +52 -0
- package/dist/esm2022/components/chat/copilot-chat-view-feather.component.mjs +55 -0
- package/dist/esm2022/components/chat/copilot-chat-view-handlers.service.mjs +19 -0
- package/dist/esm2022/components/chat/copilot-chat-view-input-container.component.mjs +110 -0
- package/dist/esm2022/components/chat/copilot-chat-view-scroll-to-bottom-button.component.mjs +93 -0
- package/dist/esm2022/components/chat/copilot-chat-view-scroll-view.component.mjs +443 -0
- package/dist/esm2022/components/chat/copilot-chat-view.component.mjs +479 -0
- package/dist/esm2022/components/chat/copilot-chat-view.types.mjs +2 -0
- package/dist/esm2022/components/chat/copilot-chat.component.mjs +214 -0
- package/dist/esm2022/components/copilotkit-tool-render.component.mjs +153 -0
- package/dist/esm2022/copilotkitnext-angular.mjs +5 -0
- package/dist/esm2022/core/chat-configuration/chat-configuration.providers.mjs +65 -0
- package/dist/esm2022/core/chat-configuration/chat-configuration.service.mjs +145 -0
- package/dist/esm2022/core/chat-configuration/chat-configuration.types.mjs +26 -0
- package/dist/esm2022/core/copilotkit.providers.mjs +34 -0
- package/dist/esm2022/core/copilotkit.service.mjs +430 -0
- package/dist/esm2022/core/copilotkit.types.mjs +12 -0
- package/dist/esm2022/directives/copilotkit-agent-context.directive.mjs +130 -0
- package/dist/esm2022/directives/copilotkit-agent.directive.mjs +217 -0
- package/dist/esm2022/directives/copilotkit-chat-config.directive.mjs +218 -0
- package/dist/esm2022/directives/copilotkit-config.directive.mjs +94 -0
- package/dist/esm2022/directives/copilotkit-frontend-tool.directive.mjs +130 -0
- package/dist/esm2022/directives/copilotkit-human-in-the-loop.directive.mjs +266 -0
- package/dist/esm2022/directives/stick-to-bottom.directive.mjs +181 -0
- package/dist/esm2022/index.mjs +70 -0
- package/dist/esm2022/lib/directives/tooltip.directive.mjs +211 -0
- package/dist/esm2022/lib/slots/copilot-slot.component.mjs +144 -0
- package/dist/esm2022/lib/slots/slot.types.mjs +6 -0
- package/dist/esm2022/lib/slots/slot.utils.mjs +222 -0
- package/dist/esm2022/lib/utils.mjs +10 -0
- package/dist/esm2022/services/resize-observer.service.mjs +152 -0
- package/dist/esm2022/services/scroll-position.service.mjs +124 -0
- package/dist/esm2022/types/frontend-tool.mjs +2 -0
- package/dist/esm2022/types/human-in-the-loop.mjs +2 -0
- package/dist/esm2022/utils/agent-context.utils.mjs +114 -0
- package/dist/esm2022/utils/agent.utils.mjs +204 -0
- package/dist/esm2022/utils/chat-config.utils.mjs +186 -0
- package/dist/esm2022/utils/copilotkit.utils.mjs +20 -0
- package/dist/esm2022/utils/frontend-tool.utils.mjs +228 -0
- package/dist/esm2022/utils/human-in-the-loop.utils.mjs +296 -0
- package/dist/fesm2022/copilotkitnext-angular.mjs +163 -164
- package/dist/fesm2022/copilotkitnext-angular.mjs.map +1 -1
- package/dist/styles.css +0 -27
- package/package.json +23 -20
- package/vitest.config.mts +32 -21
- package/.turbo/turbo-build.log +0 -39
- package/.turbo/turbo-check-types.log +0 -0
- package/.turbo/turbo-test.log +0 -71
- package/README-agent-context.md +0 -310
- package/ng-package.json +0 -19
- package/slots.md +0 -331
- package/src/components/chat/__tests__/copilot-chat-assistant-message.component.spec.ts +0 -282
- package/src/components/chat/__tests__/copilot-chat-input.component.spec.ts +0 -419
- package/src/components/chat/__tests__/copilot-chat-message-view.component.spec.ts +0 -372
- package/src/components/chat/__tests__/copilot-chat-user-message.component.spec.ts +0 -249
- package/src/components/chat/copilot-chat-assistant-message-buttons.component.ts +0 -292
- package/src/components/chat/copilot-chat-assistant-message-renderer.component.ts +0 -472
- package/src/components/chat/copilot-chat-assistant-message-toolbar.component.ts +0 -29
- package/src/components/chat/copilot-chat-assistant-message.component.ts +0 -463
- package/src/components/chat/copilot-chat-assistant-message.types.ts +0 -50
- package/src/components/chat/copilot-chat-audio-recorder.component.ts +0 -241
- package/src/components/chat/copilot-chat-buttons.component.ts +0 -308
- package/src/components/chat/copilot-chat-buttons.component.ts.bak +0 -471
- package/src/components/chat/copilot-chat-input-defaults.ts +0 -47
- package/src/components/chat/copilot-chat-input.component.ts +0 -512
- package/src/components/chat/copilot-chat-input.types.ts +0 -148
- package/src/components/chat/copilot-chat-message-view-cursor.component.ts +0 -51
- package/src/components/chat/copilot-chat-message-view.component.ts +0 -233
- package/src/components/chat/copilot-chat-message-view.types.ts +0 -39
- package/src/components/chat/copilot-chat-textarea.component.ts +0 -220
- package/src/components/chat/copilot-chat-tool-calls-view.component.ts +0 -261
- package/src/components/chat/copilot-chat-toolbar.component.ts +0 -35
- package/src/components/chat/copilot-chat-tools-menu.component.ts +0 -185
- package/src/components/chat/copilot-chat-user-message-branch-navigation.component.ts +0 -121
- package/src/components/chat/copilot-chat-user-message-buttons.component.ts +0 -170
- package/src/components/chat/copilot-chat-user-message-renderer.component.ts +0 -37
- package/src/components/chat/copilot-chat-user-message-toolbar.component.ts +0 -37
- package/src/components/chat/copilot-chat-user-message.component.ts +0 -247
- package/src/components/chat/copilot-chat-user-message.types.ts +0 -42
- package/src/components/chat/copilot-chat-view-disclaimer.component.ts +0 -51
- package/src/components/chat/copilot-chat-view-feather.component.ts +0 -47
- package/src/components/chat/copilot-chat-view-handlers.service.ts +0 -14
- package/src/components/chat/copilot-chat-view-input-container.component.ts +0 -87
- package/src/components/chat/copilot-chat-view-scroll-to-bottom-button.component.ts +0 -79
- package/src/components/chat/copilot-chat-view-scroll-view.component.ts +0 -322
- package/src/components/chat/copilot-chat-view.component.ts +0 -420
- package/src/components/chat/copilot-chat-view.types.ts +0 -52
- package/src/components/chat/copilot-chat.component.ts +0 -232
- package/src/components/copilotkit-tool-render.component.ts +0 -169
- package/src/core/__tests__/copilotkit.service.spec.ts +0 -1051
- package/src/core/__tests__/copilotkit.service.wildcard.spec.ts +0 -316
- package/src/core/chat-configuration/__tests__/chat-configuration.service.spec.ts +0 -287
- package/src/core/chat-configuration/chat-configuration.providers.ts +0 -71
- package/src/core/chat-configuration/chat-configuration.service.ts +0 -162
- package/src/core/chat-configuration/chat-configuration.types.ts +0 -57
- package/src/core/copilotkit.providers.ts +0 -59
- package/src/core/copilotkit.service.ts +0 -542
- package/src/core/copilotkit.types.ts +0 -132
- package/src/directives/__tests__/copilotkit-agent-context.directive.spec.ts +0 -384
- package/src/directives/__tests__/copilotkit-agent.directive.spec.ts +0 -253
- package/src/directives/__tests__/copilotkit-chat-config.directive.spec.ts +0 -385
- package/src/directives/__tests__/copilotkit-config.directive.spec.ts +0 -69
- package/src/directives/__tests__/copilotkit-frontend-tool-simple.directive.spec.ts +0 -60
- package/src/directives/__tests__/copilotkit-frontend-tool.directive.spec.ts +0 -108
- package/src/directives/__tests__/copilotkit-human-in-the-loop.directive.spec.ts +0 -452
- package/src/directives/copilotkit-agent-context.directive.ts +0 -138
- package/src/directives/copilotkit-agent.directive.ts +0 -225
- package/src/directives/copilotkit-chat-config.directive.ts +0 -241
- package/src/directives/copilotkit-config.directive.ts +0 -81
- package/src/directives/copilotkit-frontend-tool.directive.ts +0 -145
- package/src/directives/copilotkit-human-in-the-loop.directive.ts +0 -281
- package/src/directives/stick-to-bottom.directive.ts +0 -204
- package/src/index.ts +0 -105
- package/src/lib/directives/tooltip.directive.ts +0 -292
- package/src/lib/slots/__tests__/slot.utils.spec.ts +0 -377
- package/src/lib/slots/copilot-slot.component.ts +0 -135
- package/src/lib/slots/index.ts +0 -3
- package/src/lib/slots/slot.types.ts +0 -64
- package/src/lib/slots/slot.utils.ts +0 -289
- package/src/lib/utils.ts +0 -10
- package/src/public-api.ts +0 -1
- package/src/services/resize-observer.service.ts +0 -181
- package/src/services/scroll-position.service.ts +0 -169
- package/src/styles/globals.css +0 -266
- package/src/styles/index.css +0 -3
- package/src/test-setup.ts +0 -15
- package/src/testing/index.ts +0 -3
- package/src/testing/testing.utils.ts +0 -248
- package/src/types/frontend-tool.ts +0 -44
- package/src/types/human-in-the-loop.ts +0 -52
- package/src/utils/__tests__/agent.utils.spec.ts +0 -234
- package/src/utils/__tests__/chat-config.utils.spec.ts +0 -306
- package/src/utils/__tests__/frontend-tool-inject.spec.ts +0 -350
- package/src/utils/__tests__/frontend-tool-integration.spec.ts +0 -199
- package/src/utils/__tests__/frontend-tool.utils.spec.ts +0 -272
- package/src/utils/__tests__/human-in-the-loop.utils.spec.ts +0 -365
- package/src/utils/agent-context.utils.ts +0 -133
- package/src/utils/agent.utils.ts +0 -239
- package/src/utils/chat-config.utils.ts +0 -221
- package/src/utils/copilotkit.utils.ts +0 -20
- package/src/utils/frontend-tool.utils.ts +0 -266
- package/src/utils/human-in-the-loop.utils.ts +0 -359
- package/tsconfig.spec.json +0 -12
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import { TestBed } from "@angular/core/testing";
|
|
2
|
-
import { Component } from "@angular/core";
|
|
3
|
-
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
|
|
4
|
-
import { addFrontendTool, removeFrontendTool } from "../frontend-tool.utils";
|
|
5
|
-
import { CopilotKitService } from "../../core/copilotkit.service";
|
|
6
|
-
import { provideCopilotKit } from "../../core/copilotkit.providers";
|
|
7
|
-
import { z } from "zod";
|
|
8
|
-
|
|
9
|
-
// Mock CopilotKitCore
|
|
10
|
-
vi.mock("@copilotkitnext/core", () => ({
|
|
11
|
-
CopilotKitCore: vi.fn().mockImplementation(() => ({
|
|
12
|
-
addTool: vi.fn(),
|
|
13
|
-
removeTool: vi.fn(),
|
|
14
|
-
setRuntimeUrl: vi.fn(),
|
|
15
|
-
setHeaders: vi.fn(),
|
|
16
|
-
setProperties: vi.fn(),
|
|
17
|
-
setAgents: vi.fn(),
|
|
18
|
-
subscribe: vi.fn(() => () => {}),
|
|
19
|
-
})),
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
// Mock component for testing
|
|
23
|
-
@Component({
|
|
24
|
-
template: `<div>Mock Tool Render</div>`,
|
|
25
|
-
standalone: true,
|
|
26
|
-
})
|
|
27
|
-
class MockRenderComponent {}
|
|
28
|
-
|
|
29
|
-
describe("Frontend Tool Utils", () => {
|
|
30
|
-
let service: CopilotKitService;
|
|
31
|
-
let addToolSpy: any;
|
|
32
|
-
let removeToolSpy: any;
|
|
33
|
-
|
|
34
|
-
beforeEach(() => {
|
|
35
|
-
TestBed.configureTestingModule({
|
|
36
|
-
providers: [provideCopilotKit({})],
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
service = TestBed.inject(CopilotKitService);
|
|
40
|
-
addToolSpy = vi.spyOn(service.copilotkit, "addTool");
|
|
41
|
-
removeToolSpy = vi.spyOn(service.copilotkit, "removeTool");
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
afterEach(() => {
|
|
45
|
-
vi.clearAllMocks();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe("addFrontendTool", () => {
|
|
49
|
-
it("should add tool to CopilotKit", () => {
|
|
50
|
-
const tool = {
|
|
51
|
-
name: "testTool",
|
|
52
|
-
description: "Test tool",
|
|
53
|
-
parameters: z.object({ value: z.string() }),
|
|
54
|
-
handler: vi.fn(async () => "result"),
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const cleanup = addFrontendTool(service, tool);
|
|
58
|
-
|
|
59
|
-
expect(addToolSpy).toHaveBeenCalledWith(tool);
|
|
60
|
-
expect(typeof cleanup).toBe("function");
|
|
61
|
-
|
|
62
|
-
cleanup();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("should register render when provided", () => {
|
|
66
|
-
const tool = {
|
|
67
|
-
name: "renderTool",
|
|
68
|
-
description: "Tool with render",
|
|
69
|
-
render: MockRenderComponent,
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const cleanup = addFrontendTool(service, tool);
|
|
73
|
-
|
|
74
|
-
const renders = service.currentRenderToolCalls();
|
|
75
|
-
const renderTool = renders.find((r) => r.name === "renderTool");
|
|
76
|
-
expect(renderTool).toBeDefined();
|
|
77
|
-
expect(renderTool?.render).toBe(MockRenderComponent);
|
|
78
|
-
|
|
79
|
-
cleanup();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("should return cleanup function that removes tool and render", () => {
|
|
83
|
-
const tool = {
|
|
84
|
-
name: "cleanupTool",
|
|
85
|
-
description: "Tool to cleanup",
|
|
86
|
-
render: MockRenderComponent,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const cleanup = addFrontendTool(service, tool);
|
|
90
|
-
|
|
91
|
-
// Verify tool was added
|
|
92
|
-
expect(addToolSpy).toHaveBeenCalledWith(tool);
|
|
93
|
-
let renders = service.currentRenderToolCalls();
|
|
94
|
-
expect(renders.find((r) => r.name === "cleanupTool")).toBeDefined();
|
|
95
|
-
|
|
96
|
-
// Execute cleanup
|
|
97
|
-
cleanup();
|
|
98
|
-
|
|
99
|
-
// Verify tool was removed
|
|
100
|
-
expect(removeToolSpy).toHaveBeenCalledWith("cleanupTool");
|
|
101
|
-
renders = service.currentRenderToolCalls();
|
|
102
|
-
expect(renders.find((r) => r.name === "cleanupTool")).toBeUndefined();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("should handle tool without parameters", () => {
|
|
106
|
-
const tool = {
|
|
107
|
-
name: "noParams",
|
|
108
|
-
description: "No parameters tool",
|
|
109
|
-
handler: vi.fn(async () => "result"),
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const cleanup = addFrontendTool(service, tool);
|
|
113
|
-
|
|
114
|
-
expect(addToolSpy).toHaveBeenCalledWith(tool);
|
|
115
|
-
|
|
116
|
-
cleanup();
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("should warn about duplicate render names", () => {
|
|
120
|
-
const consoleSpy = vi
|
|
121
|
-
.spyOn(console, "error")
|
|
122
|
-
.mockImplementation(() => {});
|
|
123
|
-
|
|
124
|
-
// Pre-register a render
|
|
125
|
-
service.setCurrentRenderToolCalls([
|
|
126
|
-
{
|
|
127
|
-
name: "duplicateTool",
|
|
128
|
-
args: z.object({}),
|
|
129
|
-
render: MockRenderComponent,
|
|
130
|
-
},
|
|
131
|
-
]);
|
|
132
|
-
|
|
133
|
-
const tool = {
|
|
134
|
-
name: "duplicateTool",
|
|
135
|
-
render: MockRenderComponent,
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const cleanup = addFrontendTool(service, tool);
|
|
139
|
-
|
|
140
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
141
|
-
expect.stringContaining("already has a render")
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
consoleSpy.mockRestore();
|
|
145
|
-
cleanup();
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("should handle complex parameter schemas", () => {
|
|
149
|
-
const complexSchema = z.object({
|
|
150
|
-
user: z.object({
|
|
151
|
-
name: z.string(),
|
|
152
|
-
age: z.number(),
|
|
153
|
-
}),
|
|
154
|
-
settings: z.object({
|
|
155
|
-
theme: z.enum(["light", "dark"]),
|
|
156
|
-
notifications: z.boolean(),
|
|
157
|
-
}),
|
|
158
|
-
items: z.array(z.string()),
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const tool = {
|
|
162
|
-
name: "complexTool",
|
|
163
|
-
parameters: complexSchema,
|
|
164
|
-
handler: vi.fn(),
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const cleanup = addFrontendTool(service, tool);
|
|
168
|
-
|
|
169
|
-
expect(addToolSpy).toHaveBeenCalledWith(
|
|
170
|
-
expect.objectContaining({
|
|
171
|
-
name: "complexTool",
|
|
172
|
-
parameters: complexSchema,
|
|
173
|
-
})
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
cleanup();
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
describe("removeFrontendTool", () => {
|
|
181
|
-
it("should remove tool from CopilotKit", () => {
|
|
182
|
-
removeFrontendTool(service, "testTool");
|
|
183
|
-
expect(removeToolSpy).toHaveBeenCalledWith("testTool");
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it("should remove render if exists", () => {
|
|
187
|
-
// Setup a tool with render
|
|
188
|
-
service.setCurrentRenderToolCalls([
|
|
189
|
-
{
|
|
190
|
-
name: "toolWithRender",
|
|
191
|
-
args: z.object({}),
|
|
192
|
-
render: MockRenderComponent,
|
|
193
|
-
},
|
|
194
|
-
]);
|
|
195
|
-
|
|
196
|
-
removeFrontendTool(service, "toolWithRender");
|
|
197
|
-
|
|
198
|
-
expect(removeToolSpy).toHaveBeenCalledWith("toolWithRender");
|
|
199
|
-
const renders = service.currentRenderToolCalls();
|
|
200
|
-
expect(renders.find((r) => r.name === "toolWithRender")).toBeUndefined();
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("should handle removing non-existent tool gracefully", () => {
|
|
204
|
-
expect(() => {
|
|
205
|
-
removeFrontendTool(service, "nonExistent");
|
|
206
|
-
}).not.toThrow();
|
|
207
|
-
|
|
208
|
-
expect(removeToolSpy).toHaveBeenCalledWith("nonExistent");
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it("should only remove specified tool", () => {
|
|
212
|
-
// Setup multiple tools
|
|
213
|
-
service.setCurrentRenderToolCalls([
|
|
214
|
-
{ name: "tool1", args: z.object({}), render: MockRenderComponent },
|
|
215
|
-
{ name: "tool2", args: z.object({}), render: MockRenderComponent },
|
|
216
|
-
{ name: "tool3", args: z.object({}), render: MockRenderComponent },
|
|
217
|
-
]);
|
|
218
|
-
|
|
219
|
-
removeFrontendTool(service, "tool2");
|
|
220
|
-
|
|
221
|
-
const renders = service.currentRenderToolCalls();
|
|
222
|
-
expect(renders.find((r) => r.name === "tool1")).toBeDefined();
|
|
223
|
-
expect(renders.find((r) => r.name === "tool2")).toBeUndefined();
|
|
224
|
-
expect(renders.find((r) => r.name === "tool3")).toBeDefined();
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
describe("Service Integration", () => {
|
|
229
|
-
it("should work with service render methods", () => {
|
|
230
|
-
const tool = {
|
|
231
|
-
name: "serviceTool",
|
|
232
|
-
render: MockRenderComponent,
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
// Test registerToolRender
|
|
236
|
-
service.registerToolRender("serviceTool", {
|
|
237
|
-
name: "serviceTool",
|
|
238
|
-
args: z.object({}),
|
|
239
|
-
render: MockRenderComponent,
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
expect(service.getToolRender("serviceTool")).toBeDefined();
|
|
243
|
-
|
|
244
|
-
// Test unregisterToolRender
|
|
245
|
-
service.unregisterToolRender("serviceTool");
|
|
246
|
-
expect(service.getToolRender("serviceTool")).toBeUndefined();
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
it("should handle multiple tools with renders", () => {
|
|
250
|
-
const tools = [
|
|
251
|
-
{ name: "tool1", render: MockRenderComponent },
|
|
252
|
-
{ name: "tool2", render: MockRenderComponent },
|
|
253
|
-
{ name: "tool3", render: MockRenderComponent },
|
|
254
|
-
];
|
|
255
|
-
|
|
256
|
-
const cleanups = tools.map((tool) => addFrontendTool(service, tool));
|
|
257
|
-
|
|
258
|
-
// All tools should be registered
|
|
259
|
-
expect(addToolSpy).toHaveBeenCalledTimes(3);
|
|
260
|
-
const renders = service.currentRenderToolCalls();
|
|
261
|
-
expect(renders.find((r) => r.name === "tool1")).toBeDefined();
|
|
262
|
-
expect(renders.find((r) => r.name === "tool2")).toBeDefined();
|
|
263
|
-
expect(renders.find((r) => r.name === "tool3")).toBeDefined();
|
|
264
|
-
|
|
265
|
-
// Cleanup all
|
|
266
|
-
cleanups.forEach((cleanup) => cleanup());
|
|
267
|
-
|
|
268
|
-
// All tools should be removed
|
|
269
|
-
expect(removeToolSpy).toHaveBeenCalledTimes(3);
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
});
|
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
import { TestBed } from "@angular/core/testing";
|
|
2
|
-
import { Component } from "@angular/core";
|
|
3
|
-
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
|
|
4
|
-
import {
|
|
5
|
-
registerHumanInTheLoop,
|
|
6
|
-
addHumanInTheLoop,
|
|
7
|
-
createHumanInTheLoop,
|
|
8
|
-
enhancePropsForHumanInTheLoop,
|
|
9
|
-
} from "../human-in-the-loop.utils";
|
|
10
|
-
import { CopilotKitService } from "../../core/copilotkit.service";
|
|
11
|
-
import { provideCopilotKit } from "../../core/copilotkit.providers";
|
|
12
|
-
import { z } from "zod";
|
|
13
|
-
import {
|
|
14
|
-
HumanInTheLoopProps,
|
|
15
|
-
ToolCallStatus,
|
|
16
|
-
} from "../../core/copilotkit.types";
|
|
17
|
-
|
|
18
|
-
// Mock CopilotKitCore
|
|
19
|
-
const mockCopilotKitCore = {
|
|
20
|
-
addTool: vi.fn(() => "tool-id-123"),
|
|
21
|
-
removeTool: vi.fn(),
|
|
22
|
-
setRuntimeUrl: vi.fn(),
|
|
23
|
-
setHeaders: vi.fn(),
|
|
24
|
-
setProperties: vi.fn(),
|
|
25
|
-
setAgents: vi.fn(),
|
|
26
|
-
getAgent: vi.fn(),
|
|
27
|
-
subscribe: vi.fn(() => vi.fn()),
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
vi.mock("@copilotkitnext/core", () => ({
|
|
31
|
-
CopilotKitCore: vi.fn().mockImplementation(() => mockCopilotKitCore),
|
|
32
|
-
ToolCallStatus: {
|
|
33
|
-
InProgress: "inProgress",
|
|
34
|
-
Executing: "executing",
|
|
35
|
-
Complete: "complete",
|
|
36
|
-
},
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
|
-
// Test component for rendering
|
|
40
|
-
@Component({
|
|
41
|
-
selector: "test-approval",
|
|
42
|
-
template: `
|
|
43
|
-
<div class="approval-dialog">
|
|
44
|
-
<p>{{ props.args.action }}</p>
|
|
45
|
-
<button *ngIf="props.respond" (click)="approve()">Approve</button>
|
|
46
|
-
<button *ngIf="props.respond" (click)="reject()">Reject</button>
|
|
47
|
-
<span class="status">{{ props.status }}</span>
|
|
48
|
-
</div>
|
|
49
|
-
`,
|
|
50
|
-
standalone: true,
|
|
51
|
-
})
|
|
52
|
-
class TestApprovalComponent {
|
|
53
|
-
props!: HumanInTheLoopProps<{ action: string }>;
|
|
54
|
-
|
|
55
|
-
approve() {
|
|
56
|
-
this.props.respond?.("approved");
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
reject() {
|
|
60
|
-
this.props.respond?.("rejected");
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
describe("Human-in-the-Loop Utilities", () => {
|
|
65
|
-
let service: CopilotKitService;
|
|
66
|
-
|
|
67
|
-
beforeEach(() => {
|
|
68
|
-
TestBed.configureTestingModule({
|
|
69
|
-
providers: [provideCopilotKit({})],
|
|
70
|
-
declarations: [],
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
service = TestBed.inject(CopilotKitService);
|
|
74
|
-
vi.clearAllMocks();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
afterEach(() => {
|
|
78
|
-
vi.clearAllMocks();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
describe("registerHumanInTheLoop", () => {
|
|
82
|
-
it("should register a tool with handler that returns a Promise", () => {
|
|
83
|
-
@Component({
|
|
84
|
-
template: "",
|
|
85
|
-
standalone: true,
|
|
86
|
-
providers: [provideCopilotKit({})],
|
|
87
|
-
})
|
|
88
|
-
class TestComponent {
|
|
89
|
-
toolId = registerHumanInTheLoop({
|
|
90
|
-
name: "requireApproval",
|
|
91
|
-
description: "Requires user approval",
|
|
92
|
-
parameters: z.object({ action: z.string() }),
|
|
93
|
-
render: TestApprovalComponent,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const fixture = TestBed.createComponent(TestComponent);
|
|
98
|
-
fixture.detectChanges();
|
|
99
|
-
|
|
100
|
-
expect(mockCopilotKitCore.addTool).toHaveBeenCalled();
|
|
101
|
-
expect(fixture.componentInstance.toolId).toBe("requireApproval");
|
|
102
|
-
|
|
103
|
-
// Verify the tool was added with a handler
|
|
104
|
-
const addToolCall = mockCopilotKitCore.addTool.mock.calls[0][0];
|
|
105
|
-
expect(addToolCall.name).toBe("requireApproval");
|
|
106
|
-
expect(addToolCall.description).toBe("Requires user approval");
|
|
107
|
-
expect(typeof addToolCall.handler).toBe("function");
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it("should cleanup on component destroy", () => {
|
|
111
|
-
@Component({
|
|
112
|
-
template: "",
|
|
113
|
-
standalone: true,
|
|
114
|
-
providers: [provideCopilotKit({})],
|
|
115
|
-
})
|
|
116
|
-
class TestComponent {
|
|
117
|
-
toolId = registerHumanInTheLoop({
|
|
118
|
-
name: "requireApproval",
|
|
119
|
-
description: "Requires user approval",
|
|
120
|
-
parameters: z.object({ action: z.string() }),
|
|
121
|
-
render: TestApprovalComponent,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const fixture = TestBed.createComponent(TestComponent);
|
|
126
|
-
fixture.detectChanges();
|
|
127
|
-
|
|
128
|
-
const toolId = fixture.componentInstance.toolId;
|
|
129
|
-
|
|
130
|
-
fixture.destroy();
|
|
131
|
-
|
|
132
|
-
expect(mockCopilotKitCore.removeTool).toHaveBeenCalledWith(toolId);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("should handle Promise resolution when respond is called", async () => {
|
|
136
|
-
@Component({
|
|
137
|
-
template: "",
|
|
138
|
-
standalone: true,
|
|
139
|
-
providers: [provideCopilotKit({})],
|
|
140
|
-
})
|
|
141
|
-
class TestComponent {
|
|
142
|
-
toolId = registerHumanInTheLoop({
|
|
143
|
-
name: "requireApproval",
|
|
144
|
-
description: "Requires user approval",
|
|
145
|
-
parameters: z.object({ action: z.string() }),
|
|
146
|
-
render: TestApprovalComponent,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const fixture = TestBed.createComponent(TestComponent);
|
|
151
|
-
fixture.detectChanges();
|
|
152
|
-
|
|
153
|
-
// Get the handler from the mock call
|
|
154
|
-
const addToolCall = mockCopilotKitCore.addTool.mock.calls[0][0];
|
|
155
|
-
const handler = addToolCall.handler;
|
|
156
|
-
|
|
157
|
-
// Call the handler - it should return a Promise
|
|
158
|
-
const resultPromise = handler({ action: "delete" });
|
|
159
|
-
expect(resultPromise).toBeDefined();
|
|
160
|
-
expect(typeof resultPromise.then).toBe("function");
|
|
161
|
-
|
|
162
|
-
// The Promise should remain pending until respond is called
|
|
163
|
-
// This would require access to the respond function which is internal
|
|
164
|
-
// For now, we just verify the Promise is created
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
describe("addHumanInTheLoop", () => {
|
|
169
|
-
it("should add a tool and return cleanup function", () => {
|
|
170
|
-
const cleanup = addHumanInTheLoop(service, {
|
|
171
|
-
name: "requireApproval",
|
|
172
|
-
description: "Requires user approval",
|
|
173
|
-
args: z.object({ action: z.string() }),
|
|
174
|
-
render: TestApprovalComponent,
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
expect(mockCopilotKitCore.addTool).toHaveBeenCalled();
|
|
178
|
-
expect(typeof cleanup).toBe("function");
|
|
179
|
-
|
|
180
|
-
// Call cleanup
|
|
181
|
-
cleanup();
|
|
182
|
-
expect(mockCopilotKitCore.removeTool).toHaveBeenCalledWith(
|
|
183
|
-
"requireApproval"
|
|
184
|
-
);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("should register tool render", () => {
|
|
188
|
-
const registerSpy = vi.spyOn(service, "registerToolRender");
|
|
189
|
-
|
|
190
|
-
addHumanInTheLoop(service, {
|
|
191
|
-
name: "requireApproval",
|
|
192
|
-
description: "Requires user approval",
|
|
193
|
-
parameters: z.object({ action: z.string() }),
|
|
194
|
-
render: TestApprovalComponent,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
expect(registerSpy).toHaveBeenCalledWith("requireApproval", {
|
|
198
|
-
name: "requireApproval",
|
|
199
|
-
args: expect.any(Object), // Note: registerToolRender expects 'args', not 'parameters'
|
|
200
|
-
render: TestApprovalComponent,
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it("should unregister tool render on cleanup", () => {
|
|
205
|
-
const unregisterSpy = vi.spyOn(service, "unregisterToolRender");
|
|
206
|
-
|
|
207
|
-
const cleanup = addHumanInTheLoop(service, {
|
|
208
|
-
name: "requireApproval",
|
|
209
|
-
description: "Requires user approval",
|
|
210
|
-
args: z.object({ action: z.string() }),
|
|
211
|
-
render: TestApprovalComponent,
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
cleanup();
|
|
215
|
-
|
|
216
|
-
expect(unregisterSpy).toHaveBeenCalledWith("requireApproval");
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
describe("createHumanInTheLoop", () => {
|
|
221
|
-
it("should create tool with status signal and control methods", () => {
|
|
222
|
-
const result = createHumanInTheLoop(service, {
|
|
223
|
-
name: "requireApproval",
|
|
224
|
-
description: "Requires user approval",
|
|
225
|
-
args: z.object({ action: z.string() }),
|
|
226
|
-
render: TestApprovalComponent,
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
expect(result.status).toBeDefined();
|
|
230
|
-
expect(result.toolId).toBe("requireApproval");
|
|
231
|
-
expect(typeof result.update).toBe("function");
|
|
232
|
-
expect(typeof result.destroy).toBe("function");
|
|
233
|
-
|
|
234
|
-
// Check initial status
|
|
235
|
-
expect(result.status()).toBe(ToolCallStatus.InProgress);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
it("should allow updating tool configuration", () => {
|
|
239
|
-
const result = createHumanInTheLoop(service, {
|
|
240
|
-
name: "requireApproval",
|
|
241
|
-
description: "Requires user approval",
|
|
242
|
-
args: z.object({ action: z.string() }),
|
|
243
|
-
render: TestApprovalComponent,
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// Clear previous calls
|
|
247
|
-
vi.clearAllMocks();
|
|
248
|
-
|
|
249
|
-
// Update the tool
|
|
250
|
-
result.update({
|
|
251
|
-
description: "Updated description",
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
// Should remove old tool and add new one
|
|
255
|
-
expect(mockCopilotKitCore.removeTool).toHaveBeenCalledWith(
|
|
256
|
-
"requireApproval"
|
|
257
|
-
);
|
|
258
|
-
expect(mockCopilotKitCore.addTool).toHaveBeenCalled();
|
|
259
|
-
|
|
260
|
-
const addToolCall = mockCopilotKitCore.addTool.mock.calls[0][0];
|
|
261
|
-
expect(addToolCall.description).toBe("Updated description");
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
it("should cleanup when destroy is called", () => {
|
|
265
|
-
const result = createHumanInTheLoop(service, {
|
|
266
|
-
name: "requireApproval",
|
|
267
|
-
description: "Requires user approval",
|
|
268
|
-
args: z.object({ action: z.string() }),
|
|
269
|
-
render: TestApprovalComponent,
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
result.destroy();
|
|
273
|
-
|
|
274
|
-
expect(mockCopilotKitCore.removeTool).toHaveBeenCalledWith(
|
|
275
|
-
"requireApproval"
|
|
276
|
-
);
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
it("should track status changes through handler lifecycle", async () => {
|
|
280
|
-
const result = createHumanInTheLoop(service, {
|
|
281
|
-
name: "requireApproval",
|
|
282
|
-
description: "Requires user approval",
|
|
283
|
-
args: z.object({ action: z.string() }),
|
|
284
|
-
render: TestApprovalComponent,
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
// Initial status
|
|
288
|
-
expect(result.status()).toBe(ToolCallStatus.InProgress);
|
|
289
|
-
|
|
290
|
-
// Get the handler
|
|
291
|
-
const addToolCall = mockCopilotKitCore.addTool.mock.calls[0][0];
|
|
292
|
-
const handler = addToolCall.handler;
|
|
293
|
-
|
|
294
|
-
// Call handler - should change status to executing
|
|
295
|
-
const promise = handler({ action: "delete" });
|
|
296
|
-
|
|
297
|
-
// Note: We can't directly test the status change to 'executing'
|
|
298
|
-
// without access to internal state, but we verify the Promise is created
|
|
299
|
-
expect(promise).toBeDefined();
|
|
300
|
-
expect(typeof promise.then).toBe("function");
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
describe("enhancePropsForHumanInTheLoop", () => {
|
|
305
|
-
it("should add respond function when status is executing", () => {
|
|
306
|
-
const respond = vi.fn();
|
|
307
|
-
const props: HumanInTheLoopProps = {
|
|
308
|
-
name: "test",
|
|
309
|
-
description: "Test tool",
|
|
310
|
-
args: { action: "delete" },
|
|
311
|
-
status: ToolCallStatus.Executing,
|
|
312
|
-
result: undefined,
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
const enhanced = enhancePropsForHumanInTheLoop(
|
|
316
|
-
props,
|
|
317
|
-
ToolCallStatus.Executing,
|
|
318
|
-
respond
|
|
319
|
-
);
|
|
320
|
-
|
|
321
|
-
expect(enhanced.respond).toBe(respond);
|
|
322
|
-
expect(enhanced.status).toBe(ToolCallStatus.Executing);
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it("should not add respond function when status is inProgress", () => {
|
|
326
|
-
const respond = vi.fn();
|
|
327
|
-
const props: HumanInTheLoopProps = {
|
|
328
|
-
name: "test",
|
|
329
|
-
description: "Test tool",
|
|
330
|
-
args: { action: "delete" },
|
|
331
|
-
status: ToolCallStatus.InProgress,
|
|
332
|
-
result: undefined,
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
const enhanced = enhancePropsForHumanInTheLoop(
|
|
336
|
-
props,
|
|
337
|
-
ToolCallStatus.InProgress,
|
|
338
|
-
respond
|
|
339
|
-
);
|
|
340
|
-
|
|
341
|
-
expect(enhanced.respond).toBeUndefined();
|
|
342
|
-
expect(enhanced.status).toBe(ToolCallStatus.InProgress);
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
it("should not add respond function when status is complete", () => {
|
|
346
|
-
const respond = vi.fn();
|
|
347
|
-
const props: HumanInTheLoopProps = {
|
|
348
|
-
name: "test",
|
|
349
|
-
description: "Test tool",
|
|
350
|
-
args: { action: "delete" },
|
|
351
|
-
status: "complete",
|
|
352
|
-
result: "approved",
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
const enhanced = enhancePropsForHumanInTheLoop(
|
|
356
|
-
props,
|
|
357
|
-
"complete",
|
|
358
|
-
respond
|
|
359
|
-
);
|
|
360
|
-
|
|
361
|
-
expect(enhanced.respond).toBeUndefined();
|
|
362
|
-
expect(enhanced.status).toBe("complete");
|
|
363
|
-
});
|
|
364
|
-
});
|
|
365
|
-
});
|