@livekit/agents 1.0.14 → 1.0.15

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 (50) hide show
  1. package/dist/llm/tool_context.cjs +3 -2
  2. package/dist/llm/tool_context.cjs.map +1 -1
  3. package/dist/llm/tool_context.d.cts +37 -11
  4. package/dist/llm/tool_context.d.ts +37 -11
  5. package/dist/llm/tool_context.d.ts.map +1 -1
  6. package/dist/llm/tool_context.js +4 -3
  7. package/dist/llm/tool_context.js.map +1 -1
  8. package/dist/llm/tool_context.test.cjs +197 -0
  9. package/dist/llm/tool_context.test.cjs.map +1 -1
  10. package/dist/llm/tool_context.test.js +175 -0
  11. package/dist/llm/tool_context.test.js.map +1 -1
  12. package/dist/llm/utils.cjs +17 -11
  13. package/dist/llm/utils.cjs.map +1 -1
  14. package/dist/llm/utils.d.cts +1 -2
  15. package/dist/llm/utils.d.ts +1 -2
  16. package/dist/llm/utils.d.ts.map +1 -1
  17. package/dist/llm/utils.js +17 -11
  18. package/dist/llm/utils.js.map +1 -1
  19. package/dist/llm/zod-utils.cjs +99 -0
  20. package/dist/llm/zod-utils.cjs.map +1 -0
  21. package/dist/llm/zod-utils.d.cts +65 -0
  22. package/dist/llm/zod-utils.d.ts +65 -0
  23. package/dist/llm/zod-utils.d.ts.map +1 -0
  24. package/dist/llm/zod-utils.js +61 -0
  25. package/dist/llm/zod-utils.js.map +1 -0
  26. package/dist/llm/zod-utils.test.cjs +389 -0
  27. package/dist/llm/zod-utils.test.cjs.map +1 -0
  28. package/dist/llm/zod-utils.test.js +372 -0
  29. package/dist/llm/zod-utils.test.js.map +1 -0
  30. package/dist/vad.cjs +16 -0
  31. package/dist/vad.cjs.map +1 -1
  32. package/dist/vad.d.cts +6 -0
  33. package/dist/vad.d.ts +6 -0
  34. package/dist/vad.d.ts.map +1 -1
  35. package/dist/vad.js +16 -0
  36. package/dist/vad.js.map +1 -1
  37. package/dist/voice/generation.cjs +8 -3
  38. package/dist/voice/generation.cjs.map +1 -1
  39. package/dist/voice/generation.d.ts.map +1 -1
  40. package/dist/voice/generation.js +8 -3
  41. package/dist/voice/generation.js.map +1 -1
  42. package/package.json +5 -4
  43. package/src/llm/__snapshots__/zod-utils.test.ts.snap +341 -0
  44. package/src/llm/tool_context.test.ts +210 -1
  45. package/src/llm/tool_context.ts +57 -17
  46. package/src/llm/utils.ts +18 -15
  47. package/src/llm/zod-utils.test.ts +476 -0
  48. package/src/llm/zod-utils.ts +144 -0
  49. package/src/vad.ts +18 -0
  50. package/src/voice/generation.ts +8 -3
@@ -0,0 +1,341 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v3 schemas > should convert basic v3 object schema to JSON Schema 1`] = `
4
+ {
5
+ "$schema": "https://json-schema.org/draft/2019-09/schema#",
6
+ "additionalProperties": false,
7
+ "properties": {
8
+ "age": {
9
+ "type": "number",
10
+ },
11
+ "name": {
12
+ "type": "string",
13
+ },
14
+ },
15
+ "required": [
16
+ "name",
17
+ "age",
18
+ ],
19
+ "type": "object",
20
+ }
21
+ `;
22
+
23
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v3 schemas > should handle v3 array schemas 1`] = `
24
+ {
25
+ "$schema": "https://json-schema.org/draft/2019-09/schema#",
26
+ "additionalProperties": false,
27
+ "properties": {
28
+ "tags": {
29
+ "items": {
30
+ "type": "string",
31
+ },
32
+ "type": "array",
33
+ },
34
+ },
35
+ "required": [
36
+ "tags",
37
+ ],
38
+ "type": "object",
39
+ }
40
+ `;
41
+
42
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v3 schemas > should handle v3 enum schemas 1`] = `
43
+ {
44
+ "$schema": "https://json-schema.org/draft/2019-09/schema#",
45
+ "additionalProperties": false,
46
+ "properties": {
47
+ "color": {
48
+ "enum": [
49
+ "red",
50
+ "blue",
51
+ "green",
52
+ ],
53
+ "type": "string",
54
+ },
55
+ },
56
+ "required": [
57
+ "color",
58
+ ],
59
+ "type": "object",
60
+ }
61
+ `;
62
+
63
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v3 schemas > should handle v3 nested object schemas 1`] = `
64
+ {
65
+ "$schema": "https://json-schema.org/draft/2019-09/schema#",
66
+ "additionalProperties": false,
67
+ "properties": {
68
+ "user": {
69
+ "additionalProperties": false,
70
+ "properties": {
71
+ "email": {
72
+ "type": "string",
73
+ },
74
+ "name": {
75
+ "type": "string",
76
+ },
77
+ },
78
+ "required": [
79
+ "name",
80
+ "email",
81
+ ],
82
+ "type": "object",
83
+ },
84
+ },
85
+ "required": [
86
+ "user",
87
+ ],
88
+ "type": "object",
89
+ }
90
+ `;
91
+
92
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v3 schemas > should handle v3 schemas with default values 1`] = `
93
+ {
94
+ "$schema": "https://json-schema.org/draft/2019-09/schema#",
95
+ "additionalProperties": false,
96
+ "properties": {
97
+ "active": {
98
+ "anyOf": [
99
+ {
100
+ "default": true,
101
+ "type": "boolean",
102
+ },
103
+ {
104
+ "type": "null",
105
+ },
106
+ ],
107
+ },
108
+ "name": {
109
+ "type": "string",
110
+ },
111
+ "role": {
112
+ "anyOf": [
113
+ {
114
+ "default": "user",
115
+ "type": "string",
116
+ },
117
+ {
118
+ "type": "null",
119
+ },
120
+ ],
121
+ },
122
+ },
123
+ "required": [
124
+ "name",
125
+ "role",
126
+ "active",
127
+ ],
128
+ "type": "object",
129
+ }
130
+ `;
131
+
132
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v3 schemas > should handle v3 schemas with descriptions 1`] = `
133
+ {
134
+ "$schema": "https://json-schema.org/draft/2019-09/schema#",
135
+ "additionalProperties": false,
136
+ "properties": {
137
+ "location": {
138
+ "description": "The location to search",
139
+ "type": "string",
140
+ },
141
+ },
142
+ "required": [
143
+ "location",
144
+ ],
145
+ "type": "object",
146
+ }
147
+ `;
148
+
149
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v3 schemas > should handle v3 schemas with multiple optional fields 1`] = `
150
+ {
151
+ "$schema": "https://json-schema.org/draft/2019-09/schema#",
152
+ "additionalProperties": false,
153
+ "properties": {
154
+ "age": {
155
+ "type": [
156
+ "number",
157
+ "null",
158
+ ],
159
+ },
160
+ "email": {
161
+ "type": "string",
162
+ },
163
+ "id": {
164
+ "type": "string",
165
+ },
166
+ "name": {
167
+ "type": [
168
+ "string",
169
+ "null",
170
+ ],
171
+ },
172
+ },
173
+ "required": [
174
+ "id",
175
+ "name",
176
+ "age",
177
+ "email",
178
+ ],
179
+ "type": "object",
180
+ }
181
+ `;
182
+
183
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v4 schemas > should convert basic v4 object schema to JSON Schema 1`] = `
184
+ {
185
+ "$schema": "http://json-schema.org/draft-07/schema#",
186
+ "additionalProperties": false,
187
+ "properties": {
188
+ "age": {
189
+ "type": "number",
190
+ },
191
+ "name": {
192
+ "type": "string",
193
+ },
194
+ },
195
+ "required": [
196
+ "name",
197
+ "age",
198
+ ],
199
+ "type": "object",
200
+ }
201
+ `;
202
+
203
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v4 schemas > should handle v4 array schemas 1`] = `
204
+ {
205
+ "$schema": "http://json-schema.org/draft-07/schema#",
206
+ "additionalProperties": false,
207
+ "properties": {
208
+ "tags": {
209
+ "items": {
210
+ "type": "string",
211
+ },
212
+ "type": "array",
213
+ },
214
+ },
215
+ "required": [
216
+ "tags",
217
+ ],
218
+ "type": "object",
219
+ }
220
+ `;
221
+
222
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v4 schemas > should handle v4 enum schemas 1`] = `
223
+ {
224
+ "$schema": "http://json-schema.org/draft-07/schema#",
225
+ "additionalProperties": false,
226
+ "properties": {
227
+ "color": {
228
+ "enum": [
229
+ "red",
230
+ "blue",
231
+ "green",
232
+ ],
233
+ "type": "string",
234
+ },
235
+ },
236
+ "required": [
237
+ "color",
238
+ ],
239
+ "type": "object",
240
+ }
241
+ `;
242
+
243
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v4 schemas > should handle v4 nested object schemas 1`] = `
244
+ {
245
+ "$schema": "http://json-schema.org/draft-07/schema#",
246
+ "additionalProperties": false,
247
+ "properties": {
248
+ "user": {
249
+ "additionalProperties": false,
250
+ "properties": {
251
+ "email": {
252
+ "type": "string",
253
+ },
254
+ "name": {
255
+ "type": "string",
256
+ },
257
+ },
258
+ "required": [
259
+ "name",
260
+ "email",
261
+ ],
262
+ "type": "object",
263
+ },
264
+ },
265
+ "required": [
266
+ "user",
267
+ ],
268
+ "type": "object",
269
+ }
270
+ `;
271
+
272
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v4 schemas > should handle v4 schemas with default values 1`] = `
273
+ {
274
+ "$schema": "http://json-schema.org/draft-07/schema#",
275
+ "additionalProperties": false,
276
+ "properties": {
277
+ "active": {
278
+ "default": true,
279
+ "type": "boolean",
280
+ },
281
+ "name": {
282
+ "type": "string",
283
+ },
284
+ "role": {
285
+ "default": "user",
286
+ "type": "string",
287
+ },
288
+ },
289
+ "required": [
290
+ "name",
291
+ "role",
292
+ "active",
293
+ ],
294
+ "type": "object",
295
+ }
296
+ `;
297
+
298
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v4 schemas > should handle v4 schemas with multiple optional fields 1`] = `
299
+ {
300
+ "$schema": "http://json-schema.org/draft-07/schema#",
301
+ "additionalProperties": false,
302
+ "properties": {
303
+ "age": {
304
+ "type": "number",
305
+ },
306
+ "email": {
307
+ "type": "string",
308
+ },
309
+ "id": {
310
+ "type": "string",
311
+ },
312
+ "name": {
313
+ "type": "string",
314
+ },
315
+ },
316
+ "required": [
317
+ "id",
318
+ "email",
319
+ ],
320
+ "type": "object",
321
+ }
322
+ `;
323
+
324
+ exports[`Zod Utils > zodSchemaToJsonSchema > Zod v4 schemas > should handle v4 schemas with optional fields 1`] = `
325
+ {
326
+ "$schema": "http://json-schema.org/draft-07/schema#",
327
+ "additionalProperties": false,
328
+ "properties": {
329
+ "optional": {
330
+ "type": "string",
331
+ },
332
+ "required": {
333
+ "type": "string",
334
+ },
335
+ },
336
+ "required": [
337
+ "required",
338
+ ],
339
+ "type": "object",
340
+ }
341
+ `;
@@ -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,15 @@ 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
 
159
190
  /**
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.
191
+ * Create a function tool with inferred parameters from the schema.
165
192
  */
166
193
  export function tool<
167
- Parameters extends JSONObject = Record<string, never>,
194
+ Schema extends ToolInputSchema<any>, // eslint-disable-line @typescript-eslint/no-explicit-any -- Generic constraint needs to accept any JSONObject type
168
195
  UserData = UnknownUserData,
169
196
  Result = unknown,
170
197
  >({
@@ -173,9 +200,21 @@ export function tool<
173
200
  execute,
174
201
  }: {
175
202
  description: string;
176
- parameters?: ToolInputSchema<Parameters>;
177
- execute: ToolExecuteFunction<Parameters, UserData, Result>;
178
- }): FunctionTool<Parameters, UserData, Result>;
203
+ parameters: Schema;
204
+ execute: ToolExecuteFunction<InferToolInput<Schema>, UserData, Result>;
205
+ }): FunctionTool<InferToolInput<Schema>, UserData, Result>;
206
+
207
+ /**
208
+ * Create a function tool without parameters.
209
+ */
210
+ export function tool<UserData = UnknownUserData, Result = unknown>({
211
+ description,
212
+ execute,
213
+ }: {
214
+ description: string;
215
+ parameters?: never;
216
+ execute: ToolExecuteFunction<Record<string, never>, UserData, Result>;
217
+ }): FunctionTool<Record<string, never>, UserData, Result>;
179
218
 
180
219
  /**
181
220
  * Create a provider-defined tool.
@@ -197,12 +236,13 @@ export function tool(tool: any): any {
197
236
  // Default parameters to z.object({}) if not provided
198
237
  const parameters = tool.parameters ?? z.object({});
199
238
 
200
- // if parameters is not zod object, throw an error
201
- if (parameters instanceof ZodType && parameters._def.typeName !== 'ZodObject') {
239
+ // if parameters is a Zod schema, ensure it's an object schema
240
+ if (isZodSchema(parameters) && !isZodObjectSchema(parameters)) {
202
241
  throw new Error('Tool parameters must be a Zod object schema (z.object(...))');
203
242
  }
204
243
 
205
- if (!(parameters instanceof ZodObject) && !(typeof parameters === 'object')) {
244
+ // Ensure parameters is either a Zod schema or a plain object (JSON schema)
245
+ if (!isZodSchema(parameters) && !(typeof parameters === 'object')) {
206
246
  throw new Error('Tool parameters must be a Zod object schema or a raw JSON schema');
207
247
  }
208
248