@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.
@@ -0,0 +1,438 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { z } from "zod";
3
+ import {
4
+ resolveModel,
5
+ convertMessagesToVercelAISDKMessages,
6
+ convertJsonSchemaToZodSchema,
7
+ convertToolsToVercelAITools,
8
+ convertToolDefinitionsToVercelAITools,
9
+ defineTool,
10
+ type ToolDefinition,
11
+ } from "../index";
12
+ import type { Message } from "@ag-ui/client";
13
+
14
+ describe("resolveModel", () => {
15
+ const originalEnv = process.env;
16
+
17
+ beforeEach(() => {
18
+ process.env = { ...originalEnv };
19
+ process.env.OPENAI_API_KEY = "test-openai-key";
20
+ process.env.ANTHROPIC_API_KEY = "test-anthropic-key";
21
+ process.env.GOOGLE_API_KEY = "test-google-key";
22
+ });
23
+
24
+ afterEach(() => {
25
+ process.env = originalEnv;
26
+ });
27
+
28
+ it("should resolve OpenAI models with / separator", () => {
29
+ const model = resolveModel("openai/gpt-4o");
30
+ expect(model).toBeDefined();
31
+ expect(model.modelId).toBe("gpt-4o");
32
+ });
33
+
34
+ it("should resolve OpenAI models with : separator", () => {
35
+ const model = resolveModel("openai:gpt-4o-mini");
36
+ expect(model).toBeDefined();
37
+ expect(model.modelId).toBe("gpt-4o-mini");
38
+ });
39
+
40
+ it("should resolve Anthropic models", () => {
41
+ const model = resolveModel("anthropic/claude-sonnet-4.5");
42
+ expect(model).toBeDefined();
43
+ expect(model.modelId).toBe("claude-sonnet-4.5");
44
+ });
45
+
46
+ it("should resolve Google models", () => {
47
+ const model = resolveModel("google/gemini-2.5-pro");
48
+ expect(model).toBeDefined();
49
+ expect(model.modelId).toBe("gemini-2.5-pro");
50
+ });
51
+
52
+ it("should handle gemini provider alias", () => {
53
+ const model = resolveModel("gemini/gemini-2.5-flash");
54
+ expect(model).toBeDefined();
55
+ expect(model.modelId).toBe("gemini-2.5-flash");
56
+ });
57
+
58
+ it("should throw error for invalid format", () => {
59
+ expect(() => resolveModel("invalid")).toThrow("Invalid model string");
60
+ });
61
+
62
+ it("should throw error for unknown provider", () => {
63
+ expect(() => resolveModel("unknown/model")).toThrow("Unknown provider");
64
+ });
65
+
66
+ it("should pass through LanguageModel instances", () => {
67
+ const mockModel = resolveModel("openai/gpt-4o");
68
+ const result = resolveModel(mockModel as any);
69
+ expect(result).toBe(mockModel);
70
+ });
71
+ });
72
+
73
+ describe("convertMessagesToVercelAISDKMessages", () => {
74
+ it("should convert user messages", () => {
75
+ const messages: Message[] = [
76
+ {
77
+ id: "1",
78
+ role: "user",
79
+ content: "Hello",
80
+ },
81
+ ];
82
+
83
+ const result = convertMessagesToVercelAISDKMessages(messages);
84
+
85
+ expect(result).toEqual([
86
+ {
87
+ role: "user",
88
+ content: "Hello",
89
+ },
90
+ ]);
91
+ });
92
+
93
+ it("should convert assistant messages with text content", () => {
94
+ const messages: Message[] = [
95
+ {
96
+ id: "1",
97
+ role: "assistant",
98
+ content: "Hello there",
99
+ },
100
+ ];
101
+
102
+ const result = convertMessagesToVercelAISDKMessages(messages);
103
+
104
+ expect(result).toEqual([
105
+ {
106
+ role: "assistant",
107
+ content: [{ type: "text", text: "Hello there" }],
108
+ },
109
+ ]);
110
+ });
111
+
112
+ it("should convert assistant messages with tool calls", () => {
113
+ const messages: Message[] = [
114
+ {
115
+ id: "1",
116
+ role: "assistant",
117
+ content: "Let me help",
118
+ toolCalls: [
119
+ {
120
+ id: "call1",
121
+ type: "function",
122
+ function: {
123
+ name: "getTool",
124
+ arguments: '{"arg":"value"}',
125
+ },
126
+ },
127
+ ],
128
+ },
129
+ ];
130
+
131
+ const result = convertMessagesToVercelAISDKMessages(messages);
132
+
133
+ expect(result[0]).toEqual({
134
+ role: "assistant",
135
+ content: [
136
+ { type: "text", text: "Let me help" },
137
+ {
138
+ type: "tool-call",
139
+ toolCallId: "call1",
140
+ toolName: "getTool",
141
+ input: { arg: "value" },
142
+ },
143
+ ],
144
+ });
145
+ });
146
+
147
+ it("should convert tool messages", () => {
148
+ const messages: Message[] = [
149
+ {
150
+ id: "1",
151
+ role: "assistant",
152
+ toolCalls: [
153
+ {
154
+ id: "call1",
155
+ type: "function",
156
+ function: {
157
+ name: "getTool",
158
+ arguments: '{}',
159
+ },
160
+ },
161
+ ],
162
+ },
163
+ {
164
+ id: "2",
165
+ role: "tool",
166
+ content: "result",
167
+ toolCallId: "call1",
168
+ },
169
+ ];
170
+
171
+ const result = convertMessagesToVercelAISDKMessages(messages);
172
+
173
+ expect(result[1]).toEqual({
174
+ role: "tool",
175
+ content: [
176
+ {
177
+ type: "tool-result",
178
+ toolCallId: "call1",
179
+ toolName: "getTool",
180
+ output: {
181
+ type: "text",
182
+ value: "result",
183
+ },
184
+ },
185
+ ],
186
+ });
187
+ });
188
+
189
+ it("should handle multiple messages", () => {
190
+ const messages: Message[] = [
191
+ { id: "1", role: "user", content: "Hi" },
192
+ { id: "2", role: "assistant", content: "Hello" },
193
+ { id: "3", role: "user", content: "How are you?" },
194
+ ];
195
+
196
+ const result = convertMessagesToVercelAISDKMessages(messages);
197
+
198
+ expect(result).toHaveLength(3);
199
+ expect(result[0].role).toBe("user");
200
+ expect(result[1].role).toBe("assistant");
201
+ expect(result[2].role).toBe("user");
202
+ });
203
+ });
204
+
205
+ describe("convertJsonSchemaToZodSchema", () => {
206
+ it("should convert string schema", () => {
207
+ const jsonSchema = {
208
+ type: "string" as const,
209
+ description: "A string field",
210
+ };
211
+
212
+ const zodSchema = convertJsonSchemaToZodSchema(jsonSchema, true);
213
+ expect(zodSchema.parse("test")).toBe("test");
214
+ expect(() => zodSchema.parse(123)).toThrow();
215
+ });
216
+
217
+ it("should convert number schema", () => {
218
+ const jsonSchema = {
219
+ type: "number" as const,
220
+ description: "A number field",
221
+ };
222
+
223
+ const zodSchema = convertJsonSchemaToZodSchema(jsonSchema, true);
224
+ expect(zodSchema.parse(123)).toBe(123);
225
+ expect(() => zodSchema.parse("test")).toThrow();
226
+ });
227
+
228
+ it("should convert boolean schema", () => {
229
+ const jsonSchema = {
230
+ type: "boolean" as const,
231
+ description: "A boolean field",
232
+ };
233
+
234
+ const zodSchema = convertJsonSchemaToZodSchema(jsonSchema, true);
235
+ expect(zodSchema.parse(true)).toBe(true);
236
+ expect(() => zodSchema.parse("true")).toThrow();
237
+ });
238
+
239
+ it("should convert array schema", () => {
240
+ const jsonSchema = {
241
+ type: "array" as const,
242
+ description: "An array field",
243
+ items: {
244
+ type: "string" as const,
245
+ },
246
+ };
247
+
248
+ const zodSchema = convertJsonSchemaToZodSchema(jsonSchema, true);
249
+ expect(zodSchema.parse(["a", "b"])).toEqual(["a", "b"]);
250
+ expect(() => zodSchema.parse([1, 2])).toThrow();
251
+ });
252
+
253
+ it("should convert object schema with properties", () => {
254
+ const jsonSchema = {
255
+ type: "object" as const,
256
+ description: "An object",
257
+ properties: {
258
+ name: { type: "string" as const },
259
+ age: { type: "number" as const },
260
+ },
261
+ required: ["name"],
262
+ };
263
+
264
+ const zodSchema = convertJsonSchemaToZodSchema(jsonSchema, true);
265
+ const valid = zodSchema.parse({ name: "John", age: 30 });
266
+ expect(valid).toEqual({ name: "John", age: 30 });
267
+
268
+ expect(() => zodSchema.parse({ age: 30 })).toThrow();
269
+ });
270
+
271
+ it("should make schema optional when required is false", () => {
272
+ const jsonSchema = {
273
+ type: "string" as const,
274
+ description: "Optional string",
275
+ };
276
+
277
+ const zodSchema = convertJsonSchemaToZodSchema(jsonSchema, false);
278
+ expect(zodSchema.parse(undefined)).toBeUndefined();
279
+ expect(zodSchema.parse("test")).toBe("test");
280
+ });
281
+
282
+ it("should handle nested object schemas", () => {
283
+ const jsonSchema = {
284
+ type: "object" as const,
285
+ properties: {
286
+ user: {
287
+ type: "object" as const,
288
+ properties: {
289
+ name: { type: "string" as const },
290
+ },
291
+ required: ["name"],
292
+ },
293
+ },
294
+ required: ["user"],
295
+ };
296
+
297
+ const zodSchema = convertJsonSchemaToZodSchema(jsonSchema, true);
298
+ const valid = zodSchema.parse({ user: { name: "John" } });
299
+ expect(valid).toEqual({ user: { name: "John" } });
300
+ });
301
+ });
302
+
303
+ describe("convertToolsToVercelAITools", () => {
304
+ it("should convert AG-UI tools to Vercel AI tools", () => {
305
+ const tools = [
306
+ {
307
+ name: "testTool",
308
+ description: "A test tool",
309
+ parameters: {
310
+ type: "object" as const,
311
+ properties: {
312
+ input: { type: "string" as const },
313
+ },
314
+ required: ["input"],
315
+ },
316
+ },
317
+ ];
318
+
319
+ const result = convertToolsToVercelAITools(tools);
320
+
321
+ expect(result).toHaveProperty("testTool");
322
+ expect(result.testTool).toBeDefined();
323
+ });
324
+
325
+ it("should throw error for invalid JSON schema", () => {
326
+ const tools = [
327
+ {
328
+ name: "testTool",
329
+ description: "A test tool",
330
+ parameters: { invalid: "schema" } as any,
331
+ },
332
+ ];
333
+
334
+ expect(() => convertToolsToVercelAITools(tools)).toThrow("Invalid JSON schema");
335
+ });
336
+
337
+ it("should handle multiple tools", () => {
338
+ const tools = [
339
+ {
340
+ name: "tool1",
341
+ description: "First tool",
342
+ parameters: {
343
+ type: "object" as const,
344
+ properties: {},
345
+ },
346
+ },
347
+ {
348
+ name: "tool2",
349
+ description: "Second tool",
350
+ parameters: {
351
+ type: "object" as const,
352
+ properties: {},
353
+ },
354
+ },
355
+ ];
356
+
357
+ const result = convertToolsToVercelAITools(tools);
358
+
359
+ expect(result).toHaveProperty("tool1");
360
+ expect(result).toHaveProperty("tool2");
361
+ });
362
+ });
363
+
364
+ describe("convertToolDefinitionsToVercelAITools", () => {
365
+ it("should convert ToolDefinitions to Vercel AI tools", () => {
366
+ const tools: ToolDefinition[] = [
367
+ {
368
+ name: "testTool",
369
+ description: "A test tool",
370
+ parameters: z.object({
371
+ input: z.string(),
372
+ }),
373
+ },
374
+ ];
375
+
376
+ const result = convertToolDefinitionsToVercelAITools(tools);
377
+
378
+ expect(result).toHaveProperty("testTool");
379
+ expect(result.testTool).toBeDefined();
380
+ });
381
+
382
+ it("should handle multiple tool definitions", () => {
383
+ const tools: ToolDefinition[] = [
384
+ {
385
+ name: "tool1",
386
+ description: "First tool",
387
+ parameters: z.object({}),
388
+ },
389
+ {
390
+ name: "tool2",
391
+ description: "Second tool",
392
+ parameters: z.object({ value: z.number() }),
393
+ },
394
+ ];
395
+
396
+ const result = convertToolDefinitionsToVercelAITools(tools);
397
+
398
+ expect(result).toHaveProperty("tool1");
399
+ expect(result).toHaveProperty("tool2");
400
+ expect(Object.keys(result)).toHaveLength(2);
401
+ });
402
+ });
403
+
404
+ describe("defineTool", () => {
405
+ it("should create a ToolDefinition", () => {
406
+ const tool = defineTool({
407
+ name: "myTool",
408
+ description: "My test tool",
409
+ parameters: z.object({
410
+ input: z.string(),
411
+ }),
412
+ });
413
+
414
+ expect(tool).toEqual({
415
+ name: "myTool",
416
+ description: "My test tool",
417
+ parameters: expect.any(Object),
418
+ });
419
+
420
+ expect(tool.name).toBe("myTool");
421
+ expect(tool.description).toBe("My test tool");
422
+ });
423
+
424
+ it("should preserve type information", () => {
425
+ const tool = defineTool({
426
+ name: "calc",
427
+ description: "Calculate",
428
+ parameters: z.object({
429
+ a: z.number(),
430
+ b: z.number(),
431
+ }),
432
+ });
433
+
434
+ // Should be able to parse valid input
435
+ const result = tool.parameters.parse({ a: 1, b: 2 });
436
+ expect(result).toEqual({ a: 1, b: 2 });
437
+ });
438
+ });