@livekit/agents-plugin-openai 0.6.0 → 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/README.md +18 -0
- package/dist/index.cjs +55 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +13 -8
- package/dist/index.js.map +1 -1
- package/dist/llm.cjs +502 -0
- package/dist/llm.cjs.map +1 -0
- package/dist/llm.js +435 -424
- package/dist/llm.js.map +1 -1
- package/dist/models.cjs +17 -0
- package/dist/models.cjs.map +1 -0
- package/dist/models.js +0 -4
- package/dist/models.js.map +1 -1
- package/dist/realtime/api_proto.cjs +41 -0
- package/dist/realtime/api_proto.cjs.map +1 -0
- package/dist/realtime/api_proto.d.ts +1 -1
- package/dist/realtime/api_proto.d.ts.map +1 -1
- package/dist/realtime/api_proto.js +12 -8
- package/dist/realtime/api_proto.js.map +1 -1
- package/dist/realtime/index.cjs +25 -0
- package/dist/realtime/index.cjs.map +1 -0
- package/dist/realtime/index.js +2 -5
- package/dist/realtime/index.js.map +1 -1
- package/dist/realtime/realtime_model.cjs +878 -0
- package/dist/realtime/realtime_model.cjs.map +1 -0
- package/dist/realtime/realtime_model.js +828 -777
- package/dist/realtime/realtime_model.js.map +1 -1
- package/dist/stt.cjs +130 -0
- package/dist/stt.cjs.map +1 -0
- package/dist/stt.js +99 -102
- package/dist/stt.js.map +1 -1
- package/dist/tts.cjs +100 -0
- package/dist/tts.cjs.map +1 -0
- package/dist/tts.d.ts +1 -1
- package/dist/tts.d.ts.map +1 -1
- package/dist/tts.js +67 -65
- package/dist/tts.js.map +1 -1
- package/package.json +23 -7
- package/src/realtime/api_proto.ts +10 -1
- package/src/tts.ts +2 -1
- package/.turbo/turbo-build.log +0 -4
- package/CHANGELOG.md +0 -148
- package/api-extractor.json +0 -20
- package/tsconfig.json +0 -16
- package/tsconfig.tsbuildinfo +0 -1
package/dist/llm.js
CHANGED
|
@@ -1,456 +1,467 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import
|
|
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
|
-
|
|
10
|
-
|
|
6
|
+
model: "gpt-4o",
|
|
7
|
+
apiKey: process.env.OPENAI_API_KEY
|
|
11
8
|
};
|
|
12
9
|
const defaultAzureLLMOptions = {
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
model: "gpt-4o",
|
|
11
|
+
apiKey: process.env.AZURE_API_KEY
|
|
15
12
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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
|
-
|
|
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
|
-
|
|
298
|
-
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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 (
|
|
344
|
-
|
|
345
|
-
|
|
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 (
|
|
348
|
-
|
|
349
|
-
return undefined;
|
|
331
|
+
if (this.#toolCallId && tool.id && tool.id !== this.#toolCallId) {
|
|
332
|
+
return this.#tryBuildFunction(id, choice);
|
|
350
333
|
}
|
|
351
|
-
|
|
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
|
-
|
|
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
|
-
|
|
389
|
-
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
-
|
|
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
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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
|