@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.
- package/dist/llm/tool_context.cjs +3 -2
- package/dist/llm/tool_context.cjs.map +1 -1
- package/dist/llm/tool_context.d.cts +37 -11
- package/dist/llm/tool_context.d.ts +37 -11
- package/dist/llm/tool_context.d.ts.map +1 -1
- package/dist/llm/tool_context.js +4 -3
- package/dist/llm/tool_context.js.map +1 -1
- package/dist/llm/tool_context.test.cjs +197 -0
- package/dist/llm/tool_context.test.cjs.map +1 -1
- package/dist/llm/tool_context.test.js +175 -0
- package/dist/llm/tool_context.test.js.map +1 -1
- package/dist/llm/utils.cjs +17 -11
- package/dist/llm/utils.cjs.map +1 -1
- package/dist/llm/utils.d.cts +1 -2
- package/dist/llm/utils.d.ts +1 -2
- package/dist/llm/utils.d.ts.map +1 -1
- package/dist/llm/utils.js +17 -11
- package/dist/llm/utils.js.map +1 -1
- package/dist/llm/zod-utils.cjs +99 -0
- package/dist/llm/zod-utils.cjs.map +1 -0
- package/dist/llm/zod-utils.d.cts +65 -0
- package/dist/llm/zod-utils.d.ts +65 -0
- package/dist/llm/zod-utils.d.ts.map +1 -0
- package/dist/llm/zod-utils.js +61 -0
- package/dist/llm/zod-utils.js.map +1 -0
- package/dist/llm/zod-utils.test.cjs +389 -0
- package/dist/llm/zod-utils.test.cjs.map +1 -0
- package/dist/llm/zod-utils.test.js +372 -0
- package/dist/llm/zod-utils.test.js.map +1 -0
- package/dist/vad.cjs +16 -0
- package/dist/vad.cjs.map +1 -1
- package/dist/vad.d.cts +6 -0
- package/dist/vad.d.ts +6 -0
- package/dist/vad.d.ts.map +1 -1
- package/dist/vad.js +16 -0
- package/dist/vad.js.map +1 -1
- package/dist/voice/generation.cjs +8 -3
- package/dist/voice/generation.cjs.map +1 -1
- package/dist/voice/generation.d.ts.map +1 -1
- package/dist/voice/generation.js +8 -3
- package/dist/voice/generation.js.map +1 -1
- package/package.json +5 -4
- package/src/llm/__snapshots__/zod-utils.test.ts.snap +341 -0
- package/src/llm/tool_context.test.ts +210 -1
- package/src/llm/tool_context.ts +57 -17
- package/src/llm/utils.ts +18 -15
- package/src/llm/zod-utils.test.ts +476 -0
- package/src/llm/zod-utils.ts +144 -0
- package/src/vad.ts +18 -0
- 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
|
-
|
|
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
|
});
|
package/src/llm/tool_context.ts
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
import type { JSONSchema7 } from 'json-schema';
|
|
5
|
-
import {
|
|
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
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
177
|
-
execute: ToolExecuteFunction<
|
|
178
|
-
}): FunctionTool<
|
|
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
|
|
201
|
-
if (parameters
|
|
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
|
-
|
|
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
|
|