@crewdle/mist-connector-openai 1.0.20 → 1.0.22

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.
@@ -55,7 +55,12 @@ export class OpenAIGenerativeAIWorkerConnector {
55
55
  let file;
56
56
  for (const message of parameters.prompt) {
57
57
  if (message.type === 'input_audio') {
58
- file = new File([message.input_audio.data], 'audio.mp3', { type: message.input_audio.format });
58
+ const dataParts = message.input_audio.data.split(',');
59
+ const data = dataParts[1];
60
+ const type = dataParts[0].replace('data:', '').replace(';base64', '');
61
+ const byteArray = Uint8Array.from(Buffer.from(data, 'base64'));
62
+ const blob = new Blob([byteArray], { type });
63
+ file = new File([blob], `audio.${message.input_audio.format}`, { type });
59
64
  }
60
65
  }
61
66
  if (!file) {
@@ -72,7 +77,7 @@ export class OpenAIGenerativeAIWorkerConnector {
72
77
  console.log('OpenAIGenerativeAIWorkerConnector.processJob audio transcription response');
73
78
  return {
74
79
  type: "prompt" /* GenerativeAIJobType.Prompt */,
75
- output: (!parameters.responseFormat || parameters.responseFormat !== 'json') ? response.text : JSON.stringify(response),
80
+ output: (!parameters.responseFormat || parameters.responseFormat === 'json') ? response.text : JSON.stringify(response),
76
81
  inputTokens: response.usage?.type === 'tokens' ? response.usage.input_tokens : 0,
77
82
  outputTokens: response.usage?.type === 'tokens' ? response.usage.output_tokens : 0,
78
83
  };
@@ -127,25 +132,50 @@ export class OpenAIGenerativeAIWorkerConnector {
127
132
  };
128
133
  }
129
134
  }
130
- const responseFormat = this.getResponseFormat(parameters);
135
+ const responseFormat = this.getResponseFormat(parameters, options.model.id);
131
136
  const tools = this.getTools(parameters);
132
137
  const messages = this.getMessages(parameters);
138
+ const reasoning = this.getReasoning(parameters, options.model.id);
133
139
  let inputTokens = 0;
134
140
  let outputTokens = 0;
135
141
  let output = '';
136
142
  let resultFile;
143
+ let partial = '';
144
+ let responseId;
145
+ let continuationCount = 0;
146
+ const MAX_CONTINUATIONS = 5;
137
147
  while (true) {
138
148
  console.log('OpenAIGenerativeAIWorkerConnector.processJob', options.model.id);
139
149
  const response = await this.client.responses.create({
140
150
  model: options.model.id,
141
- input: messages,
142
- max_output_tokens: parameters.maxTokens,
143
- temperature: parameters.temperature,
151
+ input: responseId
152
+ ? [
153
+ {
154
+ role: 'developer',
155
+ content: [
156
+ {
157
+ type: "input_text",
158
+ text: `Continue exactly after the following already-emitted text (do NOT repeat any of it).
159
+ ANCHOR START:
160
+ ${partial.slice(-400)}
161
+ ANCHOR END.
162
+ Only output the missing remainder. Do not restart or re-open tags already emitted.`,
163
+ },
164
+ ],
165
+ },
166
+ ]
167
+ : messages,
168
+ max_output_tokens: !options.model.id.startsWith('gpt-5') ? parameters.maxTokens : Math.max(parameters.maxTokens ?? 0, 30000),
169
+ temperature: !options.model.id.startsWith('gpt-5') ? parameters.temperature : undefined,
144
170
  text: responseFormat,
145
171
  tools,
172
+ previous_response_id: responseId,
173
+ reasoning,
146
174
  store: parameters.privacy === true ? false : true,
147
175
  });
148
176
  console.log('OpenAIGenerativeAIWorkerConnector.processJob response');
177
+ responseId = undefined;
178
+ partial = '';
149
179
  inputTokens += response.usage?.input_tokens ?? 0;
150
180
  outputTokens += response.usage?.output_tokens ?? 0;
151
181
  const promises = [];
@@ -154,6 +184,7 @@ export class OpenAIGenerativeAIWorkerConnector {
154
184
  for (const message of content.content) {
155
185
  if (message.type === 'output_text') {
156
186
  output += message.text;
187
+ partial += message.text;
157
188
  }
158
189
  }
159
190
  }
@@ -164,8 +195,22 @@ export class OpenAIGenerativeAIWorkerConnector {
164
195
  promises.push(this.processToolCall(parameters, messages, content.name, content.call_id, content.arguments));
165
196
  }
166
197
  }
198
+ if (response.status === 'incomplete') {
199
+ console.log('OpenAIGenerativeAIWorkerConnector.processJob response.incomplete', response.incomplete_details?.reason, parameters.maxTokens);
200
+ if (response.incomplete_details?.reason === 'max_output_tokens') {
201
+ if (continuationCount < MAX_CONTINUATIONS) {
202
+ responseId = response.id;
203
+ continuationCount++;
204
+ continue;
205
+ }
206
+ else {
207
+ console.log('OpenAIGenerativeAIWorkerConnector.processJob max continuations reached');
208
+ }
209
+ }
210
+ }
167
211
  if (promises.length > 0) {
168
212
  await Promise.all(promises);
213
+ output += '\n\n';
169
214
  continue;
170
215
  }
171
216
  return {
@@ -290,25 +335,56 @@ export class OpenAIGenerativeAIWorkerConnector {
290
335
  return;
291
336
  }
292
337
  }
293
- const responseFormat = this.getResponseFormat(parameters);
338
+ const responseFormat = this.getResponseFormat(parameters, options.model.id);
294
339
  const tools = this.getTools(parameters);
295
340
  const messages = this.getMessages(parameters);
341
+ const reasoning = this.getReasoning(parameters, options.model.id);
342
+ let firstChunk = true;
343
+ let responseId;
344
+ let partial = '';
345
+ let continuationCount = 0;
346
+ const MAX_CONTINUATIONS = 5;
296
347
  while (true) {
297
348
  console.log('OpenAIGenerativeAIWorkerConnector.processJobStream', options.model.id);
298
349
  const stream = await this.client.responses.create({
299
350
  model: options.model.id,
300
- input: messages,
301
- max_output_tokens: parameters.maxTokens,
302
- temperature: parameters.temperature,
351
+ input: responseId
352
+ ? [
353
+ {
354
+ role: 'developer',
355
+ content: [
356
+ {
357
+ type: "input_text",
358
+ text: `Continue exactly after the following already-emitted text (do NOT repeat any of it).
359
+ ANCHOR START:
360
+ ${partial.slice(-400)}
361
+ ANCHOR END.
362
+ Only output the missing remainder. Do not restart or re-open tags already emitted.`,
363
+ },
364
+ ],
365
+ },
366
+ ]
367
+ : messages,
368
+ max_output_tokens: !options.model.id.startsWith('gpt-5') ? parameters.maxTokens : Math.max(parameters.maxTokens ?? 0, 30000),
369
+ temperature: !options.model.id.startsWith('gpt-5') ? parameters.temperature : undefined,
303
370
  text: responseFormat,
304
371
  tools,
372
+ previous_response_id: responseId,
373
+ reasoning,
305
374
  stream: true,
306
375
  store: parameters.privacy === true ? false : true,
307
376
  });
308
377
  console.log('OpenAIGenerativeAIWorkerConnector.processJobStream response');
378
+ responseId = undefined;
379
+ partial = '';
309
380
  const promises = [];
310
381
  for await (const chunk of stream) {
311
382
  if (chunk.type === 'response.output_text.delta') {
383
+ if (firstChunk && chunk.delta.trim()) {
384
+ console.log('OpenAIGenerativeAIWorkerConnector.processJobStream first chunk');
385
+ firstChunk = false;
386
+ }
387
+ partial += chunk.delta;
312
388
  yield {
313
389
  type: "prompt" /* GenerativeAIJobType.Prompt */,
314
390
  output: chunk.delta,
@@ -324,6 +400,24 @@ export class OpenAIGenerativeAIWorkerConnector {
324
400
  outputTokens: chunk.response.usage?.output_tokens ?? 0,
325
401
  };
326
402
  }
403
+ if (chunk.type === 'response.incomplete') {
404
+ console.log('OpenAIGenerativeAIWorkerConnector.processJobStream response.incomplete', chunk.response.incomplete_details?.reason, parameters.maxTokens);
405
+ yield {
406
+ type: "prompt" /* GenerativeAIJobType.Prompt */,
407
+ output: '',
408
+ inputTokens: chunk.response.usage?.input_tokens ?? 0,
409
+ outputTokens: chunk.response.usage?.output_tokens ?? 0,
410
+ };
411
+ if (!parameters.privacy && chunk.response.incomplete_details?.reason === 'max_output_tokens') {
412
+ if (continuationCount < MAX_CONTINUATIONS) {
413
+ responseId = chunk.response.id;
414
+ continuationCount++;
415
+ }
416
+ else {
417
+ console.log('OpenAIGenerativeAIWorkerConnector.processJobStream max continuations reached');
418
+ }
419
+ }
420
+ }
327
421
  if (chunk.type === 'response.output_item.done') {
328
422
  if (chunk.item.type === 'function_call') {
329
423
  promises.push(this.processToolCall(parameters, messages, chunk.item.name, chunk.item.call_id, chunk.item.arguments));
@@ -339,6 +433,10 @@ export class OpenAIGenerativeAIWorkerConnector {
339
433
  }
340
434
  }
341
435
  }
436
+ if (responseId) {
437
+ console.log('OpenAIGenerativeAIWorkerConnector.processJobStream needContinue');
438
+ continue;
439
+ }
342
440
  if (promises.length > 0) {
343
441
  await Promise.all(promises);
344
442
  yield {
@@ -412,7 +510,7 @@ export class OpenAIGenerativeAIWorkerConnector {
412
510
  for (const message of parameters.history) {
413
511
  messages.push({
414
512
  role: message.source === 'human' ? 'user' : 'assistant',
415
- content: message.message,
513
+ content: this.getInnerMessages(message.message),
416
514
  });
417
515
  }
418
516
  }
@@ -486,8 +584,22 @@ export class OpenAIGenerativeAIWorkerConnector {
486
584
  }
487
585
  return tools;
488
586
  }
489
- getResponseFormat(parameters) {
490
- if (!parameters.grammar) {
587
+ getReasoning(parameters, modelId) {
588
+ if (!modelId.startsWith('gpt-5')) {
589
+ return undefined;
590
+ }
591
+ if (!parameters.reasoning) {
592
+ return undefined;
593
+ }
594
+ return {
595
+ effort: parameters.reasoning,
596
+ };
597
+ }
598
+ getResponseFormat(parameters, modelId) {
599
+ if (!parameters.grammar || parameters.grammar === 'default') {
600
+ if (modelId.startsWith('gpt-5') && parameters.verbosity) {
601
+ return { verbosity: parameters.verbosity };
602
+ }
491
603
  return undefined;
492
604
  }
493
605
  if (parameters.grammar === 'json') {
@@ -13,5 +13,6 @@ export declare class OpenAIGenerativeAIWorkerConnector implements IGenerativeAIW
13
13
  private getMessages;
14
14
  private getInnerMessages;
15
15
  private getTools;
16
+ private getReasoning;
16
17
  private getResponseFormat;
17
18
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewdle/mist-connector-openai",
3
- "version": "1.0.20",
3
+ "version": "1.0.22",
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.53",
18
+ "@crewdle/web-sdk-types": "^1.0.55",
19
19
  "@types/node": "^22.13.9",
20
20
  "typescript": "^5.8.2"
21
21
  },
22
22
  "dependencies": {
23
- "openai": "^5.8.2"
23
+ "openai": "^6.1.0"
24
24
  }
25
25
  }