@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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import * as z3 from "zod/v3";
|
|
4
|
+
import * as z4 from "zod/v4";
|
|
3
5
|
import { tool } from "./tool_context.js";
|
|
4
6
|
import { createToolOptions, oaiParams } from "./utils.js";
|
|
5
7
|
describe("Tool Context", () => {
|
|
@@ -135,6 +137,31 @@ describe("Tool Context", () => {
|
|
|
135
137
|
const result = await simpleAction.execute({}, createToolOptions("123"));
|
|
136
138
|
expect(result).toBe("Action performed");
|
|
137
139
|
});
|
|
140
|
+
it("should support .optional() fields in tool parameters", async () => {
|
|
141
|
+
const weatherTool = tool({
|
|
142
|
+
description: "Get weather information",
|
|
143
|
+
parameters: z.object({
|
|
144
|
+
location: z.string().describe("The city or location").optional(),
|
|
145
|
+
units: z.enum(["celsius", "fahrenheit"]).describe("Temperature units").optional()
|
|
146
|
+
}),
|
|
147
|
+
execute: async ({ location, units }) => {
|
|
148
|
+
const loc = location ?? "Unknown";
|
|
149
|
+
const unit = units ?? "celsius";
|
|
150
|
+
return `Weather in ${loc} (${unit})`;
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
expect(weatherTool.type).toBe("function");
|
|
154
|
+
expect(weatherTool.description).toBe("Get weather information");
|
|
155
|
+
const result1 = await weatherTool.execute(
|
|
156
|
+
{ location: "London", units: "celsius" },
|
|
157
|
+
createToolOptions("123")
|
|
158
|
+
);
|
|
159
|
+
expect(result1).toBe("Weather in London (celsius)");
|
|
160
|
+
const result2 = await weatherTool.execute({}, createToolOptions("123"));
|
|
161
|
+
expect(result2).toBe("Weather in Unknown (celsius)");
|
|
162
|
+
const result3 = await weatherTool.execute({ location: "Paris" }, createToolOptions("123"));
|
|
163
|
+
expect(result3).toBe("Weather in Paris (celsius)");
|
|
164
|
+
});
|
|
138
165
|
it("should handle tools with context but no parameters", async () => {
|
|
139
166
|
const greetUser = tool({
|
|
140
167
|
description: "Greet the current user",
|
|
@@ -156,6 +183,154 @@ describe("Tool Context", () => {
|
|
|
156
183
|
expect(result).toBe("Tool call ID: test-id-456");
|
|
157
184
|
});
|
|
158
185
|
});
|
|
186
|
+
describe("Zod v3 and v4 compatibility", () => {
|
|
187
|
+
it("should work with Zod v3 schemas", async () => {
|
|
188
|
+
const v3Tool = tool({
|
|
189
|
+
description: "A tool using Zod v3 schema",
|
|
190
|
+
parameters: z3.object({
|
|
191
|
+
name: z3.string(),
|
|
192
|
+
count: z3.number()
|
|
193
|
+
}),
|
|
194
|
+
execute: async ({ name, count }) => {
|
|
195
|
+
return `${name}: ${count}`;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
const result = await v3Tool.execute(
|
|
199
|
+
{ name: "Test", count: 42 },
|
|
200
|
+
createToolOptions("v3-test")
|
|
201
|
+
);
|
|
202
|
+
expect(result).toBe("Test: 42");
|
|
203
|
+
});
|
|
204
|
+
it("should work with Zod v4 schemas", async () => {
|
|
205
|
+
const v4Tool = tool({
|
|
206
|
+
description: "A tool using Zod v4 schema",
|
|
207
|
+
parameters: z4.object({
|
|
208
|
+
name: z4.string(),
|
|
209
|
+
count: z4.number()
|
|
210
|
+
}),
|
|
211
|
+
execute: async ({ name, count }) => {
|
|
212
|
+
return `${name}: ${count}`;
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
const result = await v4Tool.execute(
|
|
216
|
+
{ name: "Test", count: 42 },
|
|
217
|
+
createToolOptions("v4-test")
|
|
218
|
+
);
|
|
219
|
+
expect(result).toBe("Test: 42");
|
|
220
|
+
});
|
|
221
|
+
it("should handle v4 schemas with optional fields", async () => {
|
|
222
|
+
const v4Tool = tool({
|
|
223
|
+
description: "Tool with optional field using v4",
|
|
224
|
+
parameters: z4.object({
|
|
225
|
+
required: z4.string(),
|
|
226
|
+
optional: z4.string().optional()
|
|
227
|
+
}),
|
|
228
|
+
execute: async ({ required, optional }) => {
|
|
229
|
+
return optional ? `${required} - ${optional}` : required;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
const result1 = await v4Tool.execute({ required: "Hello" }, createToolOptions("test-1"));
|
|
233
|
+
expect(result1).toBe("Hello");
|
|
234
|
+
const result2 = await v4Tool.execute(
|
|
235
|
+
{ required: "Hello", optional: "World" },
|
|
236
|
+
createToolOptions("test-2")
|
|
237
|
+
);
|
|
238
|
+
expect(result2).toBe("Hello - World");
|
|
239
|
+
});
|
|
240
|
+
it("should handle v4 enum schemas", async () => {
|
|
241
|
+
const v4Tool = tool({
|
|
242
|
+
description: "Tool with enum using v4",
|
|
243
|
+
parameters: z4.object({
|
|
244
|
+
color: z4.enum(["red", "blue", "green"])
|
|
245
|
+
}),
|
|
246
|
+
execute: async ({ color }) => {
|
|
247
|
+
return `Selected color: ${color}`;
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
const result = await v4Tool.execute({ color: "blue" }, createToolOptions("test-enum"));
|
|
251
|
+
expect(result).toBe("Selected color: blue");
|
|
252
|
+
});
|
|
253
|
+
it("should handle v4 array schemas", async () => {
|
|
254
|
+
const v4Tool = tool({
|
|
255
|
+
description: "Tool with array using v4",
|
|
256
|
+
parameters: z4.object({
|
|
257
|
+
tags: z4.array(z4.string())
|
|
258
|
+
}),
|
|
259
|
+
execute: async ({ tags }) => {
|
|
260
|
+
return `Tags: ${tags.join(", ")}`;
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
const result = await v4Tool.execute(
|
|
264
|
+
{ tags: ["nodejs", "typescript", "testing"] },
|
|
265
|
+
createToolOptions("test-array")
|
|
266
|
+
);
|
|
267
|
+
expect(result).toBe("Tags: nodejs, typescript, testing");
|
|
268
|
+
});
|
|
269
|
+
it("should handle v4 nested object schemas", async () => {
|
|
270
|
+
const v4Tool = tool({
|
|
271
|
+
description: "Tool with nested object using v4",
|
|
272
|
+
parameters: z4.object({
|
|
273
|
+
user: z4.object({
|
|
274
|
+
name: z4.string(),
|
|
275
|
+
email: z4.string()
|
|
276
|
+
})
|
|
277
|
+
}),
|
|
278
|
+
execute: async ({ user }) => {
|
|
279
|
+
return `${user.name} (${user.email})`;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
const result = await v4Tool.execute(
|
|
283
|
+
{ user: { name: "John Doe", email: "john@example.com" } },
|
|
284
|
+
createToolOptions("test-nested")
|
|
285
|
+
);
|
|
286
|
+
expect(result).toBe("John Doe (john@example.com)");
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
describe("oaiParams with v4 schemas", () => {
|
|
290
|
+
it("should convert v4 basic object schema", () => {
|
|
291
|
+
const schema = z4.object({
|
|
292
|
+
name: z4.string().describe("User name"),
|
|
293
|
+
age: z4.number().describe("User age")
|
|
294
|
+
});
|
|
295
|
+
const result = oaiParams(schema);
|
|
296
|
+
expect(result.type).toBe("object");
|
|
297
|
+
expect(result.properties).toHaveProperty("name");
|
|
298
|
+
expect(result.properties).toHaveProperty("age");
|
|
299
|
+
expect(result.required).toContain("name");
|
|
300
|
+
expect(result.required).toContain("age");
|
|
301
|
+
});
|
|
302
|
+
it("should handle v4 optional fields", () => {
|
|
303
|
+
const schema = z4.object({
|
|
304
|
+
required: z4.string(),
|
|
305
|
+
optional: z4.string().optional()
|
|
306
|
+
});
|
|
307
|
+
const result = oaiParams(schema);
|
|
308
|
+
expect(result.required).toContain("required");
|
|
309
|
+
expect(result.required).not.toContain("optional");
|
|
310
|
+
});
|
|
311
|
+
it("should handle v4 enum fields", () => {
|
|
312
|
+
var _a;
|
|
313
|
+
const schema = z4.object({
|
|
314
|
+
status: z4.enum(["pending", "approved", "rejected"])
|
|
315
|
+
});
|
|
316
|
+
const result = oaiParams(schema);
|
|
317
|
+
const properties = result.properties;
|
|
318
|
+
expect((_a = properties.status) == null ? void 0 : _a.enum).toEqual(["pending", "approved", "rejected"]);
|
|
319
|
+
});
|
|
320
|
+
it("should handle v4 array fields", () => {
|
|
321
|
+
const schema = z4.object({
|
|
322
|
+
items: z4.array(z4.string())
|
|
323
|
+
});
|
|
324
|
+
const result = oaiParams(schema);
|
|
325
|
+
const properties = result.properties;
|
|
326
|
+
expect(
|
|
327
|
+
properties.items && typeof properties.items === "object" ? properties.items.type : void 0
|
|
328
|
+
).toBe("array");
|
|
329
|
+
expect(
|
|
330
|
+
properties.items && properties.items.items && typeof properties.items.items === "object" ? properties.items.items.type : void 0
|
|
331
|
+
).toBe("string");
|
|
332
|
+
});
|
|
333
|
+
});
|
|
159
334
|
});
|
|
160
335
|
});
|
|
161
336
|
//# sourceMappingURL=tool_context.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/llm/tool_context.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { describe, expect, it } from 'vitest';\nimport { z } from 'zod';\nimport { type ToolOptions, tool } from './tool_context.js';\nimport { createToolOptions, oaiParams } from './utils.js';\n\ndescribe('Tool Context', () => {\n describe('oaiParams', () => {\n it('should handle basic object schema', () => {\n const schema = z.object({\n name: z.string().describe('The user name'),\n age: z.number().describe('The user age'),\n });\n\n const result = oaiParams(schema);\n\n expect(result).toMatchSnapshot();\n });\n\n it('should handle enum fields', () => {\n const schema = z.object({\n color: z.enum(['red', 'blue', 'green']).describe('Choose a color'),\n });\n\n const result = oaiParams(schema);\n\n expect(result).toMatchSnapshot();\n });\n\n it('should handle array fields', () => {\n const schema = z.object({\n tags: z.array(z.string()).describe('List of tags'),\n });\n\n const result = oaiParams(schema);\n\n expect(result).toMatchSnapshot();\n });\n\n it('should handle array of enums', () => {\n const schema = z.object({\n colors: z.array(z.enum(['red', 'blue', 'green'])).describe('List of colors'),\n });\n\n const result = oaiParams(schema);\n\n expect(result).toMatchSnapshot();\n });\n\n it('should handle optional fields', () => {\n const schema = z.object({\n name: z.string().describe('The user name'),\n age: z.number().optional().describe('The user age'),\n });\n\n const result = oaiParams(schema);\n\n expect(result).toMatchSnapshot();\n });\n\n it('should handle fields without descriptions', () => {\n const schema = z.object({\n name: z.string(),\n age: z.number(),\n });\n\n const result = oaiParams(schema);\n\n expect(result).toMatchSnapshot();\n });\n });\n\n describe('tool', () => {\n it('should create and execute a basic core tool', async () => {\n const getWeather = tool({\n description: 'Get the weather for a given location',\n parameters: z.object({\n location: z.string(),\n }),\n execute: async ({ location }, { ctx }: ToolOptions<{ name: string }>) => {\n return `The weather in ${location} is sunny, ${ctx.userData.name}`;\n },\n });\n\n const result = await getWeather.execute(\n { location: 'San Francisco' },\n createToolOptions('123', { name: 'John' }),\n );\n expect(result).toBe('The weather in San Francisco is sunny, John');\n });\n\n it('should properly type a callable function', async () => {\n const testFunction = tool({\n description: 'Test function',\n parameters: z.object({\n name: z.string().describe('The user name'),\n age: z.number().describe('The user age'),\n }),\n execute: async (args) => {\n return `${args.name} is ${args.age} years old`;\n },\n });\n\n const result = await testFunction.execute(\n { name: 'John', age: 30 },\n createToolOptions('123'),\n );\n expect(result).toBe('John is 30 years old');\n });\n\n it('should handle async execution', async () => {\n const testFunction = tool({\n description: 'Async test function',\n parameters: z.object({\n delay: z.number().describe('Delay in milliseconds'),\n }),\n execute: async (args) => {\n await new Promise((resolve) => setTimeout(resolve, args.delay));\n return args.delay;\n },\n });\n\n const start = Date.now();\n const result = await testFunction.execute({ delay: 100 }, createToolOptions('123'));\n const duration = Date.now() - start;\n\n expect(result).toBe(100);\n expect(duration).toBeGreaterThanOrEqual(95); // Allow for small timing variations\n });\n\n describe('nested array support', () => {\n it('should handle nested array fields', () => {\n const schema = z.object({\n items: z.array(\n z.object({\n name: z.string().describe('the item name'),\n modifiers: z\n .array(\n z.object({\n modifier_name: z.string(),\n modifier_value: z.string(),\n }),\n )\n .describe('list of the modifiers applied on this item, such as size'),\n }),\n ),\n });\n const result = oaiParams(schema);\n expect(result).toMatchSnapshot();\n });\n });\n\n describe('optional parameters', () => {\n it('should create a tool without parameters', async () => {\n const simpleAction = tool({\n description: 'Perform a simple action',\n execute: async () => {\n return 'Action performed';\n },\n });\n\n expect(simpleAction.type).toBe('function');\n expect(simpleAction.description).toBe('Perform a simple action');\n expect(simpleAction.parameters).toBeDefined();\n expect(simpleAction.parameters._def.typeName).toBe('ZodObject');\n\n const result = await simpleAction.execute({}, createToolOptions('123'));\n expect(result).toBe('Action performed');\n });\n\n it('should handle tools with context but no parameters', async () => {\n const greetUser = tool({\n description: 'Greet the current user',\n execute: async (_, { ctx }: ToolOptions<{ username: string }>) => {\n return `Hello, ${ctx.userData.username}!`;\n },\n });\n\n const result = await greetUser.execute({}, createToolOptions('123', { username: 'Alice' }));\n expect(result).toBe('Hello, Alice!');\n });\n\n it('should create a tool that accesses tool call id without parameters', async () => {\n const getCallId = tool({\n description: 'Get the current tool call ID',\n execute: async (_, { toolCallId }) => {\n return `Tool call ID: ${toolCallId}`;\n },\n });\n\n const result = await getCallId.execute({}, createToolOptions('test-id-456'));\n expect(result).toBe('Tool call ID: test-id-456');\n });\n });\n });\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/llm/tool_context.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { describe, expect, it } from 'vitest';\nimport { z } from 'zod';\nimport * as z3 from 'zod/v3';\nimport * as z4 from 'zod/v4';\nimport { type ToolOptions, tool } from './tool_context.js';\nimport { createToolOptions, oaiParams } from './utils.js';\n\ndescribe('Tool Context', () => {\n describe('oaiParams', () => {\n it('should handle basic object schema', () => {\n const schema = z.object({\n name: z.string().describe('The user name'),\n age: z.number().describe('The user age'),\n });\n\n const result = oaiParams(schema);\n\n expect(result).toMatchSnapshot();\n });\n\n it('should handle enum fields', () => {\n const schema = z.object({\n color: z.enum(['red', 'blue', 'green']).describe('Choose a color'),\n });\n\n const result = oaiParams(schema);\n\n expect(result).toMatchSnapshot();\n });\n\n it('should handle array fields', () => {\n const schema = z.object({\n tags: z.array(z.string()).describe('List of tags'),\n });\n\n const result = oaiParams(schema);\n\n expect(result).toMatchSnapshot();\n });\n\n it('should handle array of enums', () => {\n const schema = z.object({\n colors: z.array(z.enum(['red', 'blue', 'green'])).describe('List of colors'),\n });\n\n const result = oaiParams(schema);\n\n expect(result).toMatchSnapshot();\n });\n\n it('should handle optional fields', () => {\n const schema = z.object({\n name: z.string().describe('The user name'),\n age: z.number().optional().describe('The user age'),\n });\n\n const result = oaiParams(schema);\n\n expect(result).toMatchSnapshot();\n });\n\n it('should handle fields without descriptions', () => {\n const schema = z.object({\n name: z.string(),\n age: z.number(),\n });\n\n const result = oaiParams(schema);\n\n expect(result).toMatchSnapshot();\n });\n });\n\n describe('tool', () => {\n it('should create and execute a basic core tool', async () => {\n const getWeather = tool({\n description: 'Get the weather for a given location',\n parameters: z.object({\n location: z.string(),\n }),\n execute: async ({ location }, { ctx }: ToolOptions<{ name: string }>) => {\n return `The weather in ${location} is sunny, ${ctx.userData.name}`;\n },\n });\n\n const result = await getWeather.execute(\n { location: 'San Francisco' },\n createToolOptions('123', { name: 'John' }),\n );\n expect(result).toBe('The weather in San Francisco is sunny, John');\n });\n\n it('should properly type a callable function', async () => {\n const testFunction = tool({\n description: 'Test function',\n parameters: z.object({\n name: z.string().describe('The user name'),\n age: z.number().describe('The user age'),\n }),\n execute: async (args) => {\n return `${args.name} is ${args.age} years old`;\n },\n });\n\n const result = await testFunction.execute(\n { name: 'John', age: 30 },\n createToolOptions('123'),\n );\n expect(result).toBe('John is 30 years old');\n });\n\n it('should handle async execution', async () => {\n const testFunction = tool({\n description: 'Async test function',\n parameters: z.object({\n delay: z.number().describe('Delay in milliseconds'),\n }),\n execute: async (args) => {\n await new Promise((resolve) => setTimeout(resolve, args.delay));\n return args.delay;\n },\n });\n\n const start = Date.now();\n const result = await testFunction.execute({ delay: 100 }, createToolOptions('123'));\n const duration = Date.now() - start;\n\n expect(result).toBe(100);\n expect(duration).toBeGreaterThanOrEqual(95); // Allow for small timing variations\n });\n\n describe('nested array support', () => {\n it('should handle nested array fields', () => {\n const schema = z.object({\n items: z.array(\n z.object({\n name: z.string().describe('the item name'),\n modifiers: z\n .array(\n z.object({\n modifier_name: z.string(),\n modifier_value: z.string(),\n }),\n )\n .describe('list of the modifiers applied on this item, such as size'),\n }),\n ),\n });\n const result = oaiParams(schema);\n expect(result).toMatchSnapshot();\n });\n });\n\n describe('optional parameters', () => {\n it('should create a tool without parameters', async () => {\n const simpleAction = tool({\n description: 'Perform a simple action',\n execute: async () => {\n return 'Action performed';\n },\n });\n\n expect(simpleAction.type).toBe('function');\n expect(simpleAction.description).toBe('Perform a simple action');\n expect(simpleAction.parameters).toBeDefined();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n expect((simpleAction.parameters as any)._def.typeName).toBe('ZodObject');\n\n const result = await simpleAction.execute({}, createToolOptions('123'));\n expect(result).toBe('Action performed');\n });\n\n it('should support .optional() fields in tool parameters', async () => {\n const weatherTool = tool({\n description: 'Get weather information',\n parameters: z.object({\n location: z.string().describe('The city or location').optional(),\n units: z.enum(['celsius', 'fahrenheit']).describe('Temperature units').optional(),\n }),\n execute: async ({ location, units }) => {\n const loc = location ?? 'Unknown';\n const unit = units ?? 'celsius';\n return `Weather in ${loc} (${unit})`;\n },\n });\n\n expect(weatherTool.type).toBe('function');\n expect(weatherTool.description).toBe('Get weather information');\n\n const result1 = await weatherTool.execute(\n { location: 'London', units: 'celsius' },\n createToolOptions('123'),\n );\n expect(result1).toBe('Weather in London (celsius)');\n\n const result2 = await weatherTool.execute({}, createToolOptions('123'));\n expect(result2).toBe('Weather in Unknown (celsius)');\n\n const result3 = await weatherTool.execute({ location: 'Paris' }, createToolOptions('123'));\n expect(result3).toBe('Weather in Paris (celsius)');\n });\n\n it('should handle tools with context but no parameters', async () => {\n const greetUser = tool({\n description: 'Greet the current user',\n execute: async (_, { ctx }: ToolOptions<{ username: string }>) => {\n return `Hello, ${ctx.userData.username}!`;\n },\n });\n\n const result = await greetUser.execute({}, createToolOptions('123', { username: 'Alice' }));\n expect(result).toBe('Hello, Alice!');\n });\n\n it('should create a tool that accesses tool call id without parameters', async () => {\n const getCallId = tool({\n description: 'Get the current tool call ID',\n execute: async (_, { toolCallId }) => {\n return `Tool call ID: ${toolCallId}`;\n },\n });\n\n const result = await getCallId.execute({}, createToolOptions('test-id-456'));\n expect(result).toBe('Tool call ID: test-id-456');\n });\n });\n\n describe('Zod v3 and v4 compatibility', () => {\n it('should work with Zod v3 schemas', async () => {\n const v3Tool = tool({\n description: 'A tool using Zod v3 schema',\n parameters: z3.object({\n name: z3.string(),\n count: z3.number(),\n }),\n execute: async ({ name, count }) => {\n return `${name}: ${count}`;\n },\n });\n\n const result = await v3Tool.execute(\n { name: 'Test', count: 42 },\n createToolOptions('v3-test'),\n );\n expect(result).toBe('Test: 42');\n });\n\n it('should work with Zod v4 schemas', async () => {\n const v4Tool = tool({\n description: 'A tool using Zod v4 schema',\n parameters: z4.object({\n name: z4.string(),\n count: z4.number(),\n }),\n execute: async ({ name, count }) => {\n return `${name}: ${count}`;\n },\n });\n\n const result = await v4Tool.execute(\n { name: 'Test', count: 42 },\n createToolOptions('v4-test'),\n );\n expect(result).toBe('Test: 42');\n });\n\n it('should handle v4 schemas with optional fields', async () => {\n const v4Tool = tool({\n description: 'Tool with optional field using v4',\n parameters: z4.object({\n required: z4.string(),\n optional: z4.string().optional(),\n }),\n execute: async ({ required, optional }) => {\n return optional ? `${required} - ${optional}` : required;\n },\n });\n\n const result1 = await v4Tool.execute({ required: 'Hello' }, createToolOptions('test-1'));\n expect(result1).toBe('Hello');\n\n const result2 = await v4Tool.execute(\n { required: 'Hello', optional: 'World' },\n createToolOptions('test-2'),\n );\n expect(result2).toBe('Hello - World');\n });\n\n it('should handle v4 enum schemas', async () => {\n const v4Tool = tool({\n description: 'Tool with enum using v4',\n parameters: z4.object({\n color: z4.enum(['red', 'blue', 'green']),\n }),\n execute: async ({ color }) => {\n return `Selected color: ${color}`;\n },\n });\n\n const result = await v4Tool.execute({ color: 'blue' }, createToolOptions('test-enum'));\n expect(result).toBe('Selected color: blue');\n });\n\n it('should handle v4 array schemas', async () => {\n const v4Tool = tool({\n description: 'Tool with array using v4',\n parameters: z4.object({\n tags: z4.array(z4.string()),\n }),\n execute: async ({ tags }) => {\n return `Tags: ${tags.join(', ')}`;\n },\n });\n\n const result = await v4Tool.execute(\n { tags: ['nodejs', 'typescript', 'testing'] },\n createToolOptions('test-array'),\n );\n expect(result).toBe('Tags: nodejs, typescript, testing');\n });\n\n it('should handle v4 nested object schemas', async () => {\n const v4Tool = tool({\n description: 'Tool with nested object using v4',\n parameters: z4.object({\n user: z4.object({\n name: z4.string(),\n email: z4.string(),\n }),\n }),\n execute: async ({ user }) => {\n return `${user.name} (${user.email})`;\n },\n });\n\n const result = await v4Tool.execute(\n { user: { name: 'John Doe', email: 'john@example.com' } },\n createToolOptions('test-nested'),\n );\n expect(result).toBe('John Doe (john@example.com)');\n });\n });\n\n describe('oaiParams with v4 schemas', () => {\n it('should convert v4 basic object schema', () => {\n const schema = z4.object({\n name: z4.string().describe('User name'),\n age: z4.number().describe('User age'),\n });\n\n const result = oaiParams(schema);\n\n expect(result.type).toBe('object');\n expect(result.properties).toHaveProperty('name');\n expect(result.properties).toHaveProperty('age');\n expect(result.required).toContain('name');\n expect(result.required).toContain('age');\n });\n\n it('should handle v4 optional fields', () => {\n const schema = z4.object({\n required: z4.string(),\n optional: z4.string().optional(),\n });\n\n const result = oaiParams(schema);\n\n expect(result.required).toContain('required');\n expect(result.required).not.toContain('optional');\n });\n\n it('should handle v4 enum fields', () => {\n const schema = z4.object({\n status: z4.enum(['pending', 'approved', 'rejected']),\n });\n\n const result = oaiParams(schema);\n\n const properties = result.properties as Record<string, Record<string, unknown>>;\n expect(properties.status?.enum).toEqual(['pending', 'approved', 'rejected']);\n });\n\n it('should handle v4 array fields', () => {\n const schema = z4.object({\n items: z4.array(z4.string()),\n });\n\n const result = oaiParams(schema);\n\n const properties = result.properties as Record<string, any>;\n expect(\n properties.items && typeof properties.items === 'object'\n ? properties.items.type\n : undefined,\n ).toBe('array');\n expect(\n properties.items && properties.items.items && typeof properties.items.items === 'object'\n ? properties.items.items.type\n : undefined,\n ).toBe('string');\n });\n });\n });\n});\n"],"mappings":"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","names":[]}
|
package/dist/llm/utils.cjs
CHANGED
|
@@ -39,9 +39,8 @@ __export(utils_exports, {
|
|
|
39
39
|
module.exports = __toCommonJS(utils_exports);
|
|
40
40
|
var import_rtc_node = require("@livekit/rtc-node");
|
|
41
41
|
var import_sharp = __toESM(require("sharp"), 1);
|
|
42
|
-
var import_zod = require("zod");
|
|
43
|
-
var import_zod_to_json_schema = require("zod-to-json-schema");
|
|
44
42
|
var import_chat_context = require("./chat_context.cjs");
|
|
43
|
+
var import_zod_utils = require("./zod-utils.cjs");
|
|
45
44
|
function getChannelsFromVideoBufferType(type) {
|
|
46
45
|
switch (type) {
|
|
47
46
|
case import_rtc_node.VideoBufferType.RGBA:
|
|
@@ -128,11 +127,9 @@ async function serializeImage(image) {
|
|
|
128
127
|
const createToolOptions = (toolCallId, userData = {}) => {
|
|
129
128
|
return { ctx: { userData }, toolCallId };
|
|
130
129
|
};
|
|
131
|
-
const oaiParams = (
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
target: isOpenai ? "openAi" : "jsonSchema7"
|
|
135
|
-
});
|
|
130
|
+
const oaiParams = (schema, isOpenai = true) => {
|
|
131
|
+
const jsonSchema = (0, import_zod_utils.zodSchemaToJsonSchema)(schema, isOpenai);
|
|
132
|
+
const { properties, required, additionalProperties } = jsonSchema;
|
|
136
133
|
return {
|
|
137
134
|
type: "object",
|
|
138
135
|
properties,
|
|
@@ -165,8 +162,17 @@ async function executeToolCall(toolCall, toolCtx) {
|
|
|
165
162
|
});
|
|
166
163
|
}
|
|
167
164
|
try {
|
|
168
|
-
if (tool.parameters
|
|
169
|
-
|
|
165
|
+
if ((0, import_zod_utils.isZodSchema)(tool.parameters)) {
|
|
166
|
+
const result = await (0, import_zod_utils.parseZodSchema)(tool.parameters, args);
|
|
167
|
+
if (result.success) {
|
|
168
|
+
params = result.data;
|
|
169
|
+
} else {
|
|
170
|
+
return import_chat_context.FunctionCallOutput.create({
|
|
171
|
+
callId: toolCall.callId,
|
|
172
|
+
output: `Arguments parsing failed: ${result.error}`,
|
|
173
|
+
isError: true
|
|
174
|
+
});
|
|
175
|
+
}
|
|
170
176
|
} else {
|
|
171
177
|
params = args;
|
|
172
178
|
}
|
|
@@ -243,8 +249,8 @@ function computeChatCtxDiff(oldCtx, newCtx) {
|
|
|
243
249
|
};
|
|
244
250
|
}
|
|
245
251
|
function toJsonSchema(schema, isOpenai = true) {
|
|
246
|
-
if (
|
|
247
|
-
return
|
|
252
|
+
if ((0, import_zod_utils.isZodSchema)(schema)) {
|
|
253
|
+
return (0, import_zod_utils.zodSchemaToJsonSchema)(schema, isOpenai);
|
|
248
254
|
}
|
|
249
255
|
return schema;
|
|
250
256
|
}
|
package/dist/llm/utils.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/llm/utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { VideoBufferType, VideoFrame } from '@livekit/rtc-node';\nimport type { JSONSchema7 } from 'json-schema';\nimport sharp from 'sharp';\nimport { ZodObject } from 'zod';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport type { UnknownUserData } from '../voice/run_context.js';\nimport type { ChatContext } from './chat_context.js';\nimport {\n type ChatItem,\n FunctionCall,\n FunctionCallOutput,\n type ImageContent,\n} from './chat_context.js';\nimport type { ToolContext, ToolInputSchema, ToolOptions } from './tool_context.js';\n\nexport interface SerializedImage {\n inferenceDetail: 'auto' | 'high' | 'low';\n mimeType?: string;\n base64Data?: string;\n externalUrl?: string;\n}\n\nfunction getChannelsFromVideoBufferType(type: VideoBufferType): 3 | 4 {\n switch (type) {\n case VideoBufferType.RGBA:\n case VideoBufferType.ABGR:\n case VideoBufferType.ARGB:\n case VideoBufferType.BGRA:\n return 4;\n case VideoBufferType.RGB24:\n return 3;\n default:\n // YUV formats (I420, I420A, I422, I444, I010, NV12) need conversion\n throw new Error(`Unsupported VideoBufferType: ${type}. Only RGB/RGBA formats are supported.`);\n }\n}\n\nfunction ensureRGBCompatible(frame: VideoFrame): VideoFrame {\n // If the frame is already in an RGB/RGBA-compatible format, return it directly\n if (\n frame.type === VideoBufferType.RGBA ||\n frame.type === VideoBufferType.BGRA ||\n frame.type === VideoBufferType.ARGB ||\n frame.type === VideoBufferType.ABGR ||\n frame.type === VideoBufferType.RGB24\n ) {\n return frame;\n }\n\n // Otherwise, attempt conversion for other formats (like YUV)\n try {\n return frame.convert(VideoBufferType.RGBA);\n } catch (error) {\n throw new Error(\n `Failed to convert format ${frame.type} to RGB: ${error}. ` +\n `Consider using RGB/RGBA formats or converting on the client side.`,\n );\n }\n}\n\nexport async function serializeImage(image: ImageContent): Promise<SerializedImage> {\n if (typeof image.image === 'string') {\n if (image.image.startsWith('data:')) {\n const [header, base64Data] = image.image.split(',', 2) as [string, string];\n const headerParts = header.split(';');\n const mimeParts = headerParts[0]?.split(':');\n const headerMime = mimeParts?.[1];\n\n if (!headerMime) {\n throw new Error('Invalid data URL format');\n }\n\n let mimeType: string;\n if (image.mimeType && image.mimeType !== headerMime) {\n console.warn(\n `Provided mimeType '${image.mimeType}' does not match data URL mime type '${headerMime}'. Using provided mimeType.`,\n );\n mimeType = image.mimeType;\n } else {\n mimeType = headerMime;\n }\n\n const supportedTypes = new Set(['image/jpeg', 'image/png', 'image/webp', 'image/gif']);\n if (!supportedTypes.has(mimeType)) {\n throw new Error(`Unsupported mimeType ${mimeType}. Must be jpeg, png, webp, or gif`);\n }\n\n return {\n base64Data,\n mimeType: mimeType,\n inferenceDetail: image.inferenceDetail,\n };\n }\n\n // External URL\n return {\n mimeType: image.mimeType,\n inferenceDetail: image.inferenceDetail,\n externalUrl: image.image,\n };\n } else if (image.image instanceof VideoFrame) {\n const frame = ensureRGBCompatible(image.image);\n const channels = getChannelsFromVideoBufferType(frame.type);\n\n // Sharp needs to know the format of raw pixel data\n let encoded = sharp(frame.data, {\n raw: {\n width: frame.width,\n height: frame.height,\n channels,\n },\n });\n\n if (image.inferenceWidth && image.inferenceHeight) {\n encoded = encoded.resize(image.inferenceWidth, image.inferenceHeight);\n }\n\n const base64Data = await encoded\n .png()\n .toBuffer()\n .then((buffer) => buffer.toString('base64'));\n\n return {\n base64Data,\n mimeType: 'image/png',\n inferenceDetail: image.inferenceDetail,\n };\n } else {\n throw new Error('Unsupported image type');\n }\n}\n\n/** Raw OpenAI-adherent function parameters. */\nexport type OpenAIFunctionParameters = {\n type: 'object';\n properties: { [id: string]: any }; // eslint-disable-line @typescript-eslint/no-explicit-any\n required: string[];\n additionalProperties?: boolean;\n};\n\n// TODO(brian): remove this helper once we have the real RunContext user data\nexport const createToolOptions = <UserData extends UnknownUserData>(\n toolCallId: string,\n userData: UserData = {} as UserData,\n): ToolOptions<UserData> => {\n return { ctx: { userData }, toolCallId } as unknown as ToolOptions<UserData>;\n};\n\n/** @internal */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const oaiParams = (\n p: ZodObject<any>,\n isOpenai: boolean = true,\n): OpenAIFunctionParameters => {\n // Adapted from https://github.com/vercel/ai/blob/56eb0ee9/packages/provider-utils/src/zod-schema.ts\n const { properties, required, additionalProperties } = zodToJsonSchema(p, {\n // note: openai mode breaks various gemini conversions\n target: isOpenai ? 'openAi' : 'jsonSchema7',\n }) as OpenAIFunctionParameters;\n\n return {\n type: 'object',\n properties,\n required,\n additionalProperties,\n };\n};\n\n/** @internal */\nexport const oaiBuildFunctionInfo = (\n toolCtx: ToolContext,\n toolCallId: string,\n toolName: string,\n rawArgs: string,\n): FunctionCall => {\n const tool = toolCtx[toolName];\n if (!tool) {\n throw new Error(`AI tool ${toolName} not found`);\n }\n\n return FunctionCall.create({\n callId: toolCallId,\n name: toolName,\n args: rawArgs,\n });\n};\n\nexport async function executeToolCall(\n toolCall: FunctionCall,\n toolCtx: ToolContext,\n): Promise<FunctionCallOutput> {\n const tool = toolCtx[toolCall.name]!;\n let args: object | undefined;\n let params: object | undefined;\n\n // Ensure valid JSON\n try {\n args = JSON.parse(toolCall.args);\n } catch (error) {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Invalid JSON: ${error}`,\n isError: true,\n });\n }\n\n // Ensure valid arguments schema\n try {\n if (tool.parameters instanceof ZodObject) {\n params = tool.parameters.parse(args);\n } else {\n params = args;\n }\n } catch (error) {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Arguments parsing failed: ${error}`,\n isError: true,\n });\n }\n\n try {\n const result = await tool.execute(params, createToolOptions(toolCall.callId));\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: JSON.stringify(result),\n isError: false,\n });\n } catch (error) {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Tool execution failed: ${error}`,\n isError: true,\n });\n }\n}\n\n/**\n * Standard dynamic-programming LCS to get the common subsequence\n * of IDs (in order) that appear in both old_ids and new_ids.\n *\n * @param oldIds - The old list of IDs.\n * @param newIds - The new list of IDs.\n * @returns The longest common subsequence of the two lists of IDs.\n */\nfunction computeLCS(oldIds: string[], newIds: string[]): string[] {\n const n = oldIds.length;\n const m = newIds.length;\n const dp: number[][] = Array(n + 1)\n .fill(null)\n .map(() => Array(m + 1).fill(0));\n\n // Fill DP table\n for (let i = 1; i <= n; i++) {\n for (let j = 1; j <= m; j++) {\n if (oldIds[i - 1] === newIds[j - 1]) {\n dp[i]![j] = dp[i - 1]![j - 1]! + 1;\n } else {\n dp[i]![j] = Math.max(dp[i - 1]![j]!, dp[i]![j - 1]!);\n }\n }\n }\n\n // Backtrack to find the actual LCS sequence\n const lcsIds: string[] = [];\n let i = n;\n let j = m;\n while (i > 0 && j > 0) {\n if (oldIds[i - 1] === newIds[j - 1]) {\n lcsIds.push(oldIds[i - 1]!);\n i--;\n j--;\n } else if (dp[i - 1]![j]! > dp[i]![j - 1]!) {\n i--;\n } else {\n j--;\n }\n }\n\n return lcsIds.reverse();\n}\n\ninterface DiffOps {\n toRemove: string[];\n toCreate: Array<[string | null, string]>; // (previous_item_id, id), if previous_item_id is null, add to the root\n}\n\n/**\n * Compute the minimal list of create/remove operations to transform oldCtx into newCtx.\n *\n * @param oldCtx - The old chat context.\n * @param newCtx - The new chat context.\n * @returns The minimal list of create/remove operations to transform oldCtx into newCtx.\n */\nexport function computeChatCtxDiff(oldCtx: ChatContext, newCtx: ChatContext): DiffOps {\n const oldIds = oldCtx.items.map((item: ChatItem) => item.id);\n const newIds = newCtx.items.map((item: ChatItem) => item.id);\n const lcsIds = new Set(computeLCS(oldIds, newIds));\n\n const toRemove = oldCtx.items.filter((msg) => !lcsIds.has(msg.id)).map((msg) => msg.id);\n const toCreate: Array<[string | null, string]> = [];\n\n let lastIdInSequence: string | null = null;\n for (const newItem of newCtx.items) {\n if (lcsIds.has(newItem.id)) {\n lastIdInSequence = newItem.id;\n } else {\n const prevId = lastIdInSequence; // null if root\n toCreate.push([prevId, newItem.id]);\n lastIdInSequence = newItem.id;\n }\n }\n\n return {\n toRemove,\n toCreate,\n };\n}\n\nexport function toJsonSchema(schema: ToolInputSchema<any>, isOpenai: boolean = true): JSONSchema7 {\n if (schema instanceof ZodObject) {\n return oaiParams(schema, isOpenai);\n }\n return schema;\n}\n"],"mappings":"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","names":["sharp","i","j"]}
|
|
1
|
+
{"version":3,"sources":["../../src/llm/utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { VideoBufferType, VideoFrame } from '@livekit/rtc-node';\nimport type { JSONSchema7 } from 'json-schema';\nimport sharp from 'sharp';\nimport type { UnknownUserData } from '../voice/run_context.js';\nimport type { ChatContext } from './chat_context.js';\nimport {\n type ChatItem,\n FunctionCall,\n FunctionCallOutput,\n type ImageContent,\n} from './chat_context.js';\nimport type { ToolContext, ToolInputSchema, ToolOptions } from './tool_context.js';\nimport { isZodSchema, parseZodSchema, zodSchemaToJsonSchema } from './zod-utils.js';\n\nexport interface SerializedImage {\n inferenceDetail: 'auto' | 'high' | 'low';\n mimeType?: string;\n base64Data?: string;\n externalUrl?: string;\n}\n\nfunction getChannelsFromVideoBufferType(type: VideoBufferType): 3 | 4 {\n switch (type) {\n case VideoBufferType.RGBA:\n case VideoBufferType.ABGR:\n case VideoBufferType.ARGB:\n case VideoBufferType.BGRA:\n return 4;\n case VideoBufferType.RGB24:\n return 3;\n default:\n // YUV formats (I420, I420A, I422, I444, I010, NV12) need conversion\n throw new Error(`Unsupported VideoBufferType: ${type}. Only RGB/RGBA formats are supported.`);\n }\n}\n\nfunction ensureRGBCompatible(frame: VideoFrame): VideoFrame {\n // If the frame is already in an RGB/RGBA-compatible format, return it directly\n if (\n frame.type === VideoBufferType.RGBA ||\n frame.type === VideoBufferType.BGRA ||\n frame.type === VideoBufferType.ARGB ||\n frame.type === VideoBufferType.ABGR ||\n frame.type === VideoBufferType.RGB24\n ) {\n return frame;\n }\n\n // Otherwise, attempt conversion for other formats (like YUV)\n try {\n return frame.convert(VideoBufferType.RGBA);\n } catch (error) {\n throw new Error(\n `Failed to convert format ${frame.type} to RGB: ${error}. ` +\n `Consider using RGB/RGBA formats or converting on the client side.`,\n );\n }\n}\n\nexport async function serializeImage(image: ImageContent): Promise<SerializedImage> {\n if (typeof image.image === 'string') {\n if (image.image.startsWith('data:')) {\n const [header, base64Data] = image.image.split(',', 2) as [string, string];\n const headerParts = header.split(';');\n const mimeParts = headerParts[0]?.split(':');\n const headerMime = mimeParts?.[1];\n\n if (!headerMime) {\n throw new Error('Invalid data URL format');\n }\n\n let mimeType: string;\n if (image.mimeType && image.mimeType !== headerMime) {\n console.warn(\n `Provided mimeType '${image.mimeType}' does not match data URL mime type '${headerMime}'. Using provided mimeType.`,\n );\n mimeType = image.mimeType;\n } else {\n mimeType = headerMime;\n }\n\n const supportedTypes = new Set(['image/jpeg', 'image/png', 'image/webp', 'image/gif']);\n if (!supportedTypes.has(mimeType)) {\n throw new Error(`Unsupported mimeType ${mimeType}. Must be jpeg, png, webp, or gif`);\n }\n\n return {\n base64Data,\n mimeType: mimeType,\n inferenceDetail: image.inferenceDetail,\n };\n }\n\n // External URL\n return {\n mimeType: image.mimeType,\n inferenceDetail: image.inferenceDetail,\n externalUrl: image.image,\n };\n } else if (image.image instanceof VideoFrame) {\n const frame = ensureRGBCompatible(image.image);\n const channels = getChannelsFromVideoBufferType(frame.type);\n\n // Sharp needs to know the format of raw pixel data\n let encoded = sharp(frame.data, {\n raw: {\n width: frame.width,\n height: frame.height,\n channels,\n },\n });\n\n if (image.inferenceWidth && image.inferenceHeight) {\n encoded = encoded.resize(image.inferenceWidth, image.inferenceHeight);\n }\n\n const base64Data = await encoded\n .png()\n .toBuffer()\n .then((buffer) => buffer.toString('base64'));\n\n return {\n base64Data,\n mimeType: 'image/png',\n inferenceDetail: image.inferenceDetail,\n };\n } else {\n throw new Error('Unsupported image type');\n }\n}\n\n/** Raw OpenAI-adherent function parameters. */\nexport type OpenAIFunctionParameters = {\n type: 'object';\n properties: { [id: string]: any }; // eslint-disable-line @typescript-eslint/no-explicit-any\n required: string[];\n additionalProperties?: boolean;\n};\n\n// TODO(brian): remove this helper once we have the real RunContext user data\nexport const createToolOptions = <UserData extends UnknownUserData>(\n toolCallId: string,\n userData: UserData = {} as UserData,\n): ToolOptions<UserData> => {\n return { ctx: { userData }, toolCallId } as unknown as ToolOptions<UserData>;\n};\n\n/** @internal */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const oaiParams = (schema: any, isOpenai: boolean = true): OpenAIFunctionParameters => {\n // Adapted from https://github.com/vercel/ai/blob/56eb0ee9/packages/provider-utils/src/zod-schema.ts\n const jsonSchema = zodSchemaToJsonSchema(schema, isOpenai);\n const { properties, required, additionalProperties } = jsonSchema as OpenAIFunctionParameters;\n\n return {\n type: 'object',\n properties,\n required,\n additionalProperties,\n };\n};\n\n/** @internal */\nexport const oaiBuildFunctionInfo = (\n toolCtx: ToolContext,\n toolCallId: string,\n toolName: string,\n rawArgs: string,\n): FunctionCall => {\n const tool = toolCtx[toolName];\n if (!tool) {\n throw new Error(`AI tool ${toolName} not found`);\n }\n\n return FunctionCall.create({\n callId: toolCallId,\n name: toolName,\n args: rawArgs,\n });\n};\n\nexport async function executeToolCall(\n toolCall: FunctionCall,\n toolCtx: ToolContext,\n): Promise<FunctionCallOutput> {\n const tool = toolCtx[toolCall.name]!;\n let args: object | undefined;\n let params: object | undefined;\n\n // Ensure valid JSON\n try {\n args = JSON.parse(toolCall.args);\n } catch (error) {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Invalid JSON: ${error}`,\n isError: true,\n });\n }\n\n // Ensure valid arguments schema\n try {\n if (isZodSchema(tool.parameters)) {\n const result = await parseZodSchema<object>(tool.parameters, args);\n if (result.success) {\n params = result.data;\n } else {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Arguments parsing failed: ${result.error}`,\n isError: true,\n });\n }\n } else {\n params = args;\n }\n } catch (error) {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Arguments parsing failed: ${error}`,\n isError: true,\n });\n }\n\n try {\n const result = await tool.execute(params, createToolOptions(toolCall.callId));\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: JSON.stringify(result),\n isError: false,\n });\n } catch (error) {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Tool execution failed: ${error}`,\n isError: true,\n });\n }\n}\n\n/**\n * Standard dynamic-programming LCS to get the common subsequence\n * of IDs (in order) that appear in both old_ids and new_ids.\n *\n * @param oldIds - The old list of IDs.\n * @param newIds - The new list of IDs.\n * @returns The longest common subsequence of the two lists of IDs.\n */\nfunction computeLCS(oldIds: string[], newIds: string[]): string[] {\n const n = oldIds.length;\n const m = newIds.length;\n const dp: number[][] = Array(n + 1)\n .fill(null)\n .map(() => Array(m + 1).fill(0));\n\n // Fill DP table\n for (let i = 1; i <= n; i++) {\n for (let j = 1; j <= m; j++) {\n if (oldIds[i - 1] === newIds[j - 1]) {\n dp[i]![j] = dp[i - 1]![j - 1]! + 1;\n } else {\n dp[i]![j] = Math.max(dp[i - 1]![j]!, dp[i]![j - 1]!);\n }\n }\n }\n\n // Backtrack to find the actual LCS sequence\n const lcsIds: string[] = [];\n let i = n;\n let j = m;\n while (i > 0 && j > 0) {\n if (oldIds[i - 1] === newIds[j - 1]) {\n lcsIds.push(oldIds[i - 1]!);\n i--;\n j--;\n } else if (dp[i - 1]![j]! > dp[i]![j - 1]!) {\n i--;\n } else {\n j--;\n }\n }\n\n return lcsIds.reverse();\n}\n\ninterface DiffOps {\n toRemove: string[];\n toCreate: Array<[string | null, string]>; // (previous_item_id, id), if previous_item_id is null, add to the root\n}\n\n/**\n * Compute the minimal list of create/remove operations to transform oldCtx into newCtx.\n *\n * @param oldCtx - The old chat context.\n * @param newCtx - The new chat context.\n * @returns The minimal list of create/remove operations to transform oldCtx into newCtx.\n */\nexport function computeChatCtxDiff(oldCtx: ChatContext, newCtx: ChatContext): DiffOps {\n const oldIds = oldCtx.items.map((item: ChatItem) => item.id);\n const newIds = newCtx.items.map((item: ChatItem) => item.id);\n const lcsIds = new Set(computeLCS(oldIds, newIds));\n\n const toRemove = oldCtx.items.filter((msg) => !lcsIds.has(msg.id)).map((msg) => msg.id);\n const toCreate: Array<[string | null, string]> = [];\n\n let lastIdInSequence: string | null = null;\n for (const newItem of newCtx.items) {\n if (lcsIds.has(newItem.id)) {\n lastIdInSequence = newItem.id;\n } else {\n const prevId = lastIdInSequence; // null if root\n toCreate.push([prevId, newItem.id]);\n lastIdInSequence = newItem.id;\n }\n }\n\n return {\n toRemove,\n toCreate,\n };\n}\n\nexport function toJsonSchema(schema: ToolInputSchema<any>, isOpenai: boolean = true): JSONSchema7 {\n if (isZodSchema(schema)) {\n return zodSchemaToJsonSchema(schema, isOpenai);\n }\n return schema as JSONSchema7;\n}\n"],"mappings":"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","names":["sharp","i","j"]}
|
package/dist/llm/utils.d.cts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { JSONSchema7 } from 'json-schema';
|
|
2
|
-
import { ZodObject } from 'zod';
|
|
3
2
|
import type { ChatContext } from './chat_context.js';
|
|
4
3
|
import { FunctionCall, FunctionCallOutput, type ImageContent } from './chat_context.js';
|
|
5
4
|
import type { ToolContext, ToolInputSchema, ToolOptions } from './tool_context.js';
|
|
@@ -21,7 +20,7 @@ export type OpenAIFunctionParameters = {
|
|
|
21
20
|
};
|
|
22
21
|
export declare const createToolOptions: <UserData extends unknown>(toolCallId: string, userData?: UserData) => ToolOptions<UserData>;
|
|
23
22
|
/** @internal */
|
|
24
|
-
export declare const oaiParams: (
|
|
23
|
+
export declare const oaiParams: (schema: any, isOpenai?: boolean) => OpenAIFunctionParameters;
|
|
25
24
|
/** @internal */
|
|
26
25
|
export declare const oaiBuildFunctionInfo: (toolCtx: ToolContext, toolCallId: string, toolName: string, rawArgs: string) => FunctionCall;
|
|
27
26
|
export declare function executeToolCall(toolCall: FunctionCall, toolCtx: ToolContext): Promise<FunctionCallOutput>;
|
package/dist/llm/utils.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { JSONSchema7 } from 'json-schema';
|
|
2
|
-
import { ZodObject } from 'zod';
|
|
3
2
|
import type { ChatContext } from './chat_context.js';
|
|
4
3
|
import { FunctionCall, FunctionCallOutput, type ImageContent } from './chat_context.js';
|
|
5
4
|
import type { ToolContext, ToolInputSchema, ToolOptions } from './tool_context.js';
|
|
@@ -21,7 +20,7 @@ export type OpenAIFunctionParameters = {
|
|
|
21
20
|
};
|
|
22
21
|
export declare const createToolOptions: <UserData extends unknown>(toolCallId: string, userData?: UserData) => ToolOptions<UserData>;
|
|
23
22
|
/** @internal */
|
|
24
|
-
export declare const oaiParams: (
|
|
23
|
+
export declare const oaiParams: (schema: any, isOpenai?: boolean) => OpenAIFunctionParameters;
|
|
25
24
|
/** @internal */
|
|
26
25
|
export declare const oaiBuildFunctionInfo: (toolCtx: ToolContext, toolCallId: string, toolName: string, rawArgs: string) => FunctionCall;
|
|
27
26
|
export declare function executeToolCall(toolCall: FunctionCall, toolCtx: ToolContext): Promise<FunctionCallOutput>;
|
package/dist/llm/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/llm/utils.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/llm/utils.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAEL,YAAY,EACZ,kBAAkB,EAClB,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGnF,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAwCD,wBAAsB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAsElF;AAED,+CAA+C;AAC/C,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;IAClC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAGF,eAAO,MAAM,iBAAiB,yCAChB,MAAM,aACR,QAAQ,KACjB,YAAY,QAAQ,CAEtB,CAAC;AAEF,gBAAgB;AAEhB,eAAO,MAAM,SAAS,WAAY,GAAG,aAAY,OAAO,KAAU,wBAWjE,CAAC;AAEF,gBAAgB;AAChB,eAAO,MAAM,oBAAoB,YACtB,WAAW,cACR,MAAM,YACR,MAAM,WACP,MAAM,KACd,YAWF,CAAC;AAEF,wBAAsB,eAAe,CACnC,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,kBAAkB,CAAC,CAsD7B;AA+CD,UAAU,OAAO;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,KAAK,CAAC,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;CAC1C;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAuBpF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,EAAE,QAAQ,GAAE,OAAc,GAAG,WAAW,CAKhG"}
|
package/dist/llm/utils.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { VideoBufferType, VideoFrame } from "@livekit/rtc-node";
|
|
2
2
|
import sharp from "sharp";
|
|
3
|
-
import { ZodObject } from "zod";
|
|
4
|
-
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
5
3
|
import {
|
|
6
4
|
FunctionCall,
|
|
7
5
|
FunctionCallOutput
|
|
8
6
|
} from "./chat_context.js";
|
|
7
|
+
import { isZodSchema, parseZodSchema, zodSchemaToJsonSchema } from "./zod-utils.js";
|
|
9
8
|
function getChannelsFromVideoBufferType(type) {
|
|
10
9
|
switch (type) {
|
|
11
10
|
case VideoBufferType.RGBA:
|
|
@@ -92,11 +91,9 @@ async function serializeImage(image) {
|
|
|
92
91
|
const createToolOptions = (toolCallId, userData = {}) => {
|
|
93
92
|
return { ctx: { userData }, toolCallId };
|
|
94
93
|
};
|
|
95
|
-
const oaiParams = (
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
target: isOpenai ? "openAi" : "jsonSchema7"
|
|
99
|
-
});
|
|
94
|
+
const oaiParams = (schema, isOpenai = true) => {
|
|
95
|
+
const jsonSchema = zodSchemaToJsonSchema(schema, isOpenai);
|
|
96
|
+
const { properties, required, additionalProperties } = jsonSchema;
|
|
100
97
|
return {
|
|
101
98
|
type: "object",
|
|
102
99
|
properties,
|
|
@@ -129,8 +126,17 @@ async function executeToolCall(toolCall, toolCtx) {
|
|
|
129
126
|
});
|
|
130
127
|
}
|
|
131
128
|
try {
|
|
132
|
-
if (tool.parameters
|
|
133
|
-
|
|
129
|
+
if (isZodSchema(tool.parameters)) {
|
|
130
|
+
const result = await parseZodSchema(tool.parameters, args);
|
|
131
|
+
if (result.success) {
|
|
132
|
+
params = result.data;
|
|
133
|
+
} else {
|
|
134
|
+
return FunctionCallOutput.create({
|
|
135
|
+
callId: toolCall.callId,
|
|
136
|
+
output: `Arguments parsing failed: ${result.error}`,
|
|
137
|
+
isError: true
|
|
138
|
+
});
|
|
139
|
+
}
|
|
134
140
|
} else {
|
|
135
141
|
params = args;
|
|
136
142
|
}
|
|
@@ -207,8 +213,8 @@ function computeChatCtxDiff(oldCtx, newCtx) {
|
|
|
207
213
|
};
|
|
208
214
|
}
|
|
209
215
|
function toJsonSchema(schema, isOpenai = true) {
|
|
210
|
-
if (schema
|
|
211
|
-
return
|
|
216
|
+
if (isZodSchema(schema)) {
|
|
217
|
+
return zodSchemaToJsonSchema(schema, isOpenai);
|
|
212
218
|
}
|
|
213
219
|
return schema;
|
|
214
220
|
}
|
package/dist/llm/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/llm/utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { VideoBufferType, VideoFrame } from '@livekit/rtc-node';\nimport type { JSONSchema7 } from 'json-schema';\nimport sharp from 'sharp';\nimport { ZodObject } from 'zod';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport type { UnknownUserData } from '../voice/run_context.js';\nimport type { ChatContext } from './chat_context.js';\nimport {\n type ChatItem,\n FunctionCall,\n FunctionCallOutput,\n type ImageContent,\n} from './chat_context.js';\nimport type { ToolContext, ToolInputSchema, ToolOptions } from './tool_context.js';\n\nexport interface SerializedImage {\n inferenceDetail: 'auto' | 'high' | 'low';\n mimeType?: string;\n base64Data?: string;\n externalUrl?: string;\n}\n\nfunction getChannelsFromVideoBufferType(type: VideoBufferType): 3 | 4 {\n switch (type) {\n case VideoBufferType.RGBA:\n case VideoBufferType.ABGR:\n case VideoBufferType.ARGB:\n case VideoBufferType.BGRA:\n return 4;\n case VideoBufferType.RGB24:\n return 3;\n default:\n // YUV formats (I420, I420A, I422, I444, I010, NV12) need conversion\n throw new Error(`Unsupported VideoBufferType: ${type}. Only RGB/RGBA formats are supported.`);\n }\n}\n\nfunction ensureRGBCompatible(frame: VideoFrame): VideoFrame {\n // If the frame is already in an RGB/RGBA-compatible format, return it directly\n if (\n frame.type === VideoBufferType.RGBA ||\n frame.type === VideoBufferType.BGRA ||\n frame.type === VideoBufferType.ARGB ||\n frame.type === VideoBufferType.ABGR ||\n frame.type === VideoBufferType.RGB24\n ) {\n return frame;\n }\n\n // Otherwise, attempt conversion for other formats (like YUV)\n try {\n return frame.convert(VideoBufferType.RGBA);\n } catch (error) {\n throw new Error(\n `Failed to convert format ${frame.type} to RGB: ${error}. ` +\n `Consider using RGB/RGBA formats or converting on the client side.`,\n );\n }\n}\n\nexport async function serializeImage(image: ImageContent): Promise<SerializedImage> {\n if (typeof image.image === 'string') {\n if (image.image.startsWith('data:')) {\n const [header, base64Data] = image.image.split(',', 2) as [string, string];\n const headerParts = header.split(';');\n const mimeParts = headerParts[0]?.split(':');\n const headerMime = mimeParts?.[1];\n\n if (!headerMime) {\n throw new Error('Invalid data URL format');\n }\n\n let mimeType: string;\n if (image.mimeType && image.mimeType !== headerMime) {\n console.warn(\n `Provided mimeType '${image.mimeType}' does not match data URL mime type '${headerMime}'. Using provided mimeType.`,\n );\n mimeType = image.mimeType;\n } else {\n mimeType = headerMime;\n }\n\n const supportedTypes = new Set(['image/jpeg', 'image/png', 'image/webp', 'image/gif']);\n if (!supportedTypes.has(mimeType)) {\n throw new Error(`Unsupported mimeType ${mimeType}. Must be jpeg, png, webp, or gif`);\n }\n\n return {\n base64Data,\n mimeType: mimeType,\n inferenceDetail: image.inferenceDetail,\n };\n }\n\n // External URL\n return {\n mimeType: image.mimeType,\n inferenceDetail: image.inferenceDetail,\n externalUrl: image.image,\n };\n } else if (image.image instanceof VideoFrame) {\n const frame = ensureRGBCompatible(image.image);\n const channels = getChannelsFromVideoBufferType(frame.type);\n\n // Sharp needs to know the format of raw pixel data\n let encoded = sharp(frame.data, {\n raw: {\n width: frame.width,\n height: frame.height,\n channels,\n },\n });\n\n if (image.inferenceWidth && image.inferenceHeight) {\n encoded = encoded.resize(image.inferenceWidth, image.inferenceHeight);\n }\n\n const base64Data = await encoded\n .png()\n .toBuffer()\n .then((buffer) => buffer.toString('base64'));\n\n return {\n base64Data,\n mimeType: 'image/png',\n inferenceDetail: image.inferenceDetail,\n };\n } else {\n throw new Error('Unsupported image type');\n }\n}\n\n/** Raw OpenAI-adherent function parameters. */\nexport type OpenAIFunctionParameters = {\n type: 'object';\n properties: { [id: string]: any }; // eslint-disable-line @typescript-eslint/no-explicit-any\n required: string[];\n additionalProperties?: boolean;\n};\n\n// TODO(brian): remove this helper once we have the real RunContext user data\nexport const createToolOptions = <UserData extends UnknownUserData>(\n toolCallId: string,\n userData: UserData = {} as UserData,\n): ToolOptions<UserData> => {\n return { ctx: { userData }, toolCallId } as unknown as ToolOptions<UserData>;\n};\n\n/** @internal */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const oaiParams = (\n p: ZodObject<any>,\n isOpenai: boolean = true,\n): OpenAIFunctionParameters => {\n // Adapted from https://github.com/vercel/ai/blob/56eb0ee9/packages/provider-utils/src/zod-schema.ts\n const { properties, required, additionalProperties } = zodToJsonSchema(p, {\n // note: openai mode breaks various gemini conversions\n target: isOpenai ? 'openAi' : 'jsonSchema7',\n }) as OpenAIFunctionParameters;\n\n return {\n type: 'object',\n properties,\n required,\n additionalProperties,\n };\n};\n\n/** @internal */\nexport const oaiBuildFunctionInfo = (\n toolCtx: ToolContext,\n toolCallId: string,\n toolName: string,\n rawArgs: string,\n): FunctionCall => {\n const tool = toolCtx[toolName];\n if (!tool) {\n throw new Error(`AI tool ${toolName} not found`);\n }\n\n return FunctionCall.create({\n callId: toolCallId,\n name: toolName,\n args: rawArgs,\n });\n};\n\nexport async function executeToolCall(\n toolCall: FunctionCall,\n toolCtx: ToolContext,\n): Promise<FunctionCallOutput> {\n const tool = toolCtx[toolCall.name]!;\n let args: object | undefined;\n let params: object | undefined;\n\n // Ensure valid JSON\n try {\n args = JSON.parse(toolCall.args);\n } catch (error) {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Invalid JSON: ${error}`,\n isError: true,\n });\n }\n\n // Ensure valid arguments schema\n try {\n if (tool.parameters instanceof ZodObject) {\n params = tool.parameters.parse(args);\n } else {\n params = args;\n }\n } catch (error) {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Arguments parsing failed: ${error}`,\n isError: true,\n });\n }\n\n try {\n const result = await tool.execute(params, createToolOptions(toolCall.callId));\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: JSON.stringify(result),\n isError: false,\n });\n } catch (error) {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Tool execution failed: ${error}`,\n isError: true,\n });\n }\n}\n\n/**\n * Standard dynamic-programming LCS to get the common subsequence\n * of IDs (in order) that appear in both old_ids and new_ids.\n *\n * @param oldIds - The old list of IDs.\n * @param newIds - The new list of IDs.\n * @returns The longest common subsequence of the two lists of IDs.\n */\nfunction computeLCS(oldIds: string[], newIds: string[]): string[] {\n const n = oldIds.length;\n const m = newIds.length;\n const dp: number[][] = Array(n + 1)\n .fill(null)\n .map(() => Array(m + 1).fill(0));\n\n // Fill DP table\n for (let i = 1; i <= n; i++) {\n for (let j = 1; j <= m; j++) {\n if (oldIds[i - 1] === newIds[j - 1]) {\n dp[i]![j] = dp[i - 1]![j - 1]! + 1;\n } else {\n dp[i]![j] = Math.max(dp[i - 1]![j]!, dp[i]![j - 1]!);\n }\n }\n }\n\n // Backtrack to find the actual LCS sequence\n const lcsIds: string[] = [];\n let i = n;\n let j = m;\n while (i > 0 && j > 0) {\n if (oldIds[i - 1] === newIds[j - 1]) {\n lcsIds.push(oldIds[i - 1]!);\n i--;\n j--;\n } else if (dp[i - 1]![j]! > dp[i]![j - 1]!) {\n i--;\n } else {\n j--;\n }\n }\n\n return lcsIds.reverse();\n}\n\ninterface DiffOps {\n toRemove: string[];\n toCreate: Array<[string | null, string]>; // (previous_item_id, id), if previous_item_id is null, add to the root\n}\n\n/**\n * Compute the minimal list of create/remove operations to transform oldCtx into newCtx.\n *\n * @param oldCtx - The old chat context.\n * @param newCtx - The new chat context.\n * @returns The minimal list of create/remove operations to transform oldCtx into newCtx.\n */\nexport function computeChatCtxDiff(oldCtx: ChatContext, newCtx: ChatContext): DiffOps {\n const oldIds = oldCtx.items.map((item: ChatItem) => item.id);\n const newIds = newCtx.items.map((item: ChatItem) => item.id);\n const lcsIds = new Set(computeLCS(oldIds, newIds));\n\n const toRemove = oldCtx.items.filter((msg) => !lcsIds.has(msg.id)).map((msg) => msg.id);\n const toCreate: Array<[string | null, string]> = [];\n\n let lastIdInSequence: string | null = null;\n for (const newItem of newCtx.items) {\n if (lcsIds.has(newItem.id)) {\n lastIdInSequence = newItem.id;\n } else {\n const prevId = lastIdInSequence; // null if root\n toCreate.push([prevId, newItem.id]);\n lastIdInSequence = newItem.id;\n }\n }\n\n return {\n toRemove,\n toCreate,\n };\n}\n\nexport function toJsonSchema(schema: ToolInputSchema<any>, isOpenai: boolean = true): JSONSchema7 {\n if (schema instanceof ZodObject) {\n return oaiParams(schema, isOpenai);\n }\n return schema;\n}\n"],"mappings":"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","names":["i","j"]}
|
|
1
|
+
{"version":3,"sources":["../../src/llm/utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { VideoBufferType, VideoFrame } from '@livekit/rtc-node';\nimport type { JSONSchema7 } from 'json-schema';\nimport sharp from 'sharp';\nimport type { UnknownUserData } from '../voice/run_context.js';\nimport type { ChatContext } from './chat_context.js';\nimport {\n type ChatItem,\n FunctionCall,\n FunctionCallOutput,\n type ImageContent,\n} from './chat_context.js';\nimport type { ToolContext, ToolInputSchema, ToolOptions } from './tool_context.js';\nimport { isZodSchema, parseZodSchema, zodSchemaToJsonSchema } from './zod-utils.js';\n\nexport interface SerializedImage {\n inferenceDetail: 'auto' | 'high' | 'low';\n mimeType?: string;\n base64Data?: string;\n externalUrl?: string;\n}\n\nfunction getChannelsFromVideoBufferType(type: VideoBufferType): 3 | 4 {\n switch (type) {\n case VideoBufferType.RGBA:\n case VideoBufferType.ABGR:\n case VideoBufferType.ARGB:\n case VideoBufferType.BGRA:\n return 4;\n case VideoBufferType.RGB24:\n return 3;\n default:\n // YUV formats (I420, I420A, I422, I444, I010, NV12) need conversion\n throw new Error(`Unsupported VideoBufferType: ${type}. Only RGB/RGBA formats are supported.`);\n }\n}\n\nfunction ensureRGBCompatible(frame: VideoFrame): VideoFrame {\n // If the frame is already in an RGB/RGBA-compatible format, return it directly\n if (\n frame.type === VideoBufferType.RGBA ||\n frame.type === VideoBufferType.BGRA ||\n frame.type === VideoBufferType.ARGB ||\n frame.type === VideoBufferType.ABGR ||\n frame.type === VideoBufferType.RGB24\n ) {\n return frame;\n }\n\n // Otherwise, attempt conversion for other formats (like YUV)\n try {\n return frame.convert(VideoBufferType.RGBA);\n } catch (error) {\n throw new Error(\n `Failed to convert format ${frame.type} to RGB: ${error}. ` +\n `Consider using RGB/RGBA formats or converting on the client side.`,\n );\n }\n}\n\nexport async function serializeImage(image: ImageContent): Promise<SerializedImage> {\n if (typeof image.image === 'string') {\n if (image.image.startsWith('data:')) {\n const [header, base64Data] = image.image.split(',', 2) as [string, string];\n const headerParts = header.split(';');\n const mimeParts = headerParts[0]?.split(':');\n const headerMime = mimeParts?.[1];\n\n if (!headerMime) {\n throw new Error('Invalid data URL format');\n }\n\n let mimeType: string;\n if (image.mimeType && image.mimeType !== headerMime) {\n console.warn(\n `Provided mimeType '${image.mimeType}' does not match data URL mime type '${headerMime}'. Using provided mimeType.`,\n );\n mimeType = image.mimeType;\n } else {\n mimeType = headerMime;\n }\n\n const supportedTypes = new Set(['image/jpeg', 'image/png', 'image/webp', 'image/gif']);\n if (!supportedTypes.has(mimeType)) {\n throw new Error(`Unsupported mimeType ${mimeType}. Must be jpeg, png, webp, or gif`);\n }\n\n return {\n base64Data,\n mimeType: mimeType,\n inferenceDetail: image.inferenceDetail,\n };\n }\n\n // External URL\n return {\n mimeType: image.mimeType,\n inferenceDetail: image.inferenceDetail,\n externalUrl: image.image,\n };\n } else if (image.image instanceof VideoFrame) {\n const frame = ensureRGBCompatible(image.image);\n const channels = getChannelsFromVideoBufferType(frame.type);\n\n // Sharp needs to know the format of raw pixel data\n let encoded = sharp(frame.data, {\n raw: {\n width: frame.width,\n height: frame.height,\n channels,\n },\n });\n\n if (image.inferenceWidth && image.inferenceHeight) {\n encoded = encoded.resize(image.inferenceWidth, image.inferenceHeight);\n }\n\n const base64Data = await encoded\n .png()\n .toBuffer()\n .then((buffer) => buffer.toString('base64'));\n\n return {\n base64Data,\n mimeType: 'image/png',\n inferenceDetail: image.inferenceDetail,\n };\n } else {\n throw new Error('Unsupported image type');\n }\n}\n\n/** Raw OpenAI-adherent function parameters. */\nexport type OpenAIFunctionParameters = {\n type: 'object';\n properties: { [id: string]: any }; // eslint-disable-line @typescript-eslint/no-explicit-any\n required: string[];\n additionalProperties?: boolean;\n};\n\n// TODO(brian): remove this helper once we have the real RunContext user data\nexport const createToolOptions = <UserData extends UnknownUserData>(\n toolCallId: string,\n userData: UserData = {} as UserData,\n): ToolOptions<UserData> => {\n return { ctx: { userData }, toolCallId } as unknown as ToolOptions<UserData>;\n};\n\n/** @internal */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const oaiParams = (schema: any, isOpenai: boolean = true): OpenAIFunctionParameters => {\n // Adapted from https://github.com/vercel/ai/blob/56eb0ee9/packages/provider-utils/src/zod-schema.ts\n const jsonSchema = zodSchemaToJsonSchema(schema, isOpenai);\n const { properties, required, additionalProperties } = jsonSchema as OpenAIFunctionParameters;\n\n return {\n type: 'object',\n properties,\n required,\n additionalProperties,\n };\n};\n\n/** @internal */\nexport const oaiBuildFunctionInfo = (\n toolCtx: ToolContext,\n toolCallId: string,\n toolName: string,\n rawArgs: string,\n): FunctionCall => {\n const tool = toolCtx[toolName];\n if (!tool) {\n throw new Error(`AI tool ${toolName} not found`);\n }\n\n return FunctionCall.create({\n callId: toolCallId,\n name: toolName,\n args: rawArgs,\n });\n};\n\nexport async function executeToolCall(\n toolCall: FunctionCall,\n toolCtx: ToolContext,\n): Promise<FunctionCallOutput> {\n const tool = toolCtx[toolCall.name]!;\n let args: object | undefined;\n let params: object | undefined;\n\n // Ensure valid JSON\n try {\n args = JSON.parse(toolCall.args);\n } catch (error) {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Invalid JSON: ${error}`,\n isError: true,\n });\n }\n\n // Ensure valid arguments schema\n try {\n if (isZodSchema(tool.parameters)) {\n const result = await parseZodSchema<object>(tool.parameters, args);\n if (result.success) {\n params = result.data;\n } else {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Arguments parsing failed: ${result.error}`,\n isError: true,\n });\n }\n } else {\n params = args;\n }\n } catch (error) {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Arguments parsing failed: ${error}`,\n isError: true,\n });\n }\n\n try {\n const result = await tool.execute(params, createToolOptions(toolCall.callId));\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: JSON.stringify(result),\n isError: false,\n });\n } catch (error) {\n return FunctionCallOutput.create({\n callId: toolCall.callId,\n output: `Tool execution failed: ${error}`,\n isError: true,\n });\n }\n}\n\n/**\n * Standard dynamic-programming LCS to get the common subsequence\n * of IDs (in order) that appear in both old_ids and new_ids.\n *\n * @param oldIds - The old list of IDs.\n * @param newIds - The new list of IDs.\n * @returns The longest common subsequence of the two lists of IDs.\n */\nfunction computeLCS(oldIds: string[], newIds: string[]): string[] {\n const n = oldIds.length;\n const m = newIds.length;\n const dp: number[][] = Array(n + 1)\n .fill(null)\n .map(() => Array(m + 1).fill(0));\n\n // Fill DP table\n for (let i = 1; i <= n; i++) {\n for (let j = 1; j <= m; j++) {\n if (oldIds[i - 1] === newIds[j - 1]) {\n dp[i]![j] = dp[i - 1]![j - 1]! + 1;\n } else {\n dp[i]![j] = Math.max(dp[i - 1]![j]!, dp[i]![j - 1]!);\n }\n }\n }\n\n // Backtrack to find the actual LCS sequence\n const lcsIds: string[] = [];\n let i = n;\n let j = m;\n while (i > 0 && j > 0) {\n if (oldIds[i - 1] === newIds[j - 1]) {\n lcsIds.push(oldIds[i - 1]!);\n i--;\n j--;\n } else if (dp[i - 1]![j]! > dp[i]![j - 1]!) {\n i--;\n } else {\n j--;\n }\n }\n\n return lcsIds.reverse();\n}\n\ninterface DiffOps {\n toRemove: string[];\n toCreate: Array<[string | null, string]>; // (previous_item_id, id), if previous_item_id is null, add to the root\n}\n\n/**\n * Compute the minimal list of create/remove operations to transform oldCtx into newCtx.\n *\n * @param oldCtx - The old chat context.\n * @param newCtx - The new chat context.\n * @returns The minimal list of create/remove operations to transform oldCtx into newCtx.\n */\nexport function computeChatCtxDiff(oldCtx: ChatContext, newCtx: ChatContext): DiffOps {\n const oldIds = oldCtx.items.map((item: ChatItem) => item.id);\n const newIds = newCtx.items.map((item: ChatItem) => item.id);\n const lcsIds = new Set(computeLCS(oldIds, newIds));\n\n const toRemove = oldCtx.items.filter((msg) => !lcsIds.has(msg.id)).map((msg) => msg.id);\n const toCreate: Array<[string | null, string]> = [];\n\n let lastIdInSequence: string | null = null;\n for (const newItem of newCtx.items) {\n if (lcsIds.has(newItem.id)) {\n lastIdInSequence = newItem.id;\n } else {\n const prevId = lastIdInSequence; // null if root\n toCreate.push([prevId, newItem.id]);\n lastIdInSequence = newItem.id;\n }\n }\n\n return {\n toRemove,\n toCreate,\n };\n}\n\nexport function toJsonSchema(schema: ToolInputSchema<any>, isOpenai: boolean = true): JSONSchema7 {\n if (isZodSchema(schema)) {\n return zodSchemaToJsonSchema(schema, isOpenai);\n }\n return schema as JSONSchema7;\n}\n"],"mappings":"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","names":["i","j"]}
|