@copilotkitnext/agent 0.0.13-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +23 -0
- package/LICENSE +11 -0
- package/dist/index.d.mts +187 -0
- package/dist/index.d.ts +187 -0
- package/dist/index.js +554 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +532 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +50 -0
- package/src/__tests__/basic-agent.test.ts +499 -0
- package/src/__tests__/property-overrides.test.ts +559 -0
- package/src/__tests__/state-tools.test.ts +391 -0
- package/src/__tests__/test-helpers.ts +117 -0
- package/src/__tests__/utils.test.ts +438 -0
- package/src/index.ts +894 -0
- package/tsconfig.json +13 -0
- package/tsup.config.ts +11 -0
- package/vitest.config.ts +13 -0
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@copilotkitnext/agent",
|
|
3
|
+
"version": "0.0.13-alpha.0",
|
|
4
|
+
"description": "Basic Agent for CopilotKit",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^22.15.3",
|
|
19
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
20
|
+
"eslint": "^9.30.0",
|
|
21
|
+
"tsup": "^8.5.0",
|
|
22
|
+
"typescript": "5.8.2",
|
|
23
|
+
"vitest": "^3.0.5",
|
|
24
|
+
"@copilotkitnext/eslint-config": "0.0.0",
|
|
25
|
+
"@copilotkitnext/typescript-config": "0.0.0"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@ag-ui/client": "0.0.40-alpha.3",
|
|
29
|
+
"@ai-sdk/anthropic": "^2.0.22",
|
|
30
|
+
"@ai-sdk/google": "^2.0.17",
|
|
31
|
+
"@ai-sdk/openai": "^2.0.42",
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.18.2",
|
|
33
|
+
"ai": "^5.0.59",
|
|
34
|
+
"rxjs": "^7.8.1",
|
|
35
|
+
"zod": "^3.25.75"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup",
|
|
42
|
+
"dev": "tsup --watch",
|
|
43
|
+
"lint": "eslint . --max-warnings 0",
|
|
44
|
+
"check-types": "tsc --noEmit",
|
|
45
|
+
"clean": "rm -rf dist",
|
|
46
|
+
"test": "vitest run",
|
|
47
|
+
"test:watch": "vitest",
|
|
48
|
+
"test:coverage": "vitest run --coverage"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { BasicAgent, defineTool, type ToolDefinition } from "../index";
|
|
4
|
+
import { EventType, type RunAgentInput } from "@ag-ui/client";
|
|
5
|
+
import { streamText } from "ai";
|
|
6
|
+
import {
|
|
7
|
+
mockStreamTextResponse,
|
|
8
|
+
textDelta,
|
|
9
|
+
finish,
|
|
10
|
+
collectEvents,
|
|
11
|
+
toolCallStreamingStart,
|
|
12
|
+
toolCallDelta,
|
|
13
|
+
toolCall,
|
|
14
|
+
toolResult,
|
|
15
|
+
} from "./test-helpers";
|
|
16
|
+
|
|
17
|
+
// Mock the ai module
|
|
18
|
+
vi.mock("ai", () => ({
|
|
19
|
+
streamText: vi.fn(),
|
|
20
|
+
tool: vi.fn((config) => config),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
// Mock the SDK clients
|
|
24
|
+
vi.mock("@ai-sdk/openai", () => ({
|
|
25
|
+
createOpenAI: vi.fn(() => (modelId: string) => ({
|
|
26
|
+
modelId,
|
|
27
|
+
provider: "openai",
|
|
28
|
+
})),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
vi.mock("@ai-sdk/anthropic", () => ({
|
|
32
|
+
createAnthropic: vi.fn(() => (modelId: string) => ({
|
|
33
|
+
modelId,
|
|
34
|
+
provider: "anthropic",
|
|
35
|
+
})),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
vi.mock("@ai-sdk/google", () => ({
|
|
39
|
+
createGoogleGenerativeAI: vi.fn(() => (modelId: string) => ({
|
|
40
|
+
modelId,
|
|
41
|
+
provider: "google",
|
|
42
|
+
})),
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
describe("BasicAgent", () => {
|
|
46
|
+
const originalEnv = process.env;
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
vi.clearAllMocks();
|
|
50
|
+
process.env = { ...originalEnv };
|
|
51
|
+
process.env.OPENAI_API_KEY = "test-key";
|
|
52
|
+
process.env.ANTHROPIC_API_KEY = "test-key";
|
|
53
|
+
process.env.GOOGLE_API_KEY = "test-key";
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
afterEach(() => {
|
|
57
|
+
process.env = originalEnv;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("Basic Event Emission", () => {
|
|
61
|
+
it("should emit RUN_STARTED and RUN_FINISHED events", async () => {
|
|
62
|
+
const agent = new BasicAgent({
|
|
63
|
+
model: "openai/gpt-4o",
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
vi.mocked(streamText).mockReturnValue(mockStreamTextResponse([textDelta("Hello"), finish()]) as any);
|
|
67
|
+
|
|
68
|
+
const input: RunAgentInput = {
|
|
69
|
+
threadId: "thread1",
|
|
70
|
+
runId: "run1",
|
|
71
|
+
messages: [],
|
|
72
|
+
tools: [],
|
|
73
|
+
context: [],
|
|
74
|
+
state: {},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const events = await collectEvents(agent["run"](input));
|
|
78
|
+
|
|
79
|
+
expect(events[0]).toMatchObject({
|
|
80
|
+
type: EventType.RUN_STARTED,
|
|
81
|
+
threadId: "thread1",
|
|
82
|
+
runId: "run1",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(events[events.length - 1]).toMatchObject({
|
|
86
|
+
type: EventType.RUN_FINISHED,
|
|
87
|
+
threadId: "thread1",
|
|
88
|
+
runId: "run1",
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should emit TEXT_MESSAGE_CHUNK events for text deltas", async () => {
|
|
93
|
+
const agent = new BasicAgent({
|
|
94
|
+
model: "openai/gpt-4o",
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
vi.mocked(streamText).mockReturnValue(
|
|
98
|
+
mockStreamTextResponse([textDelta("Hello"), textDelta(" world"), finish()]) as any,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const input: RunAgentInput = {
|
|
102
|
+
threadId: "thread1",
|
|
103
|
+
runId: "run1",
|
|
104
|
+
messages: [],
|
|
105
|
+
tools: [],
|
|
106
|
+
context: [],
|
|
107
|
+
state: {},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const events = await collectEvents(agent["run"](input));
|
|
111
|
+
|
|
112
|
+
const textEvents = events.filter((e: any) => e.type === EventType.TEXT_MESSAGE_CHUNK);
|
|
113
|
+
expect(textEvents).toHaveLength(2);
|
|
114
|
+
expect(textEvents[0]).toMatchObject({
|
|
115
|
+
type: EventType.TEXT_MESSAGE_CHUNK,
|
|
116
|
+
role: "assistant",
|
|
117
|
+
delta: "Hello",
|
|
118
|
+
});
|
|
119
|
+
expect(textEvents[1]).toMatchObject({
|
|
120
|
+
type: EventType.TEXT_MESSAGE_CHUNK,
|
|
121
|
+
delta: " world",
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe("Tool Call Events", () => {
|
|
127
|
+
it("should emit tool call lifecycle events", async () => {
|
|
128
|
+
const agent = new BasicAgent({
|
|
129
|
+
model: "openai/gpt-4o",
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
vi.mocked(streamText).mockReturnValue(
|
|
133
|
+
mockStreamTextResponse([
|
|
134
|
+
toolCallStreamingStart("call1", "testTool"),
|
|
135
|
+
toolCallDelta("call1", '{"arg'),
|
|
136
|
+
toolCallDelta("call1", '":"val"}'),
|
|
137
|
+
toolCall("call1", "testTool", { arg: "val" }),
|
|
138
|
+
toolResult("call1", "testTool", { result: "success" }),
|
|
139
|
+
finish(),
|
|
140
|
+
]) as any,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const input: RunAgentInput = {
|
|
144
|
+
threadId: "thread1",
|
|
145
|
+
runId: "run1",
|
|
146
|
+
messages: [],
|
|
147
|
+
tools: [],
|
|
148
|
+
context: [],
|
|
149
|
+
state: {},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const events = await collectEvents(agent["run"](input));
|
|
153
|
+
|
|
154
|
+
// Check for TOOL_CALL_START
|
|
155
|
+
const startEvent = events.find((e: any) => e.type === EventType.TOOL_CALL_START);
|
|
156
|
+
expect(startEvent).toMatchObject({
|
|
157
|
+
type: EventType.TOOL_CALL_START,
|
|
158
|
+
toolCallId: "call1",
|
|
159
|
+
toolCallName: "testTool",
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Check for TOOL_CALL_ARGS
|
|
163
|
+
const argsEvents = events.filter((e: any) => e.type === EventType.TOOL_CALL_ARGS);
|
|
164
|
+
expect(argsEvents).toHaveLength(2);
|
|
165
|
+
|
|
166
|
+
// Check for TOOL_CALL_END
|
|
167
|
+
const endEvent = events.find((e: any) => e.type === EventType.TOOL_CALL_END);
|
|
168
|
+
expect(endEvent).toMatchObject({
|
|
169
|
+
type: EventType.TOOL_CALL_END,
|
|
170
|
+
toolCallId: "call1",
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Check for TOOL_CALL_RESULT
|
|
174
|
+
const resultEvent = events.find((e: any) => e.type === EventType.TOOL_CALL_RESULT);
|
|
175
|
+
expect(resultEvent).toMatchObject({
|
|
176
|
+
type: EventType.TOOL_CALL_RESULT,
|
|
177
|
+
role: "tool",
|
|
178
|
+
toolCallId: "call1",
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe("Prompt Building", () => {
|
|
184
|
+
it("should not add system message when no prompt, context, or state", async () => {
|
|
185
|
+
const agent = new BasicAgent({
|
|
186
|
+
model: "openai/gpt-4o",
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
vi.mocked(streamText).mockReturnValue(mockStreamTextResponse([finish()]) as any);
|
|
190
|
+
|
|
191
|
+
const input: RunAgentInput = {
|
|
192
|
+
threadId: "thread1",
|
|
193
|
+
runId: "run1",
|
|
194
|
+
messages: [{ id: "1", role: "user", content: "Hello" }],
|
|
195
|
+
tools: [],
|
|
196
|
+
context: [],
|
|
197
|
+
state: {},
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
await collectEvents(agent["run"](input));
|
|
201
|
+
|
|
202
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
203
|
+
expect(callArgs.messages).toHaveLength(1);
|
|
204
|
+
expect(callArgs.messages[0].role).toBe("user");
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("should prepend system message with config prompt", async () => {
|
|
208
|
+
const agent = new BasicAgent({
|
|
209
|
+
model: "openai/gpt-4o",
|
|
210
|
+
prompt: "You are a helpful assistant.",
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
vi.mocked(streamText).mockReturnValue(mockStreamTextResponse([finish()]) as any);
|
|
214
|
+
|
|
215
|
+
const input: RunAgentInput = {
|
|
216
|
+
threadId: "thread1",
|
|
217
|
+
runId: "run1",
|
|
218
|
+
messages: [{ id: "1", role: "user", content: "Hello" }],
|
|
219
|
+
tools: [],
|
|
220
|
+
context: [],
|
|
221
|
+
state: {},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
await collectEvents(agent["run"](input));
|
|
225
|
+
|
|
226
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
227
|
+
expect(callArgs.messages).toHaveLength(2);
|
|
228
|
+
expect(callArgs.messages[0]).toMatchObject({
|
|
229
|
+
role: "system",
|
|
230
|
+
content: "You are a helpful assistant.",
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("should include context in system message", async () => {
|
|
235
|
+
const agent = new BasicAgent({
|
|
236
|
+
model: "openai/gpt-4o",
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
vi.mocked(streamText).mockReturnValue(mockStreamTextResponse([finish()]) as any);
|
|
240
|
+
|
|
241
|
+
const input: RunAgentInput = {
|
|
242
|
+
threadId: "thread1",
|
|
243
|
+
runId: "run1",
|
|
244
|
+
messages: [],
|
|
245
|
+
tools: [],
|
|
246
|
+
context: [
|
|
247
|
+
{ description: "User Name", value: "John Doe" },
|
|
248
|
+
{ description: "Location", value: "New York" },
|
|
249
|
+
],
|
|
250
|
+
state: {},
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
await collectEvents(agent["run"](input));
|
|
254
|
+
|
|
255
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
256
|
+
const systemMessage = callArgs.messages[0];
|
|
257
|
+
expect(systemMessage.role).toBe("system");
|
|
258
|
+
expect(systemMessage.content).toContain("Context from the application");
|
|
259
|
+
expect(systemMessage.content).toContain("User Name");
|
|
260
|
+
expect(systemMessage.content).toContain("John Doe");
|
|
261
|
+
expect(systemMessage.content).toContain("Location");
|
|
262
|
+
expect(systemMessage.content).toContain("New York");
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("should include state in system message", async () => {
|
|
266
|
+
const agent = new BasicAgent({
|
|
267
|
+
model: "openai/gpt-4o",
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
vi.mocked(streamText).mockReturnValue(mockStreamTextResponse([finish()]) as any);
|
|
271
|
+
|
|
272
|
+
const input: RunAgentInput = {
|
|
273
|
+
threadId: "thread1",
|
|
274
|
+
runId: "run1",
|
|
275
|
+
messages: [],
|
|
276
|
+
tools: [],
|
|
277
|
+
context: [],
|
|
278
|
+
state: { counter: 0, items: ["a", "b"] },
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
await collectEvents(agent["run"](input));
|
|
282
|
+
|
|
283
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
284
|
+
const systemMessage = callArgs.messages[0];
|
|
285
|
+
expect(systemMessage.role).toBe("system");
|
|
286
|
+
expect(systemMessage.content).toContain("Application State");
|
|
287
|
+
expect(systemMessage.content).toContain("AGUISendStateSnapshot");
|
|
288
|
+
expect(systemMessage.content).toContain("AGUISendStateDelta");
|
|
289
|
+
expect(systemMessage.content).toContain('"counter": 0');
|
|
290
|
+
expect(systemMessage.content).toContain('"items"');
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("should combine prompt, context, and state", async () => {
|
|
294
|
+
const agent = new BasicAgent({
|
|
295
|
+
model: "openai/gpt-4o",
|
|
296
|
+
prompt: "You are helpful.",
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
vi.mocked(streamText).mockReturnValue(mockStreamTextResponse([finish()]) as any);
|
|
300
|
+
|
|
301
|
+
const input: RunAgentInput = {
|
|
302
|
+
threadId: "thread1",
|
|
303
|
+
runId: "run1",
|
|
304
|
+
messages: [],
|
|
305
|
+
tools: [],
|
|
306
|
+
context: [{ description: "Context", value: "Data" }],
|
|
307
|
+
state: { value: 1 },
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
await collectEvents(agent["run"](input));
|
|
311
|
+
|
|
312
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
313
|
+
const systemMessage = callArgs.messages[0];
|
|
314
|
+
expect(systemMessage.content).toContain("You are helpful.");
|
|
315
|
+
expect(systemMessage.content).toContain("Context from the application");
|
|
316
|
+
expect(systemMessage.content).toContain("Application State");
|
|
317
|
+
|
|
318
|
+
// Check order: prompt, then context, then state
|
|
319
|
+
const promptIndex = systemMessage.content.indexOf("You are helpful.");
|
|
320
|
+
const contextIndex = systemMessage.content.indexOf("Context from the application");
|
|
321
|
+
const stateIndex = systemMessage.content.indexOf("Application State");
|
|
322
|
+
|
|
323
|
+
expect(promptIndex).toBeLessThan(contextIndex);
|
|
324
|
+
expect(contextIndex).toBeLessThan(stateIndex);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
describe("Tool Configuration", () => {
|
|
329
|
+
it("should include tools from config", async () => {
|
|
330
|
+
const tool1 = defineTool({
|
|
331
|
+
name: "configTool",
|
|
332
|
+
description: "A config tool",
|
|
333
|
+
parameters: z.object({ input: z.string() }),
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const agent = new BasicAgent({
|
|
337
|
+
model: "openai/gpt-4o",
|
|
338
|
+
tools: [tool1],
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
vi.mocked(streamText).mockReturnValue(mockStreamTextResponse([finish()]) as any);
|
|
342
|
+
|
|
343
|
+
const input: RunAgentInput = {
|
|
344
|
+
threadId: "thread1",
|
|
345
|
+
runId: "run1",
|
|
346
|
+
messages: [],
|
|
347
|
+
tools: [],
|
|
348
|
+
context: [],
|
|
349
|
+
state: {},
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
await collectEvents(agent["run"](input));
|
|
353
|
+
|
|
354
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
355
|
+
expect(callArgs.tools).toHaveProperty("configTool");
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it("should merge config tools with input tools", async () => {
|
|
359
|
+
const configTool = defineTool({
|
|
360
|
+
name: "configTool",
|
|
361
|
+
description: "From config",
|
|
362
|
+
parameters: z.object({}),
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const agent = new BasicAgent({
|
|
366
|
+
model: "openai/gpt-4o",
|
|
367
|
+
tools: [configTool],
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
vi.mocked(streamText).mockReturnValue(mockStreamTextResponse([finish()]) as any);
|
|
371
|
+
|
|
372
|
+
const input: RunAgentInput = {
|
|
373
|
+
threadId: "thread1",
|
|
374
|
+
runId: "run1",
|
|
375
|
+
messages: [],
|
|
376
|
+
tools: [
|
|
377
|
+
{
|
|
378
|
+
name: "inputTool",
|
|
379
|
+
description: "From input",
|
|
380
|
+
parameters: { type: "object", properties: {} },
|
|
381
|
+
},
|
|
382
|
+
],
|
|
383
|
+
context: [],
|
|
384
|
+
state: {},
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
await collectEvents(agent["run"](input));
|
|
388
|
+
|
|
389
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
390
|
+
expect(callArgs.tools).toHaveProperty("configTool");
|
|
391
|
+
expect(callArgs.tools).toHaveProperty("inputTool");
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("should always include state update tools", async () => {
|
|
395
|
+
const agent = new BasicAgent({
|
|
396
|
+
model: "openai/gpt-4o",
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
vi.mocked(streamText).mockReturnValue(mockStreamTextResponse([finish()]) as any);
|
|
400
|
+
|
|
401
|
+
const input: RunAgentInput = {
|
|
402
|
+
threadId: "thread1",
|
|
403
|
+
runId: "run1",
|
|
404
|
+
messages: [],
|
|
405
|
+
tools: [],
|
|
406
|
+
context: [],
|
|
407
|
+
state: {},
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
await collectEvents(agent["run"](input));
|
|
411
|
+
|
|
412
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
413
|
+
expect(callArgs.tools).toHaveProperty("AGUISendStateSnapshot");
|
|
414
|
+
expect(callArgs.tools).toHaveProperty("AGUISendStateDelta");
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
describe("Property Overrides", () => {
|
|
419
|
+
it("should respect overridable properties", async () => {
|
|
420
|
+
const agent = new BasicAgent({
|
|
421
|
+
model: "openai/gpt-4o",
|
|
422
|
+
temperature: 0.5,
|
|
423
|
+
overridableProperties: ["temperature"],
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
vi.mocked(streamText).mockReturnValue(mockStreamTextResponse([finish()]) as any);
|
|
427
|
+
|
|
428
|
+
const input: RunAgentInput = {
|
|
429
|
+
threadId: "thread1",
|
|
430
|
+
runId: "run1",
|
|
431
|
+
messages: [],
|
|
432
|
+
tools: [],
|
|
433
|
+
context: [],
|
|
434
|
+
state: {},
|
|
435
|
+
forwardedProps: { temperature: 0.9 },
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
await collectEvents(agent["run"](input));
|
|
439
|
+
|
|
440
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
441
|
+
expect(callArgs.temperature).toBe(0.9);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it("should ignore non-overridable properties", async () => {
|
|
445
|
+
const agent = new BasicAgent({
|
|
446
|
+
model: "openai/gpt-4o",
|
|
447
|
+
temperature: 0.5,
|
|
448
|
+
overridableProperties: [], // No properties can be overridden
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
vi.mocked(streamText).mockReturnValue(mockStreamTextResponse([finish()]) as any);
|
|
452
|
+
|
|
453
|
+
const input: RunAgentInput = {
|
|
454
|
+
threadId: "thread1",
|
|
455
|
+
runId: "run1",
|
|
456
|
+
messages: [],
|
|
457
|
+
tools: [],
|
|
458
|
+
context: [],
|
|
459
|
+
state: {},
|
|
460
|
+
forwardedProps: { temperature: 0.9 },
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
await collectEvents(agent["run"](input));
|
|
464
|
+
|
|
465
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
466
|
+
expect(callArgs.temperature).toBe(0.5); // Original value, not overridden
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
describe("Error Handling", () => {
|
|
471
|
+
it("should emit RUN_ERROR event on failure", async () => {
|
|
472
|
+
const agent = new BasicAgent({
|
|
473
|
+
model: "openai/gpt-4o",
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
vi.mocked(streamText).mockImplementation(() => {
|
|
477
|
+
throw new Error("Test error");
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
const input: RunAgentInput = {
|
|
481
|
+
threadId: "thread1",
|
|
482
|
+
runId: "run1",
|
|
483
|
+
messages: [],
|
|
484
|
+
tools: [],
|
|
485
|
+
context: [],
|
|
486
|
+
state: {},
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
try {
|
|
490
|
+
await collectEvents(agent["run"](input));
|
|
491
|
+
expect.fail("Should have thrown");
|
|
492
|
+
} catch (error: any) {
|
|
493
|
+
// Error is expected - check that we got a RUN_ERROR event
|
|
494
|
+
// Note: The error is thrown after emitting the event
|
|
495
|
+
expect(error.message).toContain("Test error");
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
});
|