@ai-sdk/openai 4.0.103 → 4.0.105
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/CHANGELOG.md +39 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +181 -75
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +14 -4
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +148 -66
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +20 -10
- package/package.json +2 -2
- package/src/chat/openai-chat-language-model-options.ts +1 -0
- package/src/chat/openai-chat-language-model.ts +30 -15
- package/src/normalize-openai-json-schema.ts +2 -1
- package/src/openai-batch.ts +2 -4
- package/src/openai-decision-model-options.ts +1 -0
- package/src/openai-decision-model.ts +46 -5
- package/src/openai-language-model-capabilities.ts +105 -7
- package/src/responses/openai-responses-language-model-options.ts +2 -0
- package/src/responses/openai-responses-language-model.ts +34 -32
- package/src/transcription/openai-transcription-model-options.ts +5 -6
- package/src/transcription/openai-transcription-model.ts +89 -48
|
@@ -58,14 +58,43 @@ type OpenAIRealtimeTranscriptionEvent = {
|
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
|
|
61
|
+
* Whether `modelId` is `base` or one of its dated snapshots
|
|
62
|
+
* (e.g. `gpt-realtime-whisper-2026-01-01`).
|
|
63
|
+
*/
|
|
64
|
+
function isModelOrSnapshot(modelId: string, base: string): boolean {
|
|
65
|
+
return modelId === base || modelId.startsWith(`${base}-`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Model IDs that stream over the realtime transcription WebSocket and do not
|
|
70
|
+
* support the REST transcription endpoint.
|
|
64
71
|
*/
|
|
65
72
|
function isRealtimeTranscriptionModelId(modelId: string): boolean {
|
|
66
73
|
return (
|
|
67
|
-
modelId
|
|
68
|
-
modelId
|
|
74
|
+
isModelOrSnapshot(modelId, 'gpt-realtime-whisper') ||
|
|
75
|
+
isModelOrSnapshot(modelId, 'gpt-live-transcribe')
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Model IDs that support streaming transcription. `gpt-transcribe` also
|
|
81
|
+
* supports the REST transcription endpoint.
|
|
82
|
+
*/
|
|
83
|
+
function supportsStreamingTranscription(modelId: string): boolean {
|
|
84
|
+
return (
|
|
85
|
+
isRealtimeTranscriptionModelId(modelId) ||
|
|
86
|
+
isModelOrSnapshot(modelId, 'gpt-transcribe')
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Models whose realtime transcription session takes a `languages` list
|
|
92
|
+
* instead of the singular `language` field.
|
|
93
|
+
*/
|
|
94
|
+
function usesLanguagesList(modelId: string): boolean {
|
|
95
|
+
return (
|
|
96
|
+
isModelOrSnapshot(modelId, 'gpt-live-transcribe') ||
|
|
97
|
+
isModelOrSnapshot(modelId, 'gpt-transcribe')
|
|
69
98
|
);
|
|
70
99
|
}
|
|
71
100
|
|
|
@@ -197,54 +226,45 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
197
226
|
`audio.${fileExtension}`,
|
|
198
227
|
);
|
|
199
228
|
|
|
200
|
-
if (this.modelId === 'whisper-1') {
|
|
201
|
-
formData.append('response_format', 'verbose_json');
|
|
202
|
-
}
|
|
203
|
-
|
|
204
229
|
const isDiarizationModel = this.modelId === 'gpt-4o-transcribe-diarize';
|
|
230
|
+
|
|
231
|
+
const responseFormat =
|
|
232
|
+
openAIOptions?.responseFormat ??
|
|
233
|
+
(isDiarizationModel
|
|
234
|
+
? 'diarized_json'
|
|
235
|
+
: this.modelId === 'whisper-1'
|
|
236
|
+
? 'verbose_json'
|
|
237
|
+
: undefined);
|
|
238
|
+
|
|
239
|
+
// Timestamps are only returned with verbose_json, so only default to
|
|
240
|
+
// segments when that format is used.
|
|
241
|
+
const timestampGranularities =
|
|
242
|
+
openAIOptions?.timestampGranularities ??
|
|
243
|
+
(responseFormat === 'verbose_json' ? ['segment'] : undefined);
|
|
244
|
+
|
|
205
245
|
const chunkingStrategy =
|
|
206
246
|
openAIOptions?.chunkingStrategy ??
|
|
207
247
|
(isDiarizationModel ? 'auto' : undefined);
|
|
208
248
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
include: openAIOptions.include,
|
|
218
|
-
language: openAIOptions.language,
|
|
219
|
-
prompt: openAIOptions.prompt,
|
|
220
|
-
// https://platform.openai.com/docs/api-reference/audio/createTranscription#audio_createtranscription-response_format
|
|
221
|
-
// prefer verbose_json to get segments for models that support it
|
|
222
|
-
...(this.modelId !== 'whisper-1' && {
|
|
223
|
-
response_format:
|
|
224
|
-
openAIOptions.responseFormat ??
|
|
225
|
-
(isDiarizationModel
|
|
226
|
-
? 'diarized_json'
|
|
227
|
-
: isGpt4oTranscribeModel
|
|
228
|
-
? 'json'
|
|
229
|
-
: 'verbose_json'),
|
|
230
|
-
}),
|
|
231
|
-
temperature: openAIOptions.temperature,
|
|
232
|
-
timestamp_granularities: openAIOptions.timestampGranularities,
|
|
233
|
-
};
|
|
249
|
+
const transcriptionModelOptions = {
|
|
250
|
+
response_format: responseFormat,
|
|
251
|
+
include: openAIOptions?.include,
|
|
252
|
+
language: openAIOptions?.language,
|
|
253
|
+
prompt: openAIOptions?.prompt,
|
|
254
|
+
temperature: openAIOptions?.temperature,
|
|
255
|
+
timestamp_granularities: timestampGranularities,
|
|
256
|
+
};
|
|
234
257
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
} else {
|
|
242
|
-
formData.append(key, String(value));
|
|
258
|
+
for (const [key, value] of Object.entries(transcriptionModelOptions)) {
|
|
259
|
+
if (value != null) {
|
|
260
|
+
if (Array.isArray(value)) {
|
|
261
|
+
for (const item of value) {
|
|
262
|
+
formData.append(`${key}[]`, String(item));
|
|
243
263
|
}
|
|
264
|
+
} else {
|
|
265
|
+
formData.append(key, String(value));
|
|
244
266
|
}
|
|
245
267
|
}
|
|
246
|
-
} else if (isDiarizationModel) {
|
|
247
|
-
formData.append('response_format', 'diarized_json');
|
|
248
268
|
}
|
|
249
269
|
|
|
250
270
|
if (chunkingStrategy != null) {
|
|
@@ -344,7 +364,13 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
344
364
|
})) ??
|
|
345
365
|
[],
|
|
346
366
|
language,
|
|
347
|
-
|
|
367
|
+
// Duration-billed json responses report the duration only in `usage`:
|
|
368
|
+
durationInSeconds:
|
|
369
|
+
response.duration ??
|
|
370
|
+
(response.usage?.type === 'duration' &&
|
|
371
|
+
typeof response.usage.seconds === 'number'
|
|
372
|
+
? response.usage.seconds
|
|
373
|
+
: undefined),
|
|
348
374
|
warnings,
|
|
349
375
|
...(response.usage != null && { usage: response.usage }),
|
|
350
376
|
response: {
|
|
@@ -375,7 +401,7 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
375
401
|
): Promise<
|
|
376
402
|
Awaited<ReturnType<NonNullable<TranscriptionModelV4['doStream']>>>
|
|
377
403
|
> {
|
|
378
|
-
if (!
|
|
404
|
+
if (!supportsStreamingTranscription(this.modelId)) {
|
|
379
405
|
throw new UnsupportedFunctionalityError({
|
|
380
406
|
functionality: `streaming transcription with ${this.modelId}`,
|
|
381
407
|
});
|
|
@@ -407,11 +433,22 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
407
433
|
}
|
|
408
434
|
}
|
|
409
435
|
|
|
436
|
+
// gpt-transcribe transcribes each committed turn, so it has no delay setting:
|
|
437
|
+
const supportsDelay = !isModelOrSnapshot(this.modelId, 'gpt-transcribe');
|
|
438
|
+
if (!supportsDelay && openAIOptions?.streaming?.delay != null) {
|
|
439
|
+
warnings.push({
|
|
440
|
+
type: 'unsupported',
|
|
441
|
+
feature: 'providerOptions.openai.streaming.delay',
|
|
442
|
+
details: `OpenAI streaming transcription with ${this.modelId} does not support delay.`,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
410
446
|
const headers = combineHeaders(this.config.headers?.(), options.headers);
|
|
411
447
|
const sessionUpdate = buildOpenAIRealtimeTranscriptionSession({
|
|
412
448
|
modelId: this.modelId,
|
|
413
449
|
inputAudioFormat: options.inputAudioFormat,
|
|
414
450
|
providerOptions: openAIOptions,
|
|
451
|
+
supportsDelay,
|
|
415
452
|
});
|
|
416
453
|
|
|
417
454
|
return {
|
|
@@ -600,10 +637,12 @@ function buildOpenAIRealtimeTranscriptionSession({
|
|
|
600
637
|
modelId,
|
|
601
638
|
inputAudioFormat,
|
|
602
639
|
providerOptions,
|
|
640
|
+
supportsDelay,
|
|
603
641
|
}: {
|
|
604
642
|
modelId: string;
|
|
605
643
|
inputAudioFormat: TranscriptionModelV4StreamOptions['inputAudioFormat'];
|
|
606
644
|
providerOptions: OpenAITranscriptionModelOptions | undefined;
|
|
645
|
+
supportsDelay: boolean;
|
|
607
646
|
}) {
|
|
608
647
|
return {
|
|
609
648
|
type: 'session.update',
|
|
@@ -620,9 +659,11 @@ function buildOpenAIRealtimeTranscriptionSession({
|
|
|
620
659
|
transcription: {
|
|
621
660
|
model: modelId,
|
|
622
661
|
...(providerOptions?.language != null
|
|
623
|
-
?
|
|
662
|
+
? usesLanguagesList(modelId)
|
|
663
|
+
? { languages: [providerOptions.language] }
|
|
664
|
+
: { language: providerOptions.language }
|
|
624
665
|
: {}),
|
|
625
|
-
...(providerOptions?.streaming?.delay != null
|
|
666
|
+
...(supportsDelay && providerOptions?.streaming?.delay != null
|
|
626
667
|
? { delay: providerOptions.streaming.delay }
|
|
627
668
|
: {}),
|
|
628
669
|
},
|