@livekit/agents 1.0.14 → 1.0.16

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.
Files changed (133) hide show
  1. package/dist/cli.cjs +12 -12
  2. package/dist/cli.cjs.map +1 -1
  3. package/dist/cli.d.cts +3 -3
  4. package/dist/cli.d.ts +3 -3
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +13 -13
  7. package/dist/cli.js.map +1 -1
  8. package/dist/inference/stt.cjs.map +1 -1
  9. package/dist/inference/stt.d.ts.map +1 -1
  10. package/dist/inference/stt.js +1 -1
  11. package/dist/inference/stt.js.map +1 -1
  12. package/dist/inference/tts.cjs.map +1 -1
  13. package/dist/inference/tts.d.cts +2 -1
  14. package/dist/inference/tts.d.ts +2 -1
  15. package/dist/inference/tts.d.ts.map +1 -1
  16. package/dist/inference/tts.js +1 -5
  17. package/dist/inference/tts.js.map +1 -1
  18. package/dist/llm/chat_context.cjs +78 -0
  19. package/dist/llm/chat_context.cjs.map +1 -1
  20. package/dist/llm/chat_context.d.cts +16 -0
  21. package/dist/llm/chat_context.d.ts +16 -0
  22. package/dist/llm/chat_context.d.ts.map +1 -1
  23. package/dist/llm/chat_context.js +78 -0
  24. package/dist/llm/chat_context.js.map +1 -1
  25. package/dist/llm/chat_context.test.cjs +531 -0
  26. package/dist/llm/chat_context.test.cjs.map +1 -1
  27. package/dist/llm/chat_context.test.js +531 -0
  28. package/dist/llm/chat_context.test.js.map +1 -1
  29. package/dist/llm/tool_context.cjs +43 -2
  30. package/dist/llm/tool_context.cjs.map +1 -1
  31. package/dist/llm/tool_context.d.cts +39 -11
  32. package/dist/llm/tool_context.d.ts +39 -11
  33. package/dist/llm/tool_context.d.ts.map +1 -1
  34. package/dist/llm/tool_context.js +42 -3
  35. package/dist/llm/tool_context.js.map +1 -1
  36. package/dist/llm/tool_context.test.cjs +197 -0
  37. package/dist/llm/tool_context.test.cjs.map +1 -1
  38. package/dist/llm/tool_context.test.js +175 -0
  39. package/dist/llm/tool_context.test.js.map +1 -1
  40. package/dist/llm/utils.cjs +17 -11
  41. package/dist/llm/utils.cjs.map +1 -1
  42. package/dist/llm/utils.d.cts +1 -2
  43. package/dist/llm/utils.d.ts +1 -2
  44. package/dist/llm/utils.d.ts.map +1 -1
  45. package/dist/llm/utils.js +17 -11
  46. package/dist/llm/utils.js.map +1 -1
  47. package/dist/llm/zod-utils.cjs +99 -0
  48. package/dist/llm/zod-utils.cjs.map +1 -0
  49. package/dist/llm/zod-utils.d.cts +65 -0
  50. package/dist/llm/zod-utils.d.ts +65 -0
  51. package/dist/llm/zod-utils.d.ts.map +1 -0
  52. package/dist/llm/zod-utils.js +61 -0
  53. package/dist/llm/zod-utils.js.map +1 -0
  54. package/dist/llm/zod-utils.test.cjs +389 -0
  55. package/dist/llm/zod-utils.test.cjs.map +1 -0
  56. package/dist/llm/zod-utils.test.js +372 -0
  57. package/dist/llm/zod-utils.test.js.map +1 -0
  58. package/dist/metrics/base.cjs.map +1 -1
  59. package/dist/metrics/base.d.cts +7 -0
  60. package/dist/metrics/base.d.ts +7 -0
  61. package/dist/metrics/base.d.ts.map +1 -1
  62. package/dist/stt/stt.cjs +1 -0
  63. package/dist/stt/stt.cjs.map +1 -1
  64. package/dist/stt/stt.d.cts +7 -1
  65. package/dist/stt/stt.d.ts +7 -1
  66. package/dist/stt/stt.d.ts.map +1 -1
  67. package/dist/stt/stt.js +1 -0
  68. package/dist/stt/stt.js.map +1 -1
  69. package/dist/vad.cjs +16 -0
  70. package/dist/vad.cjs.map +1 -1
  71. package/dist/vad.d.cts +6 -0
  72. package/dist/vad.d.ts +6 -0
  73. package/dist/vad.d.ts.map +1 -1
  74. package/dist/vad.js +16 -0
  75. package/dist/vad.js.map +1 -1
  76. package/dist/voice/agent_activity.cjs +83 -8
  77. package/dist/voice/agent_activity.cjs.map +1 -1
  78. package/dist/voice/agent_activity.d.cts +6 -2
  79. package/dist/voice/agent_activity.d.ts +6 -2
  80. package/dist/voice/agent_activity.d.ts.map +1 -1
  81. package/dist/voice/agent_activity.js +83 -8
  82. package/dist/voice/agent_activity.js.map +1 -1
  83. package/dist/voice/agent_session.cjs +3 -2
  84. package/dist/voice/agent_session.cjs.map +1 -1
  85. package/dist/voice/agent_session.d.cts +2 -1
  86. package/dist/voice/agent_session.d.ts +2 -1
  87. package/dist/voice/agent_session.d.ts.map +1 -1
  88. package/dist/voice/agent_session.js +3 -2
  89. package/dist/voice/agent_session.js.map +1 -1
  90. package/dist/voice/audio_recognition.cjs +138 -16
  91. package/dist/voice/audio_recognition.cjs.map +1 -1
  92. package/dist/voice/audio_recognition.d.cts +11 -0
  93. package/dist/voice/audio_recognition.d.ts +11 -0
  94. package/dist/voice/audio_recognition.d.ts.map +1 -1
  95. package/dist/voice/audio_recognition.js +138 -16
  96. package/dist/voice/audio_recognition.js.map +1 -1
  97. package/dist/voice/generation.cjs +8 -3
  98. package/dist/voice/generation.cjs.map +1 -1
  99. package/dist/voice/generation.d.ts.map +1 -1
  100. package/dist/voice/generation.js +8 -3
  101. package/dist/voice/generation.js.map +1 -1
  102. package/dist/voice/room_io/_input.cjs.map +1 -1
  103. package/dist/voice/room_io/_input.d.ts.map +1 -1
  104. package/dist/voice/room_io/_input.js +0 -1
  105. package/dist/voice/room_io/_input.js.map +1 -1
  106. package/dist/worker.cjs +17 -11
  107. package/dist/worker.cjs.map +1 -1
  108. package/dist/worker.d.cts +16 -9
  109. package/dist/worker.d.ts +16 -9
  110. package/dist/worker.d.ts.map +1 -1
  111. package/dist/worker.js +16 -12
  112. package/dist/worker.js.map +1 -1
  113. package/package.json +5 -4
  114. package/src/cli.ts +17 -17
  115. package/src/inference/stt.ts +2 -1
  116. package/src/inference/tts.ts +2 -5
  117. package/src/llm/__snapshots__/zod-utils.test.ts.snap +341 -0
  118. package/src/llm/chat_context.test.ts +607 -0
  119. package/src/llm/chat_context.ts +106 -0
  120. package/src/llm/tool_context.test.ts +210 -1
  121. package/src/llm/tool_context.ts +101 -17
  122. package/src/llm/utils.ts +18 -15
  123. package/src/llm/zod-utils.test.ts +476 -0
  124. package/src/llm/zod-utils.ts +144 -0
  125. package/src/metrics/base.ts +7 -0
  126. package/src/stt/stt.ts +6 -0
  127. package/src/vad.ts +18 -0
  128. package/src/voice/agent_activity.ts +119 -9
  129. package/src/voice/agent_session.ts +3 -1
  130. package/src/voice/audio_recognition.ts +235 -57
  131. package/src/voice/generation.ts +8 -3
  132. package/src/voice/room_io/_input.ts +1 -1
  133. package/src/worker.ts +29 -18
@@ -3,6 +3,8 @@
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { describe, expect, it } from 'vitest';
5
5
  import { z } from 'zod';
6
+ import * as z3 from 'zod/v3';
7
+ import * as z4 from 'zod/v4';
6
8
  import { type ToolOptions, tool } from './tool_context.js';
7
9
  import { createToolOptions, oaiParams } from './utils.js';
8
10
 
@@ -164,12 +166,43 @@ describe('Tool Context', () => {
164
166
  expect(simpleAction.type).toBe('function');
165
167
  expect(simpleAction.description).toBe('Perform a simple action');
166
168
  expect(simpleAction.parameters).toBeDefined();
167
- expect(simpleAction.parameters._def.typeName).toBe('ZodObject');
169
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
170
+ expect((simpleAction.parameters as any)._def.typeName).toBe('ZodObject');
168
171
 
169
172
  const result = await simpleAction.execute({}, createToolOptions('123'));
170
173
  expect(result).toBe('Action performed');
171
174
  });
172
175
 
176
+ it('should support .optional() fields in tool parameters', async () => {
177
+ const weatherTool = tool({
178
+ description: 'Get weather information',
179
+ parameters: z.object({
180
+ location: z.string().describe('The city or location').optional(),
181
+ units: z.enum(['celsius', 'fahrenheit']).describe('Temperature units').optional(),
182
+ }),
183
+ execute: async ({ location, units }) => {
184
+ const loc = location ?? 'Unknown';
185
+ const unit = units ?? 'celsius';
186
+ return `Weather in ${loc} (${unit})`;
187
+ },
188
+ });
189
+
190
+ expect(weatherTool.type).toBe('function');
191
+ expect(weatherTool.description).toBe('Get weather information');
192
+
193
+ const result1 = await weatherTool.execute(
194
+ { location: 'London', units: 'celsius' },
195
+ createToolOptions('123'),
196
+ );
197
+ expect(result1).toBe('Weather in London (celsius)');
198
+
199
+ const result2 = await weatherTool.execute({}, createToolOptions('123'));
200
+ expect(result2).toBe('Weather in Unknown (celsius)');
201
+
202
+ const result3 = await weatherTool.execute({ location: 'Paris' }, createToolOptions('123'));
203
+ expect(result3).toBe('Weather in Paris (celsius)');
204
+ });
205
+
173
206
  it('should handle tools with context but no parameters', async () => {
174
207
  const greetUser = tool({
175
208
  description: 'Greet the current user',
@@ -194,5 +227,181 @@ describe('Tool Context', () => {
194
227
  expect(result).toBe('Tool call ID: test-id-456');
195
228
  });
196
229
  });
230
+
231
+ describe('Zod v3 and v4 compatibility', () => {
232
+ it('should work with Zod v3 schemas', async () => {
233
+ const v3Tool = tool({
234
+ description: 'A tool using Zod v3 schema',
235
+ parameters: z3.object({
236
+ name: z3.string(),
237
+ count: z3.number(),
238
+ }),
239
+ execute: async ({ name, count }) => {
240
+ return `${name}: ${count}`;
241
+ },
242
+ });
243
+
244
+ const result = await v3Tool.execute(
245
+ { name: 'Test', count: 42 },
246
+ createToolOptions('v3-test'),
247
+ );
248
+ expect(result).toBe('Test: 42');
249
+ });
250
+
251
+ it('should work with Zod v4 schemas', async () => {
252
+ const v4Tool = tool({
253
+ description: 'A tool using Zod v4 schema',
254
+ parameters: z4.object({
255
+ name: z4.string(),
256
+ count: z4.number(),
257
+ }),
258
+ execute: async ({ name, count }) => {
259
+ return `${name}: ${count}`;
260
+ },
261
+ });
262
+
263
+ const result = await v4Tool.execute(
264
+ { name: 'Test', count: 42 },
265
+ createToolOptions('v4-test'),
266
+ );
267
+ expect(result).toBe('Test: 42');
268
+ });
269
+
270
+ it('should handle v4 schemas with optional fields', async () => {
271
+ const v4Tool = tool({
272
+ description: 'Tool with optional field using v4',
273
+ parameters: z4.object({
274
+ required: z4.string(),
275
+ optional: z4.string().optional(),
276
+ }),
277
+ execute: async ({ required, optional }) => {
278
+ return optional ? `${required} - ${optional}` : required;
279
+ },
280
+ });
281
+
282
+ const result1 = await v4Tool.execute({ required: 'Hello' }, createToolOptions('test-1'));
283
+ expect(result1).toBe('Hello');
284
+
285
+ const result2 = await v4Tool.execute(
286
+ { required: 'Hello', optional: 'World' },
287
+ createToolOptions('test-2'),
288
+ );
289
+ expect(result2).toBe('Hello - World');
290
+ });
291
+
292
+ it('should handle v4 enum schemas', async () => {
293
+ const v4Tool = tool({
294
+ description: 'Tool with enum using v4',
295
+ parameters: z4.object({
296
+ color: z4.enum(['red', 'blue', 'green']),
297
+ }),
298
+ execute: async ({ color }) => {
299
+ return `Selected color: ${color}`;
300
+ },
301
+ });
302
+
303
+ const result = await v4Tool.execute({ color: 'blue' }, createToolOptions('test-enum'));
304
+ expect(result).toBe('Selected color: blue');
305
+ });
306
+
307
+ it('should handle v4 array schemas', async () => {
308
+ const v4Tool = tool({
309
+ description: 'Tool with array using v4',
310
+ parameters: z4.object({
311
+ tags: z4.array(z4.string()),
312
+ }),
313
+ execute: async ({ tags }) => {
314
+ return `Tags: ${tags.join(', ')}`;
315
+ },
316
+ });
317
+
318
+ const result = await v4Tool.execute(
319
+ { tags: ['nodejs', 'typescript', 'testing'] },
320
+ createToolOptions('test-array'),
321
+ );
322
+ expect(result).toBe('Tags: nodejs, typescript, testing');
323
+ });
324
+
325
+ it('should handle v4 nested object schemas', async () => {
326
+ const v4Tool = tool({
327
+ description: 'Tool with nested object using v4',
328
+ parameters: z4.object({
329
+ user: z4.object({
330
+ name: z4.string(),
331
+ email: z4.string(),
332
+ }),
333
+ }),
334
+ execute: async ({ user }) => {
335
+ return `${user.name} (${user.email})`;
336
+ },
337
+ });
338
+
339
+ const result = await v4Tool.execute(
340
+ { user: { name: 'John Doe', email: 'john@example.com' } },
341
+ createToolOptions('test-nested'),
342
+ );
343
+ expect(result).toBe('John Doe (john@example.com)');
344
+ });
345
+ });
346
+
347
+ describe('oaiParams with v4 schemas', () => {
348
+ it('should convert v4 basic object schema', () => {
349
+ const schema = z4.object({
350
+ name: z4.string().describe('User name'),
351
+ age: z4.number().describe('User age'),
352
+ });
353
+
354
+ const result = oaiParams(schema);
355
+
356
+ expect(result.type).toBe('object');
357
+ expect(result.properties).toHaveProperty('name');
358
+ expect(result.properties).toHaveProperty('age');
359
+ expect(result.required).toContain('name');
360
+ expect(result.required).toContain('age');
361
+ });
362
+
363
+ it('should handle v4 optional fields', () => {
364
+ const schema = z4.object({
365
+ required: z4.string(),
366
+ optional: z4.string().optional(),
367
+ });
368
+
369
+ const result = oaiParams(schema);
370
+
371
+ expect(result.required).toContain('required');
372
+ expect(result.required).not.toContain('optional');
373
+ });
374
+
375
+ it('should handle v4 enum fields', () => {
376
+ const schema = z4.object({
377
+ status: z4.enum(['pending', 'approved', 'rejected']),
378
+ });
379
+
380
+ const result = oaiParams(schema);
381
+
382
+ const properties = result.properties as Record<string, Record<string, unknown>>;
383
+ expect(properties.status?.enum).toEqual(['pending', 'approved', 'rejected']);
384
+ });
385
+
386
+ it('should handle v4 array fields', () => {
387
+ const schema = z4.object({
388
+ items: z4.array(z4.string()),
389
+ });
390
+
391
+ const result = oaiParams(schema);
392
+
393
+ const properties = result.properties as Record<string, any>;
394
+ expect(
395
+ properties.items && typeof properties.items === 'object'
396
+ ? properties.items.type
397
+ : undefined,
398
+ ).toBe('array');
399
+ expect(
400
+ properties.items && properties.items.items && typeof properties.items.items === 'object'
401
+ ? properties.items.items.type
402
+ : undefined,
403
+ ).toBe('string');
404
+ });
405
+ });
197
406
  });
198
407
  });
@@ -2,9 +2,10 @@
2
2
  //
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import type { JSONSchema7 } from 'json-schema';
5
- import { ZodObject, ZodType, z } from 'zod';
5
+ import { z } from 'zod';
6
6
  import type { Agent } from '../voice/agent.js';
7
7
  import type { RunContext, UnknownUserData } from '../voice/run_context.js';
8
+ import { isZodObjectSchema, isZodSchema } from './zod-utils.js';
8
9
 
9
10
  // heavily inspired by Vercel AI's `tool()`:
10
11
  // https://github.com/vercel/ai/blob/3b0983b/packages/ai/core/tool/tool.ts
@@ -23,9 +24,38 @@ export type JSONObject = {
23
24
  [key: string]: JSONValue;
24
25
  };
25
26
 
26
- // TODO(AJS-111): support Zod cross-version compatibility, raw JSON schema, both strict and non-strict versions
27
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
- export type ToolInputSchema<T extends JSONObject> = ZodObject<any, any, any, T, T> | JSONSchema7;
27
+ // Supports both Zod v3 and v4 schemas, as well as raw JSON schema
28
+ // Adapted from Vercel AI SDK's FlexibleSchema approach
29
+ // Source: https://github.com/vercel/ai/blob/main/packages/provider-utils/src/schema.ts#L67-L70
30
+ //
31
+ // Vercel uses StandardSchemaV1 from @standard-schema/spec package.
32
+ // We use a simpler approach by directly checking for schema properties:
33
+ // - Zod v3: Has `_output` property
34
+ // - Zod v4: Implements Standard Schema spec with `~standard` property
35
+ // - JSON Schema: Plain object fallback
36
+ export type ToolInputSchema<T = JSONObject> =
37
+ | {
38
+ // Zod v3 schema - has _output property for type inference
39
+ _output: T;
40
+ }
41
+ | {
42
+ // Zod v4 schema (Standard Schema) - has ~standard property
43
+ '~standard': {
44
+ types?: { output: T };
45
+ };
46
+ }
47
+ | JSONSchema7;
48
+
49
+ /**
50
+ * Infer the output type from a ToolInputSchema.
51
+ * Adapted from Vercel AI SDK's InferSchema type.
52
+ * Source: https://github.com/vercel/ai/blob/main/packages/provider-utils/src/schema.ts#L72-L79
53
+ */
54
+ export type InferToolInput<T> = T extends { _output: infer O }
55
+ ? O
56
+ : T extends { '~standard': { types?: { output: infer O } } }
57
+ ? O
58
+ : any; // eslint-disable-line @typescript-eslint/no-explicit-any -- Fallback type for JSON Schema objects without type inference
29
59
 
30
60
  export type ToolType = 'function' | 'provider-defined';
31
61
 
@@ -153,18 +183,59 @@ export interface FunctionTool<
153
183
 
154
184
  // TODO(AJS-112): support provider-defined tools in the future)
155
185
  export type ToolContext<UserData = UnknownUserData> = {
156
- [name: string]: FunctionTool<any, UserData, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
186
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Generic tool registry needs to accept any parameter/result types
187
+ [name: string]: FunctionTool<any, UserData, any>;
157
188
  };
158
189
 
190
+ export function isSameToolContext(ctx1: ToolContext, ctx2: ToolContext): boolean {
191
+ const toolNames = new Set(Object.keys(ctx1));
192
+ const toolNames2 = new Set(Object.keys(ctx2));
193
+
194
+ if (toolNames.size !== toolNames2.size) {
195
+ return false;
196
+ }
197
+
198
+ for (const name of toolNames) {
199
+ if (!toolNames2.has(name)) {
200
+ return false;
201
+ }
202
+
203
+ const tool1 = ctx1[name];
204
+ const tool2 = ctx2[name];
205
+
206
+ if (!tool1 || !tool2) {
207
+ return false;
208
+ }
209
+
210
+ if (tool1.description !== tool2.description) {
211
+ return false;
212
+ }
213
+ }
214
+
215
+ return true;
216
+ }
217
+
218
+ export function isSameToolChoice(choice1: ToolChoice | null, choice2: ToolChoice | null): boolean {
219
+ if (choice1 === choice2) {
220
+ return true;
221
+ }
222
+ if (choice1 === null || choice2 === null) {
223
+ return false;
224
+ }
225
+ if (typeof choice1 === 'string' && typeof choice2 === 'string') {
226
+ return choice1 === choice2;
227
+ }
228
+ if (typeof choice1 === 'object' && typeof choice2 === 'object') {
229
+ return choice1.type === choice2.type && choice1.function.name === choice2.function.name;
230
+ }
231
+ return false;
232
+ }
233
+
159
234
  /**
160
- * Create a function tool.
161
- *
162
- * @param description - The description of the tool.
163
- * @param parameters - The schema of the input that the tool expects. If not provided, defaults to z.object({}).
164
- * @param execute - The function that is called with the arguments from the tool call and produces a result.
235
+ * Create a function tool with inferred parameters from the schema.
165
236
  */
166
237
  export function tool<
167
- Parameters extends JSONObject = Record<string, never>,
238
+ Schema extends ToolInputSchema<any>, // eslint-disable-line @typescript-eslint/no-explicit-any -- Generic constraint needs to accept any JSONObject type
168
239
  UserData = UnknownUserData,
169
240
  Result = unknown,
170
241
  >({
@@ -173,9 +244,21 @@ export function tool<
173
244
  execute,
174
245
  }: {
175
246
  description: string;
176
- parameters?: ToolInputSchema<Parameters>;
177
- execute: ToolExecuteFunction<Parameters, UserData, Result>;
178
- }): FunctionTool<Parameters, UserData, Result>;
247
+ parameters: Schema;
248
+ execute: ToolExecuteFunction<InferToolInput<Schema>, UserData, Result>;
249
+ }): FunctionTool<InferToolInput<Schema>, UserData, Result>;
250
+
251
+ /**
252
+ * Create a function tool without parameters.
253
+ */
254
+ export function tool<UserData = UnknownUserData, Result = unknown>({
255
+ description,
256
+ execute,
257
+ }: {
258
+ description: string;
259
+ parameters?: never;
260
+ execute: ToolExecuteFunction<Record<string, never>, UserData, Result>;
261
+ }): FunctionTool<Record<string, never>, UserData, Result>;
179
262
 
180
263
  /**
181
264
  * Create a provider-defined tool.
@@ -197,12 +280,13 @@ export function tool(tool: any): any {
197
280
  // Default parameters to z.object({}) if not provided
198
281
  const parameters = tool.parameters ?? z.object({});
199
282
 
200
- // if parameters is not zod object, throw an error
201
- if (parameters instanceof ZodType && parameters._def.typeName !== 'ZodObject') {
283
+ // if parameters is a Zod schema, ensure it's an object schema
284
+ if (isZodSchema(parameters) && !isZodObjectSchema(parameters)) {
202
285
  throw new Error('Tool parameters must be a Zod object schema (z.object(...))');
203
286
  }
204
287
 
205
- if (!(parameters instanceof ZodObject) && !(typeof parameters === 'object')) {
288
+ // Ensure parameters is either a Zod schema or a plain object (JSON schema)
289
+ if (!isZodSchema(parameters) && !(typeof parameters === 'object')) {
206
290
  throw new Error('Tool parameters must be a Zod object schema or a raw JSON schema');
207
291
  }
208
292
 
package/src/llm/utils.ts CHANGED
@@ -4,8 +4,6 @@
4
4
  import { VideoBufferType, VideoFrame } from '@livekit/rtc-node';
5
5
  import type { JSONSchema7 } from 'json-schema';
6
6
  import sharp from 'sharp';
7
- import { ZodObject } from 'zod';
8
- import { zodToJsonSchema } from 'zod-to-json-schema';
9
7
  import type { UnknownUserData } from '../voice/run_context.js';
10
8
  import type { ChatContext } from './chat_context.js';
11
9
  import {
@@ -15,6 +13,7 @@ import {
15
13
  type ImageContent,
16
14
  } from './chat_context.js';
17
15
  import type { ToolContext, ToolInputSchema, ToolOptions } from './tool_context.js';
16
+ import { isZodSchema, parseZodSchema, zodSchemaToJsonSchema } from './zod-utils.js';
18
17
 
19
18
  export interface SerializedImage {
20
19
  inferenceDetail: 'auto' | 'high' | 'low';
@@ -151,15 +150,10 @@ export const createToolOptions = <UserData extends UnknownUserData>(
151
150
 
152
151
  /** @internal */
153
152
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
- export const oaiParams = (
155
- p: ZodObject<any>,
156
- isOpenai: boolean = true,
157
- ): OpenAIFunctionParameters => {
153
+ export const oaiParams = (schema: any, isOpenai: boolean = true): OpenAIFunctionParameters => {
158
154
  // Adapted from https://github.com/vercel/ai/blob/56eb0ee9/packages/provider-utils/src/zod-schema.ts
159
- const { properties, required, additionalProperties } = zodToJsonSchema(p, {
160
- // note: openai mode breaks various gemini conversions
161
- target: isOpenai ? 'openAi' : 'jsonSchema7',
162
- }) as OpenAIFunctionParameters;
155
+ const jsonSchema = zodSchemaToJsonSchema(schema, isOpenai);
156
+ const { properties, required, additionalProperties } = jsonSchema as OpenAIFunctionParameters;
163
157
 
164
158
  return {
165
159
  type: 'object',
@@ -209,8 +203,17 @@ export async function executeToolCall(
209
203
 
210
204
  // Ensure valid arguments schema
211
205
  try {
212
- if (tool.parameters instanceof ZodObject) {
213
- params = tool.parameters.parse(args);
206
+ if (isZodSchema(tool.parameters)) {
207
+ const result = await parseZodSchema<object>(tool.parameters, args);
208
+ if (result.success) {
209
+ params = result.data;
210
+ } else {
211
+ return FunctionCallOutput.create({
212
+ callId: toolCall.callId,
213
+ output: `Arguments parsing failed: ${result.error}`,
214
+ isError: true,
215
+ });
216
+ }
214
217
  } else {
215
218
  params = args;
216
219
  }
@@ -321,8 +324,8 @@ export function computeChatCtxDiff(oldCtx: ChatContext, newCtx: ChatContext): Di
321
324
  }
322
325
 
323
326
  export function toJsonSchema(schema: ToolInputSchema<any>, isOpenai: boolean = true): JSONSchema7 {
324
- if (schema instanceof ZodObject) {
325
- return oaiParams(schema, isOpenai);
327
+ if (isZodSchema(schema)) {
328
+ return zodSchemaToJsonSchema(schema, isOpenai);
326
329
  }
327
- return schema;
330
+ return schema as JSONSchema7;
328
331
  }