@crewdle/mist-connector-openai 1.0.13 → 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/models/OpenAIFileConnector.js +1 -1
- package/dist/models/OpenAIGenerativeAIWorkerConnector.js +330 -153
- package/dist/models/OpenAISpeechConnector.js +4 -3
- package/dist/types/models/OpenAIGenerativeAIWorkerConnector.d.ts +2 -2
- package/dist/types/models/OpenAISpeechConnector.d.ts +1 -1
- package/package.json +3 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
+
import { GenerativeAIEngineType, GenerativeAITaskType } from '@crewdle/web-sdk-types';
|
|
2
3
|
export class OpenAIGenerativeAIWorkerConnector {
|
|
3
4
|
models = new Map();
|
|
4
5
|
apiKey;
|
|
@@ -22,7 +23,7 @@ export class OpenAIGenerativeAIWorkerConnector {
|
|
|
22
23
|
this.models.clear();
|
|
23
24
|
}
|
|
24
25
|
getEngineType() {
|
|
25
|
-
return
|
|
26
|
+
return GenerativeAIEngineType.OpenAI;
|
|
26
27
|
}
|
|
27
28
|
async processJob(parameters, options) {
|
|
28
29
|
if (!this.client) {
|
|
@@ -31,7 +32,7 @@ export class OpenAIGenerativeAIWorkerConnector {
|
|
|
31
32
|
if (!options || !this.models.has(options.model.id)) {
|
|
32
33
|
throw new Error('Model not initialized');
|
|
33
34
|
}
|
|
34
|
-
if (options.model.
|
|
35
|
+
if (options.model.taskType === GenerativeAITaskType.Embeddings) {
|
|
35
36
|
const response = await this.client.embeddings.create({
|
|
36
37
|
model: options.model.id,
|
|
37
38
|
input: parameters.prompt,
|
|
@@ -41,42 +42,131 @@ export class OpenAIGenerativeAIWorkerConnector {
|
|
|
41
42
|
throw new Error('No response data');
|
|
42
43
|
}
|
|
43
44
|
return {
|
|
44
|
-
type:
|
|
45
|
+
type: "prompt" /* GenerativeAIJobType.Prompt */,
|
|
45
46
|
output: response.data[0].embedding,
|
|
46
47
|
inputTokens: response.usage?.prompt_tokens ?? 0,
|
|
47
48
|
outputTokens: 0,
|
|
48
49
|
};
|
|
49
50
|
}
|
|
51
|
+
if (options.model.taskType === GenerativeAITaskType.SpeechToText) {
|
|
52
|
+
if (typeof parameters.prompt === 'string') {
|
|
53
|
+
throw new Error('Prompt must be an array of messages');
|
|
54
|
+
}
|
|
55
|
+
let file;
|
|
56
|
+
for (const message of parameters.prompt) {
|
|
57
|
+
if (message.type === 'input_audio') {
|
|
58
|
+
file = new File([message.input_audio.data], 'audio.mp3', { type: message.input_audio.format });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!file) {
|
|
62
|
+
throw new Error('No file found');
|
|
63
|
+
}
|
|
64
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJob audio transcription');
|
|
65
|
+
const response = await this.client.audio.transcriptions.create({
|
|
66
|
+
model: options.model.id,
|
|
67
|
+
file,
|
|
68
|
+
response_format: 'json',
|
|
69
|
+
});
|
|
70
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJob audio transcription response');
|
|
71
|
+
return {
|
|
72
|
+
type: "prompt" /* GenerativeAIJobType.Prompt */,
|
|
73
|
+
output: response.text,
|
|
74
|
+
inputTokens: response.usage?.type === 'tokens' ? response.usage.input_tokens : 0,
|
|
75
|
+
outputTokens: response.usage?.type === 'tokens' ? response.usage.output_tokens : 0,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (options.model.taskType === GenerativeAITaskType.ImageGeneration) {
|
|
79
|
+
const images = [];
|
|
80
|
+
let prompt = parameters.prompt;
|
|
81
|
+
if (typeof parameters.prompt !== 'string') {
|
|
82
|
+
for (const message of parameters.prompt) {
|
|
83
|
+
if (message.type === 'image_url') {
|
|
84
|
+
const parts = message.image_url.url.split(',');
|
|
85
|
+
const data = parts[1];
|
|
86
|
+
const type = parts[0].replace('data:', '').replace(';base64', '');
|
|
87
|
+
const byteArray = Uint8Array.from(Buffer.from(data, 'base64'));
|
|
88
|
+
const blob = new Blob([byteArray], { type });
|
|
89
|
+
images.push(new File([blob], `image.${type.split('/')[1]}`, { type }));
|
|
90
|
+
}
|
|
91
|
+
if (message.type === 'text') {
|
|
92
|
+
prompt = message.text;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (images.length === 0) {
|
|
97
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJob image generation');
|
|
98
|
+
const response = await this.client.images.generate({
|
|
99
|
+
model: options.model.id,
|
|
100
|
+
prompt,
|
|
101
|
+
});
|
|
102
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJob image generation response');
|
|
103
|
+
return {
|
|
104
|
+
type: "prompt" /* GenerativeAIJobType.Prompt */,
|
|
105
|
+
output: '',
|
|
106
|
+
resultFile: `data:image/png;base64,${response.data?.[0]?.b64_json ?? ''}`,
|
|
107
|
+
inputTokens: response.usage?.input_tokens ?? 0,
|
|
108
|
+
outputTokens: response.usage?.output_tokens ?? 0,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJob image edit');
|
|
113
|
+
const response = await this.client.images.edit({
|
|
114
|
+
model: options.model.id,
|
|
115
|
+
image: images.length === 1 ? images[0] : images,
|
|
116
|
+
prompt,
|
|
117
|
+
});
|
|
118
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJob image edit response');
|
|
119
|
+
return {
|
|
120
|
+
type: "prompt" /* GenerativeAIJobType.Prompt */,
|
|
121
|
+
output: '',
|
|
122
|
+
resultFile: `data:image/png;base64,${response.data?.[0]?.b64_json ?? ''}`,
|
|
123
|
+
inputTokens: response.usage?.input_tokens ?? 0,
|
|
124
|
+
outputTokens: response.usage?.output_tokens ?? 0,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
50
128
|
const responseFormat = this.getResponseFormat(parameters);
|
|
51
129
|
const tools = this.getTools(parameters);
|
|
52
130
|
const messages = this.getMessages(parameters);
|
|
53
|
-
let
|
|
54
|
-
let
|
|
55
|
-
let
|
|
131
|
+
let inputTokens = 0;
|
|
132
|
+
let outputTokens = 0;
|
|
133
|
+
let output = '';
|
|
56
134
|
while (true) {
|
|
57
|
-
|
|
135
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJob', options.model.id);
|
|
136
|
+
const response = await this.client.responses.create({
|
|
58
137
|
model: options.model.id,
|
|
59
|
-
messages,
|
|
60
|
-
|
|
138
|
+
input: messages,
|
|
139
|
+
max_output_tokens: parameters.maxTokens,
|
|
61
140
|
temperature: parameters.temperature,
|
|
62
|
-
|
|
141
|
+
text: responseFormat,
|
|
63
142
|
tools,
|
|
64
143
|
});
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (!message.tool_calls) {
|
|
69
|
-
accumulatedOutput += message.content ?? '';
|
|
70
|
-
return {
|
|
71
|
-
type: 'prompt',
|
|
72
|
-
output: accumulatedOutput,
|
|
73
|
-
inputTokens: totalInputTokens,
|
|
74
|
-
outputTokens: totalOutputTokens,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
144
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJob response');
|
|
145
|
+
inputTokens += response.usage?.input_tokens ?? 0;
|
|
146
|
+
outputTokens += response.usage?.output_tokens ?? 0;
|
|
77
147
|
const promises = [];
|
|
78
|
-
|
|
79
|
-
|
|
148
|
+
for (const content of response.output) {
|
|
149
|
+
if (content.type === 'message') {
|
|
150
|
+
for (const message of content.content) {
|
|
151
|
+
if (message.type === 'output_text') {
|
|
152
|
+
output += message.text;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (content.type === 'function_call') {
|
|
157
|
+
promises.push(this.processToolCall(parameters, messages, content.name, content.call_id, content.arguments));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (promises.length > 0) {
|
|
161
|
+
await Promise.all(promises);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
type: "prompt" /* GenerativeAIJobType.Prompt */,
|
|
166
|
+
output,
|
|
167
|
+
inputTokens,
|
|
168
|
+
outputTokens,
|
|
169
|
+
};
|
|
80
170
|
}
|
|
81
171
|
}
|
|
82
172
|
async *processJobStream(parameters, options) {
|
|
@@ -86,138 +176,193 @@ export class OpenAIGenerativeAIWorkerConnector {
|
|
|
86
176
|
if (!options || !this.models.has(options.model.id)) {
|
|
87
177
|
throw new Error('Model not initialized');
|
|
88
178
|
}
|
|
179
|
+
if (options.model.taskType === GenerativeAITaskType.SpeechToText) {
|
|
180
|
+
if (typeof parameters.prompt === 'string') {
|
|
181
|
+
throw new Error('Prompt must be an array of messages');
|
|
182
|
+
}
|
|
183
|
+
let file;
|
|
184
|
+
for (const message of parameters.prompt) {
|
|
185
|
+
if (message.type === 'input_audio') {
|
|
186
|
+
const dataParts = message.input_audio.data.split(',');
|
|
187
|
+
const data = dataParts[1];
|
|
188
|
+
const type = dataParts[0].replace('data:', '').replace(';base64', '');
|
|
189
|
+
const byteArray = Uint8Array.from(Buffer.from(data, 'base64'));
|
|
190
|
+
const blob = new Blob([byteArray], { type });
|
|
191
|
+
file = new File([blob], `audio.${message.input_audio.format}`, { type });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (!file) {
|
|
195
|
+
throw new Error('No file found');
|
|
196
|
+
}
|
|
197
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJobStream audio transcription');
|
|
198
|
+
const stream = await this.client.audio.transcriptions.create({
|
|
199
|
+
model: options.model.id,
|
|
200
|
+
file,
|
|
201
|
+
response_format: 'json',
|
|
202
|
+
stream: true,
|
|
203
|
+
});
|
|
204
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJobStream audio transcription response');
|
|
205
|
+
for await (const chunk of stream) {
|
|
206
|
+
yield {
|
|
207
|
+
type: "prompt" /* GenerativeAIJobType.Prompt */,
|
|
208
|
+
output: chunk.type === 'transcript.text.delta' ? chunk.delta : '',
|
|
209
|
+
inputTokens: chunk.type === 'transcript.text.done' ? 0 : chunk.usage?.input_tokens ?? 0,
|
|
210
|
+
outputTokens: chunk.type === 'transcript.text.done' ? 0 : chunk.usage?.output_tokens ?? 0,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (options.model.taskType === GenerativeAITaskType.ImageGeneration) {
|
|
216
|
+
const images = [];
|
|
217
|
+
let prompt = parameters.prompt;
|
|
218
|
+
if (typeof parameters.prompt !== 'string') {
|
|
219
|
+
for (const message of parameters.prompt) {
|
|
220
|
+
if (message.type === 'image_url') {
|
|
221
|
+
const parts = message.image_url.url.split(',');
|
|
222
|
+
const data = parts[1];
|
|
223
|
+
const type = parts[0].replace('data:', '').replace(';base64', '');
|
|
224
|
+
const byteArray = Uint8Array.from(Buffer.from(data, 'base64'));
|
|
225
|
+
const blob = new Blob([byteArray], { type });
|
|
226
|
+
images.push(new File([blob], `image.${type.split('/')[1]}`, { type }));
|
|
227
|
+
}
|
|
228
|
+
if (message.type === 'text') {
|
|
229
|
+
prompt = message.text;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (images.length === 0) {
|
|
234
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJobStream images.length === 0');
|
|
235
|
+
const response = await this.client.images.generate({
|
|
236
|
+
model: options.model.id,
|
|
237
|
+
prompt,
|
|
238
|
+
});
|
|
239
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJobStream images.length === 0 response');
|
|
240
|
+
yield {
|
|
241
|
+
type: "prompt" /* GenerativeAIJobType.Prompt */,
|
|
242
|
+
output: '',
|
|
243
|
+
resultFile: `data:image/png;base64,${response.data?.[0]?.b64_json ?? ''}`,
|
|
244
|
+
inputTokens: response.usage?.input_tokens ?? 0,
|
|
245
|
+
outputTokens: response.usage?.output_tokens ?? 0,
|
|
246
|
+
};
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJobStream images.length > 0');
|
|
251
|
+
const response = await this.client.images.edit({
|
|
252
|
+
model: options.model.id,
|
|
253
|
+
image: images.length === 1 ? images[0] : images,
|
|
254
|
+
prompt,
|
|
255
|
+
});
|
|
256
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJobStream images.length > 0 response');
|
|
257
|
+
yield {
|
|
258
|
+
type: "prompt" /* GenerativeAIJobType.Prompt */,
|
|
259
|
+
output: '',
|
|
260
|
+
resultFile: `data:image/png;base64,${response.data?.[0]?.b64_json ?? ''}`,
|
|
261
|
+
inputTokens: response.usage?.input_tokens ?? 0,
|
|
262
|
+
outputTokens: response.usage?.output_tokens ?? 0,
|
|
263
|
+
};
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
89
267
|
const responseFormat = this.getResponseFormat(parameters);
|
|
90
268
|
const tools = this.getTools(parameters);
|
|
91
269
|
const messages = this.getMessages(parameters);
|
|
92
270
|
while (true) {
|
|
93
|
-
|
|
271
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJobStream', options.model.id);
|
|
272
|
+
const stream = await this.client.responses.create({
|
|
94
273
|
model: options.model.id,
|
|
95
|
-
messages,
|
|
96
|
-
|
|
274
|
+
input: messages,
|
|
275
|
+
max_output_tokens: parameters.maxTokens,
|
|
97
276
|
temperature: parameters.temperature,
|
|
98
|
-
|
|
99
|
-
stream_options: {
|
|
100
|
-
include_usage: true,
|
|
101
|
-
},
|
|
277
|
+
text: responseFormat,
|
|
102
278
|
tools,
|
|
103
|
-
|
|
279
|
+
stream: true,
|
|
104
280
|
});
|
|
105
|
-
|
|
281
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processJobStream response');
|
|
282
|
+
const promises = [];
|
|
106
283
|
for await (const chunk of stream) {
|
|
107
|
-
if (chunk.
|
|
108
|
-
|
|
284
|
+
if (chunk.type === 'response.output_text.delta') {
|
|
285
|
+
yield {
|
|
286
|
+
type: "prompt" /* GenerativeAIJobType.Prompt */,
|
|
287
|
+
output: chunk.delta,
|
|
288
|
+
inputTokens: 0,
|
|
289
|
+
outputTokens: 0,
|
|
290
|
+
};
|
|
109
291
|
}
|
|
292
|
+
if (chunk.type === 'response.completed') {
|
|
293
|
+
yield {
|
|
294
|
+
type: "prompt" /* GenerativeAIJobType.Prompt */,
|
|
295
|
+
output: '',
|
|
296
|
+
inputTokens: chunk.response.usage?.input_tokens ?? 0,
|
|
297
|
+
outputTokens: chunk.response.usage?.output_tokens ?? 0,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
if (chunk.type === 'response.output_item.done') {
|
|
301
|
+
if (chunk.item.type === 'function_call') {
|
|
302
|
+
promises.push(this.processToolCall(parameters, messages, chunk.item.name, chunk.item.call_id, chunk.item.arguments));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (promises.length > 0) {
|
|
307
|
+
await Promise.all(promises);
|
|
110
308
|
yield {
|
|
111
|
-
type:
|
|
112
|
-
output:
|
|
113
|
-
inputTokens:
|
|
114
|
-
outputTokens:
|
|
309
|
+
type: "prompt" /* GenerativeAIJobType.Prompt */,
|
|
310
|
+
output: '\n\n',
|
|
311
|
+
inputTokens: 0,
|
|
312
|
+
outputTokens: 0,
|
|
115
313
|
};
|
|
314
|
+
continue;
|
|
116
315
|
}
|
|
117
|
-
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
const promises = [];
|
|
121
|
-
this.processToolCalls(promises, message.tool_calls, messages, parameters.functions);
|
|
122
|
-
await Promise.all(promises);
|
|
123
|
-
yield {
|
|
124
|
-
type: "prompt" /* GenerativeAIJobType.Prompt */,
|
|
125
|
-
output: '\n\n',
|
|
126
|
-
inputTokens: 0,
|
|
127
|
-
outputTokens: 0,
|
|
128
|
-
};
|
|
316
|
+
break;
|
|
129
317
|
}
|
|
130
318
|
}
|
|
131
|
-
|
|
319
|
+
async processToolCall(parameters, messages, toolName, toolCallId, input) {
|
|
132
320
|
messages.push({
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
321
|
+
type: 'function_call',
|
|
322
|
+
arguments: input,
|
|
323
|
+
call_id: toolCallId,
|
|
324
|
+
name: toolName,
|
|
137
325
|
});
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
messages.push({
|
|
147
|
-
role: 'tool',
|
|
148
|
-
tool_call_id: toolCall.id,
|
|
149
|
-
content: res ?? 'Tool does not exist',
|
|
150
|
-
});
|
|
151
|
-
}));
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
messages.push({
|
|
155
|
-
role: 'tool',
|
|
156
|
-
tool_call_id: toolCall.id,
|
|
157
|
-
content: result ?? 'Tool does not exist',
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
messages.push({
|
|
163
|
-
role: 'tool',
|
|
164
|
-
tool_call_id: toolCall.id,
|
|
165
|
-
content: 'Tool does not exist',
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
catch (error) {
|
|
170
|
-
console.error('Error processing tool call', error);
|
|
171
|
-
messages.push({
|
|
172
|
-
role: 'tool',
|
|
173
|
-
tool_call_id: toolCall.id,
|
|
174
|
-
content: 'Error processing tool call',
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
326
|
+
const func = parameters.functions?.get(toolName);
|
|
327
|
+
if (!func) {
|
|
328
|
+
messages.push({
|
|
329
|
+
type: 'function_call_output',
|
|
330
|
+
call_id: toolCallId,
|
|
331
|
+
output: 'Tool not found',
|
|
332
|
+
});
|
|
333
|
+
return;
|
|
178
334
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const accArray = acc[key];
|
|
201
|
-
for (let i = 0; i < value.length; i++) {
|
|
202
|
-
const { index, ...chunkTool } = value[i];
|
|
203
|
-
if (index - accArray.length > 1) {
|
|
204
|
-
throw new Error(`Error: An array has an empty value when tool_calls are constructed. tool_calls: ${accArray}; tool: ${value}`);
|
|
205
|
-
}
|
|
206
|
-
accArray[index] = reduce(accArray[index], chunkTool);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
else if (typeof acc[key] === 'object' && typeof value === 'object') {
|
|
210
|
-
acc[key] = reduce(acc[key], value);
|
|
211
|
-
}
|
|
335
|
+
try {
|
|
336
|
+
if (input === '') {
|
|
337
|
+
input = '{}';
|
|
338
|
+
}
|
|
339
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processToolCall', toolName, input);
|
|
340
|
+
const result = func.callback(JSON.parse(input));
|
|
341
|
+
if (result instanceof Promise) {
|
|
342
|
+
const output = await result;
|
|
343
|
+
console.log('OpenAIGenerativeAIWorkerConnector.processToolCall result');
|
|
344
|
+
messages.push({
|
|
345
|
+
type: 'function_call_output',
|
|
346
|
+
call_id: toolCallId,
|
|
347
|
+
output,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
messages.push({
|
|
352
|
+
type: 'function_call_output',
|
|
353
|
+
call_id: toolCallId,
|
|
354
|
+
output: result,
|
|
355
|
+
});
|
|
212
356
|
}
|
|
213
|
-
return acc;
|
|
214
|
-
};
|
|
215
|
-
const choice = item.choices[0];
|
|
216
|
-
if (!choice) {
|
|
217
|
-
// chunk contains information about usage and token counts
|
|
218
|
-
return previous;
|
|
219
357
|
}
|
|
220
|
-
|
|
358
|
+
catch (error) {
|
|
359
|
+
console.error('Error processing tool call', error);
|
|
360
|
+
messages.push({
|
|
361
|
+
type: 'function_call_output',
|
|
362
|
+
call_id: toolCallId,
|
|
363
|
+
output: 'Error processing tool call',
|
|
364
|
+
});
|
|
365
|
+
}
|
|
221
366
|
}
|
|
222
367
|
getMessages(parameters) {
|
|
223
368
|
const messages = [];
|
|
@@ -237,31 +382,58 @@ export class OpenAIGenerativeAIWorkerConnector {
|
|
|
237
382
|
}
|
|
238
383
|
messages.push({
|
|
239
384
|
role: 'user',
|
|
240
|
-
content: parameters.prompt,
|
|
385
|
+
content: this.getInnerMessages(parameters.prompt),
|
|
241
386
|
});
|
|
242
387
|
return messages;
|
|
243
388
|
}
|
|
389
|
+
getInnerMessages(content) {
|
|
390
|
+
if (typeof content === 'string') {
|
|
391
|
+
return content;
|
|
392
|
+
}
|
|
393
|
+
return content.map((c) => {
|
|
394
|
+
if (c.type === 'text') {
|
|
395
|
+
return {
|
|
396
|
+
type: 'input_text',
|
|
397
|
+
text: c.text,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
if (c.type === 'file') {
|
|
401
|
+
return {
|
|
402
|
+
type: 'input_file',
|
|
403
|
+
filename: c.file.filename,
|
|
404
|
+
file_data: c.file.file_data,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
if (c.type === 'image_url') {
|
|
408
|
+
return {
|
|
409
|
+
type: 'input_image',
|
|
410
|
+
image_url: c.image_url.url,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
throw new Error('Invalid content type');
|
|
414
|
+
});
|
|
415
|
+
}
|
|
244
416
|
getTools(parameters) {
|
|
245
417
|
if (!parameters.functions || parameters.functions.size === 0) {
|
|
246
418
|
return undefined;
|
|
247
419
|
}
|
|
248
420
|
const tools = [];
|
|
249
421
|
for (const [name, func] of parameters.functions) {
|
|
250
|
-
let params;
|
|
422
|
+
let params = null;
|
|
251
423
|
if (func.params) {
|
|
252
424
|
params = {
|
|
253
425
|
type: 'object',
|
|
254
426
|
properties: func.params,
|
|
255
427
|
required: Object.keys(func.params),
|
|
428
|
+
additionalProperties: false,
|
|
256
429
|
};
|
|
257
430
|
}
|
|
258
431
|
tools.push({
|
|
259
432
|
type: 'function',
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
433
|
+
name,
|
|
434
|
+
description: func.description,
|
|
435
|
+
strict: true,
|
|
436
|
+
parameters: params,
|
|
265
437
|
});
|
|
266
438
|
}
|
|
267
439
|
return tools;
|
|
@@ -271,33 +443,38 @@ export class OpenAIGenerativeAIWorkerConnector {
|
|
|
271
443
|
return undefined;
|
|
272
444
|
}
|
|
273
445
|
if (parameters.grammar === 'json') {
|
|
274
|
-
return { type: 'json_object' };
|
|
446
|
+
return { format: { type: 'json_object' } };
|
|
275
447
|
}
|
|
276
448
|
if (parameters.grammar === 'if_else') {
|
|
277
449
|
return {
|
|
278
|
-
|
|
279
|
-
|
|
450
|
+
format: {
|
|
451
|
+
type: 'json_schema',
|
|
452
|
+
strict: true,
|
|
280
453
|
name: 'router_response',
|
|
281
454
|
schema: {
|
|
282
455
|
type: 'object',
|
|
283
456
|
properties: {
|
|
284
|
-
|
|
457
|
+
condition_result: {
|
|
285
458
|
type: 'boolean',
|
|
459
|
+
description: 'Indicates whether a certain condition is met.'
|
|
286
460
|
}
|
|
287
461
|
},
|
|
288
|
-
required: [
|
|
289
|
-
|
|
290
|
-
|
|
462
|
+
required: [
|
|
463
|
+
'condition_result'
|
|
464
|
+
],
|
|
465
|
+
additionalProperties: false
|
|
466
|
+
},
|
|
291
467
|
}
|
|
292
468
|
};
|
|
293
469
|
}
|
|
294
470
|
if (parameters.grammar !== 'default') {
|
|
295
471
|
return {
|
|
296
|
-
|
|
297
|
-
|
|
472
|
+
format: {
|
|
473
|
+
type: 'json_schema',
|
|
474
|
+
strict: true,
|
|
298
475
|
name: 'custom_response',
|
|
299
476
|
schema: parameters.grammar,
|
|
300
|
-
}
|
|
477
|
+
},
|
|
301
478
|
};
|
|
302
479
|
}
|
|
303
480
|
return undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
+
import { GenerativeAIEngineType } from '@crewdle/web-sdk-types';
|
|
2
3
|
export class OpenAISpeechConnector {
|
|
3
4
|
client;
|
|
4
5
|
constructor(apiKey) {
|
|
@@ -6,13 +7,13 @@ export class OpenAISpeechConnector {
|
|
|
6
7
|
apiKey,
|
|
7
8
|
});
|
|
8
9
|
}
|
|
9
|
-
async speak(model, text, voice, instructions) {
|
|
10
|
+
async speak(model, text, voice, format, instructions) {
|
|
10
11
|
const response = await this.client.audio.speech.create({
|
|
11
12
|
model,
|
|
12
13
|
voice: voice,
|
|
13
14
|
input: text,
|
|
14
15
|
instructions,
|
|
15
|
-
response_format:
|
|
16
|
+
response_format: format,
|
|
16
17
|
});
|
|
17
18
|
if (!response.body) {
|
|
18
19
|
throw new Error('No response body');
|
|
@@ -30,6 +31,6 @@ export class OpenAISpeechConnector {
|
|
|
30
31
|
});
|
|
31
32
|
}
|
|
32
33
|
getEngineType() {
|
|
33
|
-
return
|
|
34
|
+
return GenerativeAIEngineType.OpenAI;
|
|
34
35
|
}
|
|
35
36
|
}
|
|
@@ -9,9 +9,9 @@ export declare class OpenAIGenerativeAIWorkerConnector implements IGenerativeAIW
|
|
|
9
9
|
getEngineType(): GenerativeAIEngineType;
|
|
10
10
|
processJob(parameters: GenerativeAIWorkerConnectorParameters, options?: IGenerativeAIWorkerOptions): Promise<IGenerativeAIWorkerConnectorPromptResult>;
|
|
11
11
|
processJobStream(parameters: GenerativeAIWorkerConnectorParameters, options?: IGenerativeAIWorkerOptions): AsyncGenerator<IGenerativeAIWorkerConnectorPromptResult>;
|
|
12
|
-
private
|
|
13
|
-
private messageReducer;
|
|
12
|
+
private processToolCall;
|
|
14
13
|
private getMessages;
|
|
14
|
+
private getInnerMessages;
|
|
15
15
|
private getTools;
|
|
16
16
|
private getResponseFormat;
|
|
17
17
|
}
|
|
@@ -2,6 +2,6 @@ import { GenerativeAIEngineType, IGenerativeAISpeechConnector } from '@crewdle/w
|
|
|
2
2
|
export declare class OpenAISpeechConnector implements IGenerativeAISpeechConnector {
|
|
3
3
|
private client;
|
|
4
4
|
constructor(apiKey: string);
|
|
5
|
-
speak(model: string, text: string, voice: string, instructions?: string): Promise<ReadableStream>;
|
|
5
|
+
speak(model: string, text: string, voice: string, format: string, instructions?: string): Promise<ReadableStream>;
|
|
6
6
|
getEngineType(): GenerativeAIEngineType;
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crewdle/mist-connector-openai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
"dist/"
|
|
16
16
|
],
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@crewdle/web-sdk-types": "^1.0.
|
|
18
|
+
"@crewdle/web-sdk-types": "^1.0.48",
|
|
19
19
|
"@types/node": "^22.13.9",
|
|
20
20
|
"typescript": "^5.8.2"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"openai": "^
|
|
23
|
+
"openai": "^5.8.2"
|
|
24
24
|
}
|
|
25
25
|
}
|