@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.
- package/dist/cli.cjs +12 -12
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +3 -3
- package/dist/cli.d.ts +3 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +13 -13
- package/dist/cli.js.map +1 -1
- package/dist/inference/stt.cjs.map +1 -1
- package/dist/inference/stt.d.ts.map +1 -1
- package/dist/inference/stt.js +1 -1
- package/dist/inference/stt.js.map +1 -1
- package/dist/inference/tts.cjs.map +1 -1
- package/dist/inference/tts.d.cts +2 -1
- package/dist/inference/tts.d.ts +2 -1
- package/dist/inference/tts.d.ts.map +1 -1
- package/dist/inference/tts.js +1 -5
- package/dist/inference/tts.js.map +1 -1
- package/dist/llm/chat_context.cjs +78 -0
- package/dist/llm/chat_context.cjs.map +1 -1
- package/dist/llm/chat_context.d.cts +16 -0
- package/dist/llm/chat_context.d.ts +16 -0
- package/dist/llm/chat_context.d.ts.map +1 -1
- package/dist/llm/chat_context.js +78 -0
- package/dist/llm/chat_context.js.map +1 -1
- package/dist/llm/chat_context.test.cjs +531 -0
- package/dist/llm/chat_context.test.cjs.map +1 -1
- package/dist/llm/chat_context.test.js +531 -0
- package/dist/llm/chat_context.test.js.map +1 -1
- package/dist/llm/tool_context.cjs +43 -2
- package/dist/llm/tool_context.cjs.map +1 -1
- package/dist/llm/tool_context.d.cts +39 -11
- package/dist/llm/tool_context.d.ts +39 -11
- package/dist/llm/tool_context.d.ts.map +1 -1
- package/dist/llm/tool_context.js +42 -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/metrics/base.cjs.map +1 -1
- package/dist/metrics/base.d.cts +7 -0
- package/dist/metrics/base.d.ts +7 -0
- package/dist/metrics/base.d.ts.map +1 -1
- package/dist/stt/stt.cjs +1 -0
- package/dist/stt/stt.cjs.map +1 -1
- package/dist/stt/stt.d.cts +7 -1
- package/dist/stt/stt.d.ts +7 -1
- package/dist/stt/stt.d.ts.map +1 -1
- package/dist/stt/stt.js +1 -0
- package/dist/stt/stt.js.map +1 -1
- 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/agent_activity.cjs +83 -8
- package/dist/voice/agent_activity.cjs.map +1 -1
- package/dist/voice/agent_activity.d.cts +6 -2
- package/dist/voice/agent_activity.d.ts +6 -2
- package/dist/voice/agent_activity.d.ts.map +1 -1
- package/dist/voice/agent_activity.js +83 -8
- package/dist/voice/agent_activity.js.map +1 -1
- package/dist/voice/agent_session.cjs +3 -2
- package/dist/voice/agent_session.cjs.map +1 -1
- package/dist/voice/agent_session.d.cts +2 -1
- package/dist/voice/agent_session.d.ts +2 -1
- package/dist/voice/agent_session.d.ts.map +1 -1
- package/dist/voice/agent_session.js +3 -2
- package/dist/voice/agent_session.js.map +1 -1
- package/dist/voice/audio_recognition.cjs +138 -16
- package/dist/voice/audio_recognition.cjs.map +1 -1
- package/dist/voice/audio_recognition.d.cts +11 -0
- package/dist/voice/audio_recognition.d.ts +11 -0
- package/dist/voice/audio_recognition.d.ts.map +1 -1
- package/dist/voice/audio_recognition.js +138 -16
- package/dist/voice/audio_recognition.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/dist/voice/room_io/_input.cjs.map +1 -1
- package/dist/voice/room_io/_input.d.ts.map +1 -1
- package/dist/voice/room_io/_input.js +0 -1
- package/dist/voice/room_io/_input.js.map +1 -1
- package/dist/worker.cjs +17 -11
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +16 -9
- package/dist/worker.d.ts +16 -9
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +16 -12
- package/dist/worker.js.map +1 -1
- package/package.json +5 -4
- package/src/cli.ts +17 -17
- package/src/inference/stt.ts +2 -1
- package/src/inference/tts.ts +2 -5
- package/src/llm/__snapshots__/zod-utils.test.ts.snap +341 -0
- package/src/llm/chat_context.test.ts +607 -0
- package/src/llm/chat_context.ts +106 -0
- package/src/llm/tool_context.test.ts +210 -1
- package/src/llm/tool_context.ts +101 -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/metrics/base.ts +7 -0
- package/src/stt/stt.ts +6 -0
- package/src/vad.ts +18 -0
- package/src/voice/agent_activity.ts +119 -9
- package/src/voice/agent_session.ts +3 -1
- package/src/voice/audio_recognition.ts +235 -57
- package/src/voice/generation.ts +8 -3
- package/src/voice/room_io/_input.ts +1 -1
- 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
|
-
|
|
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,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
|
-
|
|
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
|
-
|
|
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
|
|
177
|
-
execute: ToolExecuteFunction<
|
|
178
|
-
}): FunctionTool<
|
|
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
|
|
201
|
-
if (parameters
|
|
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
|
-
|
|
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
|
|
160
|
-
|
|
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
|
|
213
|
-
|
|
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
|
|
325
|
-
return
|
|
327
|
+
if (isZodSchema(schema)) {
|
|
328
|
+
return zodSchemaToJsonSchema(schema, isOpenai);
|
|
326
329
|
}
|
|
327
|
-
return schema;
|
|
330
|
+
return schema as JSONSchema7;
|
|
328
331
|
}
|