@crewdle/mist-connector-openai 1.0.12 → 1.0.14

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.
@@ -22,6 +22,6 @@ export class OpenAIFileConnector {
22
22
  return uploadedFile.id;
23
23
  }
24
24
  async deleteFile(fileId) {
25
- await this.client.files.del(fileId);
25
+ await this.client.files.delete(fileId);
26
26
  }
27
27
  }
@@ -50,33 +50,43 @@ export class OpenAIGenerativeAIWorkerConnector {
50
50
  const responseFormat = this.getResponseFormat(parameters);
51
51
  const tools = this.getTools(parameters);
52
52
  const messages = this.getMessages(parameters);
53
- let totalInputTokens = 0;
54
- let totalOutputTokens = 0;
55
- let accumulatedOutput = '';
53
+ let inputTokens = 0;
54
+ let outputTokens = 0;
55
+ let output = '';
56
56
  while (true) {
57
- const response = await this.client.chat.completions.create({
57
+ const response = await this.client.responses.create({
58
58
  model: options.model.id,
59
- messages,
60
- max_completion_tokens: parameters.maxTokens,
59
+ input: messages,
60
+ max_output_tokens: parameters.maxTokens,
61
61
  temperature: parameters.temperature,
62
- response_format: responseFormat,
62
+ text: responseFormat,
63
63
  tools,
64
64
  });
65
- const message = response.choices[0].message;
66
- totalInputTokens += response.usage?.prompt_tokens ?? 0;
67
- totalOutputTokens += response.usage?.completion_tokens ?? 0;
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
- }
65
+ inputTokens += response.usage?.input_tokens ?? 0;
66
+ outputTokens += response.usage?.output_tokens ?? 0;
77
67
  const promises = [];
78
- this.processToolCalls(promises, message.tool_calls, messages, parameters.functions);
79
- await Promise.all(promises);
68
+ for (const content of response.output) {
69
+ if (content.type === 'message') {
70
+ for (const message of content.content) {
71
+ if (message.type === 'output_text') {
72
+ output += message.text;
73
+ }
74
+ }
75
+ }
76
+ if (content.type === 'function_call') {
77
+ promises.push(this.processToolCall(parameters, messages, content.name, content.call_id, content.arguments));
78
+ }
79
+ }
80
+ if (promises.length > 0) {
81
+ await Promise.all(promises);
82
+ continue;
83
+ }
84
+ return {
85
+ type: "prompt" /* GenerativeAIJobType.Prompt */,
86
+ output,
87
+ inputTokens,
88
+ outputTokens,
89
+ };
80
90
  }
81
91
  }
82
92
  async *processJobStream(parameters, options) {
@@ -90,134 +100,96 @@ export class OpenAIGenerativeAIWorkerConnector {
90
100
  const tools = this.getTools(parameters);
91
101
  const messages = this.getMessages(parameters);
92
102
  while (true) {
93
- const stream = await this.client.chat.completions.create({
103
+ const stream = await this.client.responses.create({
94
104
  model: options.model.id,
95
- messages,
96
- max_completion_tokens: parameters.maxTokens,
105
+ input: messages,
106
+ max_output_tokens: parameters.maxTokens,
97
107
  temperature: parameters.temperature,
98
- stream: true,
99
- stream_options: {
100
- include_usage: true,
101
- },
108
+ text: responseFormat,
102
109
  tools,
103
- response_format: responseFormat,
110
+ stream: true,
104
111
  });
105
- let message = {};
112
+ const promises = [];
106
113
  for await (const chunk of stream) {
107
- if (chunk.choices[0]?.delta?.tool_calls) {
108
- message = this.messageReducer(message, chunk);
114
+ if (chunk.type === 'response.output_text.delta') {
115
+ yield {
116
+ type: "prompt" /* GenerativeAIJobType.Prompt */,
117
+ output: chunk.delta,
118
+ inputTokens: 0,
119
+ outputTokens: 0,
120
+ };
121
+ }
122
+ if (chunk.type === 'response.completed') {
123
+ yield {
124
+ type: "prompt" /* GenerativeAIJobType.Prompt */,
125
+ output: '',
126
+ inputTokens: chunk.response.usage?.input_tokens ?? 0,
127
+ outputTokens: chunk.response.usage?.output_tokens ?? 0,
128
+ };
109
129
  }
130
+ if (chunk.type === 'response.output_item.done') {
131
+ if (chunk.item.type === 'function_call') {
132
+ promises.push(this.processToolCall(parameters, messages, chunk.item.name, chunk.item.call_id, chunk.item.arguments));
133
+ }
134
+ }
135
+ }
136
+ if (promises.length > 0) {
137
+ await Promise.all(promises);
110
138
  yield {
111
- type: 'prompt',
112
- output: chunk.choices[0]?.delta?.content ?? '',
113
- inputTokens: chunk.usage?.prompt_tokens ?? 0,
114
- outputTokens: chunk.usage?.completion_tokens ?? 0,
139
+ type: "prompt" /* GenerativeAIJobType.Prompt */,
140
+ output: '\n\n',
141
+ inputTokens: 0,
142
+ outputTokens: 0,
115
143
  };
144
+ continue;
116
145
  }
117
- if (!message.tool_calls) {
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
- };
146
+ break;
129
147
  }
130
148
  }
131
- processToolCalls(promises, toolCalls, messages, functions) {
149
+ async processToolCall(parameters, messages, toolName, toolCallId, input) {
132
150
  messages.push({
133
- role: 'assistant',
134
- content: null,
135
- tool_calls: toolCalls,
136
- refusal: null,
151
+ type: 'function_call',
152
+ arguments: input,
153
+ call_id: toolCallId,
154
+ name: toolName,
137
155
  });
138
- for (const toolCall of toolCalls) {
139
- if (toolCall?.function && functions) {
140
- try {
141
- const func = functions.get(toolCall.function.name);
142
- if (func) {
143
- const result = func.callback(JSON.parse(toolCall.function.arguments));
144
- if (result instanceof Promise) {
145
- promises.push(result.then((res) => {
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
- }
156
+ const func = parameters.functions?.get(toolName);
157
+ if (!func) {
158
+ messages.push({
159
+ type: 'function_call_output',
160
+ call_id: toolCallId,
161
+ output: 'Tool not found',
162
+ });
163
+ return;
178
164
  }
179
- }
180
- messageReducer(previous, item) {
181
- const reduce = (acc, delta) => {
182
- acc = { ...acc };
183
- for (const [key, value] of Object.entries(delta)) {
184
- if (acc[key] === undefined || acc[key] === null) {
185
- acc[key] = value;
186
- // OpenAI.Chat.Completions.ChatCompletionMessageToolCall does not have a key, .index
187
- if (Array.isArray(acc[key])) {
188
- for (const arr of acc[key]) {
189
- delete arr.index;
190
- }
191
- }
192
- }
193
- else if (typeof acc[key] === 'string' && typeof value === 'string') {
194
- acc[key] += value;
195
- }
196
- else if (typeof acc[key] === 'number' && typeof value === 'number') {
197
- acc[key] = value;
198
- }
199
- else if (Array.isArray(acc[key]) && Array.isArray(value)) {
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
- }
165
+ try {
166
+ if (input === '') {
167
+ input = '{}';
168
+ }
169
+ const result = func.callback(JSON.parse(input));
170
+ if (result instanceof Promise) {
171
+ messages.push({
172
+ type: 'function_call_output',
173
+ call_id: toolCallId,
174
+ output: await result,
175
+ });
176
+ }
177
+ else {
178
+ messages.push({
179
+ type: 'function_call_output',
180
+ call_id: toolCallId,
181
+ output: result,
182
+ });
212
183
  }
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
184
  }
220
- return reduce(previous, choice.delta);
185
+ catch (error) {
186
+ console.error('Error processing tool call', error);
187
+ messages.push({
188
+ type: 'function_call_output',
189
+ call_id: toolCallId,
190
+ output: 'Error processing tool call',
191
+ });
192
+ }
221
193
  }
222
194
  getMessages(parameters) {
223
195
  const messages = [];
@@ -237,31 +209,58 @@ export class OpenAIGenerativeAIWorkerConnector {
237
209
  }
238
210
  messages.push({
239
211
  role: 'user',
240
- content: parameters.prompt,
212
+ content: this.getInnerMessages(parameters.prompt),
241
213
  });
242
214
  return messages;
243
215
  }
216
+ getInnerMessages(content) {
217
+ if (typeof content === 'string') {
218
+ return content;
219
+ }
220
+ return content.map((c) => {
221
+ if (c.type === 'text') {
222
+ return {
223
+ type: 'input_text',
224
+ text: c.text,
225
+ };
226
+ }
227
+ if (c.type === 'file') {
228
+ return {
229
+ type: 'input_file',
230
+ filename: c.file.filename,
231
+ file_data: c.file.file_data,
232
+ };
233
+ }
234
+ if (c.type === 'image_url') {
235
+ return {
236
+ type: 'input_image',
237
+ image_url: c.image_url.url,
238
+ };
239
+ }
240
+ throw new Error('Invalid content type');
241
+ });
242
+ }
244
243
  getTools(parameters) {
245
244
  if (!parameters.functions || parameters.functions.size === 0) {
246
245
  return undefined;
247
246
  }
248
247
  const tools = [];
249
248
  for (const [name, func] of parameters.functions) {
250
- let params;
249
+ let params = null;
251
250
  if (func.params) {
252
251
  params = {
253
252
  type: 'object',
254
253
  properties: func.params,
255
254
  required: Object.keys(func.params),
255
+ additionalProperties: false,
256
256
  };
257
257
  }
258
258
  tools.push({
259
259
  type: 'function',
260
- function: {
261
- name,
262
- description: func.description,
263
- parameters: params,
264
- }
260
+ name,
261
+ description: func.description,
262
+ strict: true,
263
+ parameters: params,
265
264
  });
266
265
  }
267
266
  return tools;
@@ -271,33 +270,38 @@ export class OpenAIGenerativeAIWorkerConnector {
271
270
  return undefined;
272
271
  }
273
272
  if (parameters.grammar === 'json') {
274
- return { type: 'json_object' };
273
+ return { format: { type: 'json_object' } };
275
274
  }
276
275
  if (parameters.grammar === 'if_else') {
277
276
  return {
278
- type: 'json_schema',
279
- json_schema: {
277
+ format: {
278
+ type: 'json_schema',
279
+ strict: true,
280
280
  name: 'router_response',
281
281
  schema: {
282
282
  type: 'object',
283
283
  properties: {
284
- 'condition_result': {
284
+ condition_result: {
285
285
  type: 'boolean',
286
+ description: 'Indicates whether a certain condition is met.'
286
287
  }
287
288
  },
288
- required: ['result'],
289
- additionalProperties: false,
290
- }
289
+ required: [
290
+ 'condition_result'
291
+ ],
292
+ additionalProperties: false
293
+ },
291
294
  }
292
295
  };
293
296
  }
294
297
  if (parameters.grammar !== 'default') {
295
298
  return {
296
- type: 'json_schema',
297
- json_schema: {
299
+ format: {
300
+ type: 'json_schema',
301
+ strict: true,
298
302
  name: 'custom_response',
299
303
  schema: parameters.grammar,
300
- }
304
+ },
301
305
  };
302
306
  }
303
307
  return undefined;
@@ -14,7 +14,20 @@ export class OpenAISpeechConnector {
14
14
  instructions,
15
15
  response_format: 'pcm',
16
16
  });
17
- return response.arrayBuffer();
17
+ if (!response.body) {
18
+ throw new Error('No response body');
19
+ }
20
+ if (response.body instanceof ReadableStream) {
21
+ return response.body;
22
+ }
23
+ const audio = response.body;
24
+ return new ReadableStream({
25
+ start(controller) {
26
+ audio.on('data', (chunk) => controller.enqueue(chunk));
27
+ audio.on('end', () => controller.close());
28
+ audio.on('error', (err) => controller.error(err));
29
+ }
30
+ });
18
31
  }
19
32
  getEngineType() {
20
33
  return 'openai';
@@ -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 processToolCalls;
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<ArrayBuffer>;
5
+ speak(model: string, text: string, voice: 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.12",
3
+ "version": "1.0.14",
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.44",
18
+ "@crewdle/web-sdk-types": "^1.0.46",
19
19
  "@types/node": "^22.13.9",
20
20
  "typescript": "^5.8.2"
21
21
  },
22
22
  "dependencies": {
23
- "openai": "^4.100.0"
23
+ "openai": "^5.5.1"
24
24
  }
25
25
  }