@livekit/agents-plugin-openai 0.6.1 → 0.7.0

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.js CHANGED
@@ -1,456 +1,467 @@
1
- // SPDX-FileCopyrightText: 2024 LiveKit, Inc.
2
- //
3
- // SPDX-License-Identifier: Apache-2.0
4
- import { llm, log } from '@livekit/agents';
5
- import { randomUUID } from 'node:crypto';
6
- import { AzureOpenAI, OpenAI } from 'openai';
7
- import sharp from 'sharp';
1
+ import { llm, log } from "@livekit/agents";
2
+ import { randomUUID } from "node:crypto";
3
+ import { AzureOpenAI, OpenAI } from "openai";
4
+ import sharp from "sharp";
8
5
  const defaultLLMOptions = {
9
- model: 'gpt-4o',
10
- apiKey: process.env.OPENAI_API_KEY,
6
+ model: "gpt-4o",
7
+ apiKey: process.env.OPENAI_API_KEY
11
8
  };
12
9
  const defaultAzureLLMOptions = {
13
- model: 'gpt-4o',
14
- apiKey: process.env.AZURE_API_KEY,
10
+ model: "gpt-4o",
11
+ apiKey: process.env.AZURE_API_KEY
15
12
  };
16
- export class LLM extends llm.LLM {
17
- #opts;
18
- #client;
19
- /**
20
- * Create a new instance of OpenAI LLM.
21
- *
22
- * @remarks
23
- * `apiKey` must be set to your OpenAI API key, either using the argument or by setting the
24
- * `OPENAI_API_KEY` environmental variable.
25
- */
26
- constructor(opts = defaultLLMOptions) {
27
- super();
28
- this.#opts = { ...defaultLLMOptions, ...opts };
29
- if (this.#opts.apiKey === undefined) {
30
- throw new Error('OpenAI API key is required, whether as an argument or as $OPENAI_API_KEY');
31
- }
32
- this.#client =
33
- this.#opts.client ||
34
- new OpenAI({
35
- baseURL: opts.baseURL,
36
- apiKey: opts.apiKey,
37
- });
38
- }
39
- /**
40
- * Create a new instance of OpenAI LLM with Azure.
41
- *
42
- * @remarks
43
- * This automatically infers the following arguments from their corresponding environment variables if they are not provided:
44
- * - `apiKey` from `AZURE_OPENAI_API_KEY`
45
- * - `organization` from `OPENAI_ORG_ID`
46
- * - `project` from `OPENAI_PROJECT_ID`
47
- * - `azureAdToken` from `AZURE_OPENAI_AD_TOKEN`
48
- * - `apiVersion` from `OPENAI_API_VERSION`
49
- * - `azureEndpoint` from `AZURE_OPENAI_ENDPOINT`
50
- */
51
- static withAzure(opts = defaultAzureLLMOptions) {
52
- opts = { ...defaultLLMOptions, ...opts };
53
- if (opts.apiKey === undefined) {
54
- throw new Error('Azure API key is required, whether as an argument or as $AZURE_API_KEY');
55
- }
56
- return new LLM({
57
- temperature: opts.temperature,
58
- user: opts.user,
59
- client: new AzureOpenAI(opts),
60
- });
13
+ class LLM extends llm.LLM {
14
+ #opts;
15
+ #client;
16
+ /**
17
+ * Create a new instance of OpenAI LLM.
18
+ *
19
+ * @remarks
20
+ * `apiKey` must be set to your OpenAI API key, either using the argument or by setting the
21
+ * `OPENAI_API_KEY` environmental variable.
22
+ */
23
+ constructor(opts = defaultLLMOptions) {
24
+ super();
25
+ this.#opts = { ...defaultLLMOptions, ...opts };
26
+ if (this.#opts.apiKey === void 0) {
27
+ throw new Error("OpenAI API key is required, whether as an argument or as $OPENAI_API_KEY");
61
28
  }
62
- /**
63
- * Create a new instance of Cerebras LLM.
64
- *
65
- * @remarks
66
- * `apiKey` must be set to your Cerebras API key, either using the argument or by setting the
67
- * `CEREBRAS_API_KEY` environmental variable.
68
- */
69
- static withCerebras(opts = {}) {
70
- opts.apiKey = opts.apiKey || process.env.CEREBRAS_API_KEY;
71
- if (opts.apiKey === undefined) {
72
- throw new Error('Cerebras API key is required, whether as an argument or as $CEREBRAS_API_KEY');
73
- }
74
- return new LLM({
75
- model: 'llama3.1-8b',
76
- baseURL: 'https://api.cerebras.ai/v1',
77
- ...opts,
78
- });
29
+ this.#client = this.#opts.client || new OpenAI({
30
+ baseURL: opts.baseURL,
31
+ apiKey: opts.apiKey
32
+ });
33
+ }
34
+ /**
35
+ * Create a new instance of OpenAI LLM with Azure.
36
+ *
37
+ * @remarks
38
+ * This automatically infers the following arguments from their corresponding environment variables if they are not provided:
39
+ * - `apiKey` from `AZURE_OPENAI_API_KEY`
40
+ * - `organization` from `OPENAI_ORG_ID`
41
+ * - `project` from `OPENAI_PROJECT_ID`
42
+ * - `azureAdToken` from `AZURE_OPENAI_AD_TOKEN`
43
+ * - `apiVersion` from `OPENAI_API_VERSION`
44
+ * - `azureEndpoint` from `AZURE_OPENAI_ENDPOINT`
45
+ */
46
+ static withAzure(opts = defaultAzureLLMOptions) {
47
+ opts = { ...defaultLLMOptions, ...opts };
48
+ if (opts.apiKey === void 0) {
49
+ throw new Error("Azure API key is required, whether as an argument or as $AZURE_API_KEY");
79
50
  }
80
- /**
81
- * Create a new instance of Fireworks LLM.
82
- *
83
- * @remarks
84
- * `apiKey` must be set to your Fireworks API key, either using the argument or by setting the
85
- * `FIREWORKS_API_KEY` environmental variable.
86
- */
87
- static withFireworks(opts = {}) {
88
- opts.apiKey = opts.apiKey || process.env.FIREWORKS_API_KEY;
89
- if (opts.apiKey === undefined) {
90
- throw new Error('Fireworks API key is required, whether as an argument or as $FIREWORKS_API_KEY');
91
- }
92
- return new LLM({
93
- model: 'accounts/fireworks/models/llama-v3p1-70b-instruct',
94
- baseURL: 'https://api.fireworks.ai/inference/v1',
95
- ...opts,
96
- });
97
- }
98
- /**
99
- * Create a new instance of xAI LLM.
100
- *
101
- * @remarks
102
- * `apiKey` must be set to your xAI API key, either using the argument or by setting the
103
- * `XAI_API_KEY` environmental variable.
104
- */
105
- static withXAI(opts = {}) {
106
- opts.apiKey = opts.apiKey || process.env.XAI_API_KEY;
107
- if (opts.apiKey === undefined) {
108
- throw new Error('xAI API key is required, whether as an argument or as $XAI_API_KEY');
109
- }
110
- return new LLM({
111
- model: 'grok-2-public',
112
- baseURL: 'https://api.x.ai/v1',
113
- ...opts,
114
- });
51
+ return new LLM({
52
+ temperature: opts.temperature,
53
+ user: opts.user,
54
+ client: new AzureOpenAI(opts)
55
+ });
56
+ }
57
+ /**
58
+ * Create a new instance of Cerebras LLM.
59
+ *
60
+ * @remarks
61
+ * `apiKey` must be set to your Cerebras API key, either using the argument or by setting the
62
+ * `CEREBRAS_API_KEY` environmental variable.
63
+ */
64
+ static withCerebras(opts = {}) {
65
+ opts.apiKey = opts.apiKey || process.env.CEREBRAS_API_KEY;
66
+ if (opts.apiKey === void 0) {
67
+ throw new Error(
68
+ "Cerebras API key is required, whether as an argument or as $CEREBRAS_API_KEY"
69
+ );
115
70
  }
116
- /**
117
- * Create a new instance of Groq LLM.
118
- *
119
- * @remarks
120
- * `apiKey` must be set to your Groq API key, either using the argument or by setting the
121
- * `GROQ_API_KEY` environmental variable.
122
- */
123
- static withGroq(opts = {}) {
124
- opts.apiKey = opts.apiKey || process.env.GROQ_API_KEY;
125
- if (opts.apiKey === undefined) {
126
- throw new Error('Groq API key is required, whether as an argument or as $GROQ_API_KEY');
127
- }
128
- return new LLM({
129
- model: 'llama3-8b-8192',
130
- baseURL: 'https://api.groq.com/openai/v1',
131
- ...opts,
132
- });
71
+ return new LLM({
72
+ model: "llama3.1-8b",
73
+ baseURL: "https://api.cerebras.ai/v1",
74
+ ...opts
75
+ });
76
+ }
77
+ /**
78
+ * Create a new instance of Fireworks LLM.
79
+ *
80
+ * @remarks
81
+ * `apiKey` must be set to your Fireworks API key, either using the argument or by setting the
82
+ * `FIREWORKS_API_KEY` environmental variable.
83
+ */
84
+ static withFireworks(opts = {}) {
85
+ opts.apiKey = opts.apiKey || process.env.FIREWORKS_API_KEY;
86
+ if (opts.apiKey === void 0) {
87
+ throw new Error(
88
+ "Fireworks API key is required, whether as an argument or as $FIREWORKS_API_KEY"
89
+ );
133
90
  }
134
- /**
135
- * Create a new instance of DeepSeek LLM.
136
- *
137
- * @remarks
138
- * `apiKey` must be set to your DeepSeek API key, either using the argument or by setting the
139
- * `DEEPSEEK_API_KEY` environmental variable.
140
- */
141
- static withDeepSeek(opts = {}) {
142
- opts.apiKey = opts.apiKey || process.env.DEEPSEEK_API_KEY;
143
- if (opts.apiKey === undefined) {
144
- throw new Error('DeepSeek API key is required, whether as an argument or as $DEEPSEEK_API_KEY');
145
- }
146
- return new LLM({
147
- model: 'deepseek-chat',
148
- baseURL: 'https://api.deepseek.com/v1',
149
- ...opts,
150
- });
91
+ return new LLM({
92
+ model: "accounts/fireworks/models/llama-v3p1-70b-instruct",
93
+ baseURL: "https://api.fireworks.ai/inference/v1",
94
+ ...opts
95
+ });
96
+ }
97
+ /**
98
+ * Create a new instance of xAI LLM.
99
+ *
100
+ * @remarks
101
+ * `apiKey` must be set to your xAI API key, either using the argument or by setting the
102
+ * `XAI_API_KEY` environmental variable.
103
+ */
104
+ static withXAI(opts = {}) {
105
+ opts.apiKey = opts.apiKey || process.env.XAI_API_KEY;
106
+ if (opts.apiKey === void 0) {
107
+ throw new Error("xAI API key is required, whether as an argument or as $XAI_API_KEY");
151
108
  }
152
- /**
153
- * Create a new instance of OctoAI LLM.
154
- *
155
- * @remarks
156
- * `apiKey` must be set to your OctoAI API key, either using the argument or by setting the
157
- * `OCTOAI_TOKEN` environmental variable.
158
- */
159
- static withOcto(opts = {}) {
160
- opts.apiKey = opts.apiKey || process.env.OCTOAI_TOKEN;
161
- if (opts.apiKey === undefined) {
162
- throw new Error('OctoAI API key is required, whether as an argument or as $OCTOAI_TOKEN');
163
- }
164
- return new LLM({
165
- model: 'llama-2-13b-chat',
166
- baseURL: 'https://text.octoai.run/v1',
167
- ...opts,
168
- });
109
+ return new LLM({
110
+ model: "grok-2-public",
111
+ baseURL: "https://api.x.ai/v1",
112
+ ...opts
113
+ });
114
+ }
115
+ /**
116
+ * Create a new instance of Groq LLM.
117
+ *
118
+ * @remarks
119
+ * `apiKey` must be set to your Groq API key, either using the argument or by setting the
120
+ * `GROQ_API_KEY` environmental variable.
121
+ */
122
+ static withGroq(opts = {}) {
123
+ opts.apiKey = opts.apiKey || process.env.GROQ_API_KEY;
124
+ if (opts.apiKey === void 0) {
125
+ throw new Error("Groq API key is required, whether as an argument or as $GROQ_API_KEY");
169
126
  }
170
- /** Create a new instance of Ollama LLM. */
171
- static withOllama(opts = {}) {
172
- return new LLM({
173
- model: 'llama-2-13b-chat',
174
- baseURL: 'https://text.octoai.run/v1',
175
- apiKey: 'ollama',
176
- ...opts,
177
- });
127
+ return new LLM({
128
+ model: "llama3-8b-8192",
129
+ baseURL: "https://api.groq.com/openai/v1",
130
+ ...opts
131
+ });
132
+ }
133
+ /**
134
+ * Create a new instance of DeepSeek LLM.
135
+ *
136
+ * @remarks
137
+ * `apiKey` must be set to your DeepSeek API key, either using the argument or by setting the
138
+ * `DEEPSEEK_API_KEY` environmental variable.
139
+ */
140
+ static withDeepSeek(opts = {}) {
141
+ opts.apiKey = opts.apiKey || process.env.DEEPSEEK_API_KEY;
142
+ if (opts.apiKey === void 0) {
143
+ throw new Error(
144
+ "DeepSeek API key is required, whether as an argument or as $DEEPSEEK_API_KEY"
145
+ );
178
146
  }
179
- /**
180
- * Create a new instance of PerplexityAI LLM.
181
- *
182
- * @remarks
183
- * `apiKey` must be set to your PerplexityAI API key, either using the argument or by setting the
184
- * `PERPLEXITY_API_KEY` environmental variable.
185
- */
186
- static withPerplexity(opts = {}) {
187
- opts.apiKey = opts.apiKey || process.env.PERPLEXITY_API_KEY;
188
- if (opts.apiKey === undefined) {
189
- throw new Error('PerplexityAI API key is required, whether as an argument or as $PERPLEXITY_API_KEY');
190
- }
191
- return new LLM({
192
- model: 'llama-3.1-sonar-small-128k-chat',
193
- baseURL: 'https://api.perplexity.ai',
194
- ...opts,
195
- });
147
+ return new LLM({
148
+ model: "deepseek-chat",
149
+ baseURL: "https://api.deepseek.com/v1",
150
+ ...opts
151
+ });
152
+ }
153
+ /**
154
+ * Create a new instance of OctoAI LLM.
155
+ *
156
+ * @remarks
157
+ * `apiKey` must be set to your OctoAI API key, either using the argument or by setting the
158
+ * `OCTOAI_TOKEN` environmental variable.
159
+ */
160
+ static withOcto(opts = {}) {
161
+ opts.apiKey = opts.apiKey || process.env.OCTOAI_TOKEN;
162
+ if (opts.apiKey === void 0) {
163
+ throw new Error("OctoAI API key is required, whether as an argument or as $OCTOAI_TOKEN");
196
164
  }
197
- /**
198
- * Create a new instance of TogetherAI LLM.
199
- *
200
- * @remarks
201
- * `apiKey` must be set to your TogetherAI API key, either using the argument or by setting the
202
- * `TOGETHER_API_KEY` environmental variable.
203
- */
204
- static withTogether(opts = {}) {
205
- opts.apiKey = opts.apiKey || process.env.TOGETHER_API_KEY;
206
- if (opts.apiKey === undefined) {
207
- throw new Error('TogetherAI API key is required, whether as an argument or as $TOGETHER_API_KEY');
208
- }
209
- return new LLM({
210
- model: 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo',
211
- baseURL: 'https://api.together.xyz/v1',
212
- ...opts,
213
- });
165
+ return new LLM({
166
+ model: "llama-2-13b-chat",
167
+ baseURL: "https://text.octoai.run/v1",
168
+ ...opts
169
+ });
170
+ }
171
+ /** Create a new instance of Ollama LLM. */
172
+ static withOllama(opts = {}) {
173
+ return new LLM({
174
+ model: "llama-2-13b-chat",
175
+ baseURL: "https://text.octoai.run/v1",
176
+ apiKey: "ollama",
177
+ ...opts
178
+ });
179
+ }
180
+ /**
181
+ * Create a new instance of PerplexityAI LLM.
182
+ *
183
+ * @remarks
184
+ * `apiKey` must be set to your PerplexityAI API key, either using the argument or by setting the
185
+ * `PERPLEXITY_API_KEY` environmental variable.
186
+ */
187
+ static withPerplexity(opts = {}) {
188
+ opts.apiKey = opts.apiKey || process.env.PERPLEXITY_API_KEY;
189
+ if (opts.apiKey === void 0) {
190
+ throw new Error(
191
+ "PerplexityAI API key is required, whether as an argument or as $PERPLEXITY_API_KEY"
192
+ );
214
193
  }
215
- /**
216
- * Create a new instance of Telnyx LLM.
217
- *
218
- * @remarks
219
- * `apiKey` must be set to your Telnyx API key, either using the argument or by setting the
220
- * `TELNYX_API_KEY` environmental variable.
221
- */
222
- static withTelnyx(opts = {}) {
223
- opts.apiKey = opts.apiKey || process.env.TELNYX_API_KEY;
224
- if (opts.apiKey === undefined) {
225
- throw new Error('Telnyx API key is required, whether as an argument or as $TELNYX_API_KEY');
226
- }
227
- return new LLM({
228
- model: 'meta-llama/Meta-Llama-3.1-70B-Instruct',
229
- baseURL: 'https://api.telnyx.com/v2/ai',
230
- ...opts,
231
- });
194
+ return new LLM({
195
+ model: "llama-3.1-sonar-small-128k-chat",
196
+ baseURL: "https://api.perplexity.ai",
197
+ ...opts
198
+ });
199
+ }
200
+ /**
201
+ * Create a new instance of TogetherAI LLM.
202
+ *
203
+ * @remarks
204
+ * `apiKey` must be set to your TogetherAI API key, either using the argument or by setting the
205
+ * `TOGETHER_API_KEY` environmental variable.
206
+ */
207
+ static withTogether(opts = {}) {
208
+ opts.apiKey = opts.apiKey || process.env.TOGETHER_API_KEY;
209
+ if (opts.apiKey === void 0) {
210
+ throw new Error(
211
+ "TogetherAI API key is required, whether as an argument or as $TOGETHER_API_KEY"
212
+ );
232
213
  }
233
- chat({ chatCtx, fncCtx, temperature, n, parallelToolCalls, }) {
234
- temperature = temperature || this.#opts.temperature;
235
- return new LLMStream(this.#client, chatCtx, fncCtx, this.#opts, parallelToolCalls, temperature, n);
214
+ return new LLM({
215
+ model: "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
216
+ baseURL: "https://api.together.xyz/v1",
217
+ ...opts
218
+ });
219
+ }
220
+ /**
221
+ * Create a new instance of Telnyx LLM.
222
+ *
223
+ * @remarks
224
+ * `apiKey` must be set to your Telnyx API key, either using the argument or by setting the
225
+ * `TELNYX_API_KEY` environmental variable.
226
+ */
227
+ static withTelnyx(opts = {}) {
228
+ opts.apiKey = opts.apiKey || process.env.TELNYX_API_KEY;
229
+ if (opts.apiKey === void 0) {
230
+ throw new Error("Telnyx API key is required, whether as an argument or as $TELNYX_API_KEY");
236
231
  }
232
+ return new LLM({
233
+ model: "meta-llama/Meta-Llama-3.1-70B-Instruct",
234
+ baseURL: "https://api.telnyx.com/v2/ai",
235
+ ...opts
236
+ });
237
+ }
238
+ chat({
239
+ chatCtx,
240
+ fncCtx,
241
+ temperature,
242
+ n,
243
+ parallelToolCalls
244
+ }) {
245
+ temperature = temperature || this.#opts.temperature;
246
+ return new LLMStream(
247
+ this.#client,
248
+ chatCtx,
249
+ fncCtx,
250
+ this.#opts,
251
+ parallelToolCalls,
252
+ temperature,
253
+ n
254
+ );
255
+ }
237
256
  }
238
- export class LLMStream extends llm.LLMStream {
239
- #toolCallId;
240
- #fncName;
241
- #fncRawArguments;
242
- #client;
243
- #logger = log();
244
- #id = randomUUID();
245
- constructor(client, chatCtx, fncCtx, opts, parallelToolCalls, temperature, n) {
246
- super(chatCtx, fncCtx);
247
- this.#client = client;
248
- this.#run(opts, n, parallelToolCalls, temperature);
249
- }
250
- async #run(opts, n, parallelToolCalls, temperature) {
251
- const tools = this.fncCtx
252
- ? Object.entries(this.fncCtx).map(([name, func]) => ({
253
- type: 'function',
254
- function: {
255
- name,
256
- description: func.description,
257
- // don't format parameters if they are raw openai params
258
- parameters: func.parameters.type == 'object'
259
- ? func.parameters
260
- : llm.oaiParams(func.parameters),
261
- },
262
- }))
263
- : undefined;
264
- try {
265
- const stream = await this.#client.chat.completions.create({
266
- model: opts.model,
267
- user: opts.user,
268
- n,
269
- messages: await Promise.all(this.chatCtx.messages.map(async (m) => await buildMessage(m, this.#id))),
270
- temperature: temperature || opts.temperature,
271
- stream_options: { include_usage: true },
272
- stream: true,
273
- tools,
274
- parallel_tool_calls: this.fncCtx && parallelToolCalls,
257
+ class LLMStream extends llm.LLMStream {
258
+ #toolCallId;
259
+ #fncName;
260
+ #fncRawArguments;
261
+ #client;
262
+ #logger = log();
263
+ #id = randomUUID();
264
+ constructor(client, chatCtx, fncCtx, opts, parallelToolCalls, temperature, n) {
265
+ super(chatCtx, fncCtx);
266
+ this.#client = client;
267
+ this.#run(opts, n, parallelToolCalls, temperature);
268
+ }
269
+ async #run(opts, n, parallelToolCalls, temperature) {
270
+ const tools = this.fncCtx ? Object.entries(this.fncCtx).map(([name, func]) => ({
271
+ type: "function",
272
+ function: {
273
+ name,
274
+ description: func.description,
275
+ // don't format parameters if they are raw openai params
276
+ parameters: func.parameters.type == "object" ? func.parameters : llm.oaiParams(func.parameters)
277
+ }
278
+ })) : void 0;
279
+ try {
280
+ const stream = await this.#client.chat.completions.create({
281
+ model: opts.model,
282
+ user: opts.user,
283
+ n,
284
+ messages: await Promise.all(
285
+ this.chatCtx.messages.map(async (m) => await buildMessage(m, this.#id))
286
+ ),
287
+ temperature: temperature || opts.temperature,
288
+ stream_options: { include_usage: true },
289
+ stream: true,
290
+ tools,
291
+ parallel_tool_calls: this.fncCtx && parallelToolCalls
292
+ });
293
+ for await (const chunk of stream) {
294
+ for (const choice of chunk.choices) {
295
+ const chatChunk = this.#parseChoice(chunk.id, choice);
296
+ if (chatChunk) {
297
+ this.queue.put(chatChunk);
298
+ }
299
+ if (chunk.usage) {
300
+ const usage = chunk.usage;
301
+ this.queue.put({
302
+ requestId: chunk.id,
303
+ choices: [],
304
+ usage: {
305
+ completionTokens: usage.completion_tokens,
306
+ promptTokens: usage.prompt_tokens,
307
+ totalTokens: usage.total_tokens
308
+ }
275
309
  });
276
- for await (const chunk of stream) {
277
- for (const choice of chunk.choices) {
278
- const chatChunk = this.#parseChoice(chunk.id, choice);
279
- if (chatChunk) {
280
- this.queue.put(chatChunk);
281
- }
282
- if (chunk.usage) {
283
- const usage = chunk.usage;
284
- this.queue.put({
285
- requestId: chunk.id,
286
- choices: [],
287
- usage: {
288
- completionTokens: usage.completion_tokens,
289
- promptTokens: usage.prompt_tokens,
290
- totalTokens: usage.total_tokens,
291
- },
292
- });
293
- }
294
- }
295
- }
310
+ }
296
311
  }
297
- finally {
298
- this.queue.close();
299
- }
300
- }
301
- #parseChoice(id, choice) {
302
- const delta = choice.delta;
303
- if (delta.tool_calls) {
304
- // check if we have functions to calls
305
- for (const tool of delta.tool_calls) {
306
- if (!tool.function) {
307
- continue; // oai may add other tools in the future
308
- }
309
- if (tool.function.name) {
310
- this.#toolCallId = tool.id;
311
- this.#fncName = tool.function.name;
312
- this.#fncRawArguments = tool.function.arguments || '';
313
- }
314
- else if (tool.function.arguments) {
315
- this.#fncRawArguments += tool.function.arguments;
316
- }
317
- if (this.#toolCallId && tool.id && tool.id !== this.#toolCallId) {
318
- return this.#tryBuildFunction(id, choice);
319
- }
320
- }
321
- }
322
- if (choice.finish_reason &&
323
- ['tool_calls', 'stop'].includes(choice.finish_reason) &&
324
- this.#toolCallId) {
325
- // we're done with the tool calls, run the last one
326
- return this.#tryBuildFunction(id, choice);
327
- }
328
- return {
329
- requestId: id,
330
- choices: [
331
- {
332
- delta: { content: delta.content || undefined, role: llm.ChatRole.ASSISTANT },
333
- index: choice.index,
334
- },
335
- ],
336
- };
312
+ }
313
+ } finally {
314
+ this.queue.close();
337
315
  }
338
- #tryBuildFunction(id, choice) {
339
- if (!this.fncCtx) {
340
- this.#logger.warn('oai stream tried to run function without function context');
341
- return undefined;
316
+ }
317
+ #parseChoice(id, choice) {
318
+ const delta = choice.delta;
319
+ if (delta.tool_calls) {
320
+ for (const tool of delta.tool_calls) {
321
+ if (!tool.function) {
322
+ continue;
342
323
  }
343
- if (!this.#toolCallId) {
344
- this.#logger.warn('oai stream tried to run function but toolCallId is not set');
345
- return undefined;
324
+ if (tool.function.name) {
325
+ this.#toolCallId = tool.id;
326
+ this.#fncName = tool.function.name;
327
+ this.#fncRawArguments = tool.function.arguments || "";
328
+ } else if (tool.function.arguments) {
329
+ this.#fncRawArguments += tool.function.arguments;
346
330
  }
347
- if (!this.#fncRawArguments || !this.#fncName) {
348
- this.#logger.warn('oai stream tried to run function but rawArguments or fncName are not set');
349
- return undefined;
331
+ if (this.#toolCallId && tool.id && tool.id !== this.#toolCallId) {
332
+ return this.#tryBuildFunction(id, choice);
350
333
  }
351
- const functionInfo = llm.oaiBuildFunctionInfo(this.fncCtx, this.#toolCallId, this.#fncName, this.#fncRawArguments);
352
- this.#toolCallId = this.#fncName = this.#fncRawArguments = undefined;
353
- this._functionCalls.push(functionInfo);
354
- return {
355
- requestId: id,
356
- choices: [
357
- {
358
- delta: {
359
- content: choice.delta.content || undefined,
360
- role: llm.ChatRole.ASSISTANT,
361
- toolCalls: this._functionCalls,
362
- },
363
- index: choice.index,
364
- },
365
- ],
366
- };
334
+ }
367
335
  }
368
- }
369
- const buildMessage = async (msg, cacheKey) => {
370
- const oaiMsg = {};
371
- switch (msg.role) {
372
- case llm.ChatRole.SYSTEM:
373
- oaiMsg.role = 'system';
374
- break;
375
- case llm.ChatRole.USER:
376
- oaiMsg.role = 'user';
377
- break;
378
- case llm.ChatRole.ASSISTANT:
379
- oaiMsg.role = 'assistant';
380
- break;
381
- case llm.ChatRole.TOOL:
382
- oaiMsg.role = 'tool';
383
- if (oaiMsg.role === 'tool') {
384
- oaiMsg.tool_call_id = msg.toolCallId;
385
- }
386
- break;
336
+ if (choice.finish_reason && ["tool_calls", "stop"].includes(choice.finish_reason) && this.#toolCallId) {
337
+ return this.#tryBuildFunction(id, choice);
387
338
  }
388
- if (typeof msg.content === 'string') {
389
- oaiMsg.content = msg.content;
339
+ return {
340
+ requestId: id,
341
+ choices: [
342
+ {
343
+ delta: { content: delta.content || void 0, role: llm.ChatRole.ASSISTANT },
344
+ index: choice.index
345
+ }
346
+ ]
347
+ };
348
+ }
349
+ #tryBuildFunction(id, choice) {
350
+ if (!this.fncCtx) {
351
+ this.#logger.warn("oai stream tried to run function without function context");
352
+ return void 0;
390
353
  }
391
- else if (((c) => {
392
- return c.length !== undefined;
393
- })(msg.content)) {
394
- oaiMsg.content = (await Promise.all(msg.content.map(async (c) => {
395
- if (typeof c === 'string') {
396
- return { type: 'text', text: c };
397
- }
398
- else if (
399
- // typescript type guard for determining ChatAudio vs ChatImage
400
- ((c) => {
401
- return c.image !== undefined;
402
- })(c)) {
403
- return await buildImageContent(c, cacheKey);
404
- }
405
- else {
406
- throw new Error('ChatAudio is not supported');
407
- }
408
- })));
354
+ if (!this.#toolCallId) {
355
+ this.#logger.warn("oai stream tried to run function but toolCallId is not set");
356
+ return void 0;
409
357
  }
410
- // make sure to provide when function has been called inside the context
411
- // (+ raw_arguments)
412
- if (msg.toolCalls && oaiMsg.role === 'assistant') {
413
- oaiMsg.tool_calls = Object.entries(msg.toolCalls).map(([name, func]) => ({
414
- id: func.toolCallId,
415
- type: 'function',
416
- function: {
417
- name: name,
418
- arguments: func.rawParams,
419
- },
420
- }));
358
+ if (!this.#fncRawArguments || !this.#fncName) {
359
+ this.#logger.warn("oai stream tried to run function but rawArguments or fncName are not set");
360
+ return void 0;
421
361
  }
422
- return oaiMsg;
362
+ const functionInfo = llm.oaiBuildFunctionInfo(
363
+ this.fncCtx,
364
+ this.#toolCallId,
365
+ this.#fncName,
366
+ this.#fncRawArguments
367
+ );
368
+ this.#toolCallId = this.#fncName = this.#fncRawArguments = void 0;
369
+ this._functionCalls.push(functionInfo);
370
+ return {
371
+ requestId: id,
372
+ choices: [
373
+ {
374
+ delta: {
375
+ content: choice.delta.content || void 0,
376
+ role: llm.ChatRole.ASSISTANT,
377
+ toolCalls: this._functionCalls
378
+ },
379
+ index: choice.index
380
+ }
381
+ ]
382
+ };
383
+ }
384
+ }
385
+ const buildMessage = async (msg, cacheKey) => {
386
+ const oaiMsg = {};
387
+ switch (msg.role) {
388
+ case llm.ChatRole.SYSTEM:
389
+ oaiMsg.role = "system";
390
+ break;
391
+ case llm.ChatRole.USER:
392
+ oaiMsg.role = "user";
393
+ break;
394
+ case llm.ChatRole.ASSISTANT:
395
+ oaiMsg.role = "assistant";
396
+ break;
397
+ case llm.ChatRole.TOOL:
398
+ oaiMsg.role = "tool";
399
+ if (oaiMsg.role === "tool") {
400
+ oaiMsg.tool_call_id = msg.toolCallId;
401
+ }
402
+ break;
403
+ }
404
+ if (typeof msg.content === "string") {
405
+ oaiMsg.content = msg.content;
406
+ } else if (((c) => {
407
+ return c.length !== void 0;
408
+ })(msg.content)) {
409
+ oaiMsg.content = await Promise.all(
410
+ msg.content.map(async (c) => {
411
+ if (typeof c === "string") {
412
+ return { type: "text", text: c };
413
+ } else if (
414
+ // typescript type guard for determining ChatAudio vs ChatImage
415
+ ((c2) => {
416
+ return c2.image !== void 0;
417
+ })(c)
418
+ ) {
419
+ return await buildImageContent(c, cacheKey);
420
+ } else {
421
+ throw new Error("ChatAudio is not supported");
422
+ }
423
+ })
424
+ );
425
+ }
426
+ if (msg.toolCalls && oaiMsg.role === "assistant") {
427
+ oaiMsg.tool_calls = Object.entries(msg.toolCalls).map(([name, func]) => ({
428
+ id: func.toolCallId,
429
+ type: "function",
430
+ function: {
431
+ name,
432
+ arguments: func.rawParams
433
+ }
434
+ }));
435
+ }
436
+ return oaiMsg;
423
437
  };
424
438
  const buildImageContent = async (image, cacheKey) => {
425
- if (typeof image.image === 'string') {
426
- // image url
427
- return {
428
- type: 'image_url',
429
- image_url: {
430
- url: image.image,
431
- detail: 'auto',
432
- },
433
- };
434
- }
435
- else {
436
- if (!image.cache[cacheKey]) {
437
- // inside our internal implementation, we allow to put extra metadata to
438
- // each ChatImage (avoid to reencode each time we do a chatcompletion request)
439
- let encoded = sharp(image.image.data);
440
- if (image.inferenceHeight && image.inferenceHeight) {
441
- encoded = encoded.resize(image.inferenceWidth, image.inferenceHeight);
442
- }
443
- image.cache[cacheKey] = await encoded
444
- .jpeg()
445
- .toBuffer()
446
- .then((buffer) => buffer.toString('utf-8'));
447
- }
448
- return {
449
- type: 'image_url',
450
- image_url: {
451
- url: `data:image/jpeg;base64,${image.cache[cacheKey]}`,
452
- },
453
- };
439
+ if (typeof image.image === "string") {
440
+ return {
441
+ type: "image_url",
442
+ image_url: {
443
+ url: image.image,
444
+ detail: "auto"
445
+ }
446
+ };
447
+ } else {
448
+ if (!image.cache[cacheKey]) {
449
+ let encoded = sharp(image.image.data);
450
+ if (image.inferenceHeight && image.inferenceHeight) {
451
+ encoded = encoded.resize(image.inferenceWidth, image.inferenceHeight);
452
+ }
453
+ image.cache[cacheKey] = await encoded.jpeg().toBuffer().then((buffer) => buffer.toString("utf-8"));
454
454
  }
455
+ return {
456
+ type: "image_url",
457
+ image_url: {
458
+ url: `data:image/jpeg;base64,${image.cache[cacheKey]}`
459
+ }
460
+ };
461
+ }
462
+ };
463
+ export {
464
+ LLM,
465
+ LLMStream
455
466
  };
456
467
  //# sourceMappingURL=llm.js.map