@ai-sdk/openai 4.0.105 → 4.0.107
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 +26 -0
- package/dist/index.d.ts +51 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +611 -202
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +6 -4
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +565 -164
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +127 -5
- package/package.json +3 -3
- package/src/chat/convert-to-openai-chat-messages.ts +22 -3
- package/src/chat/openai-chat-language-model.ts +27 -20
- package/src/completion/openai-completion-language-model.ts +11 -3
- package/src/embedding/openai-embedding-model.ts +2 -1
- package/src/files/openai-files.ts +10 -8
- package/src/image/openai-image-model-options.ts +10 -20
- package/src/image/openai-image-model.ts +10 -21
- package/src/live/openai-realtime-model-live.ts +15 -16
- package/src/openai-batch.ts +22 -6
- package/src/openai-config.ts +2 -1
- package/src/openai-decision-model.ts +2 -1
- package/src/openai-model-catalog.ts +935 -0
- package/src/openai-provider.ts +18 -13
- package/src/realtime/openai-realtime-factory.ts +6 -6
- package/src/realtime/openai-realtime-model.ts +7 -3
- package/src/resolve-openai-reasoning-effort.ts +45 -0
- package/src/responses/convert-to-openai-responses-input.ts +29 -11
- package/src/responses/openai-responses-language-model.ts +28 -22
- package/src/skills/openai-skills.ts +4 -2
- package/src/speech/openai-speech-model.ts +5 -1
- package/src/speech-translation/openai-speech-translation-model.ts +5 -1
- package/src/transcription/openai-transcription-model-options.ts +12 -0
- package/src/transcription/openai-transcription-model.ts +29 -56
- package/src/openai-language-model-capabilities.ts +0 -196
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type TranscriptionModelV4CallOptions,
|
|
7
7
|
} from '@ai-sdk/provider';
|
|
8
8
|
import {
|
|
9
|
+
resolve,
|
|
9
10
|
combineHeaders,
|
|
10
11
|
convertBase64ToUint8Array,
|
|
11
12
|
convertToBase64,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
} from '@ai-sdk/provider-utils';
|
|
26
27
|
import type { OpenAIConfig } from '../openai-config';
|
|
27
28
|
import { openaiFailedResponseHandler } from '../openai-error';
|
|
29
|
+
import { getOpenAITranscriptionModelEntry } from '../openai-model-catalog';
|
|
28
30
|
import { openaiTranscriptionResponseSchema } from './openai-transcription-api';
|
|
29
31
|
import {
|
|
30
32
|
openAITranscriptionModelOptions,
|
|
@@ -57,47 +59,6 @@ type OpenAIRealtimeTranscriptionEvent = {
|
|
|
57
59
|
error?: { message?: string };
|
|
58
60
|
};
|
|
59
61
|
|
|
60
|
-
/**
|
|
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.
|
|
71
|
-
*/
|
|
72
|
-
function isRealtimeTranscriptionModelId(modelId: string): boolean {
|
|
73
|
-
return (
|
|
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')
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
62
|
interface OpenAITranscriptionModelConfig extends OpenAIConfig {
|
|
102
63
|
_internal?: {
|
|
103
64
|
currentDate?: () => Date;
|
|
@@ -226,15 +187,12 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
226
187
|
`audio.${fileExtension}`,
|
|
227
188
|
);
|
|
228
189
|
|
|
229
|
-
const
|
|
190
|
+
const { transcription: requestConfig } = getOpenAITranscriptionModelEntry(
|
|
191
|
+
this.modelId,
|
|
192
|
+
).requestConfig;
|
|
230
193
|
|
|
231
194
|
const responseFormat =
|
|
232
|
-
openAIOptions?.responseFormat ??
|
|
233
|
-
(isDiarizationModel
|
|
234
|
-
? 'diarized_json'
|
|
235
|
-
: this.modelId === 'whisper-1'
|
|
236
|
-
? 'verbose_json'
|
|
237
|
-
: undefined);
|
|
195
|
+
openAIOptions?.responseFormat ?? requestConfig.responseFormat;
|
|
238
196
|
|
|
239
197
|
// Timestamps are only returned with verbose_json, so only default to
|
|
240
198
|
// segments when that format is used.
|
|
@@ -243,13 +201,14 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
243
201
|
(responseFormat === 'verbose_json' ? ['segment'] : undefined);
|
|
244
202
|
|
|
245
203
|
const chunkingStrategy =
|
|
246
|
-
openAIOptions?.chunkingStrategy ??
|
|
247
|
-
(isDiarizationModel ? 'auto' : undefined);
|
|
204
|
+
openAIOptions?.chunkingStrategy ?? requestConfig.chunkingStrategy;
|
|
248
205
|
|
|
249
206
|
const transcriptionModelOptions = {
|
|
250
207
|
response_format: responseFormat,
|
|
251
208
|
include: openAIOptions?.include,
|
|
252
209
|
language: openAIOptions?.language,
|
|
210
|
+
languages: openAIOptions?.languages,
|
|
211
|
+
keywords: openAIOptions?.keywords,
|
|
253
212
|
prompt: openAIOptions?.prompt,
|
|
254
213
|
temperature: openAIOptions?.temperature,
|
|
255
214
|
timestamp_granularities: timestampGranularities,
|
|
@@ -296,7 +255,10 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
296
255
|
async doGenerate(
|
|
297
256
|
options: OpenAITranscriptionCallOptions,
|
|
298
257
|
): Promise<Awaited<ReturnType<TranscriptionModelV4['doGenerate']>>> {
|
|
299
|
-
if (
|
|
258
|
+
if (
|
|
259
|
+
getOpenAITranscriptionModelEntry(this.modelId).capabilities.transcription
|
|
260
|
+
.transcription === false
|
|
261
|
+
) {
|
|
300
262
|
throw new UnsupportedFunctionalityError({
|
|
301
263
|
functionality: `non-streaming transcription with ${this.modelId}`,
|
|
302
264
|
});
|
|
@@ -314,7 +276,10 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
314
276
|
path: '/audio/transcriptions',
|
|
315
277
|
modelId: this.modelId,
|
|
316
278
|
}),
|
|
317
|
-
headers: combineHeaders(
|
|
279
|
+
headers: combineHeaders(
|
|
280
|
+
await resolve(this.config.headers),
|
|
281
|
+
options.headers,
|
|
282
|
+
),
|
|
318
283
|
formData,
|
|
319
284
|
failedResponseHandler: openaiFailedResponseHandler,
|
|
320
285
|
successfulResponseHandler: createJsonResponseHandler(
|
|
@@ -401,7 +366,8 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
401
366
|
): Promise<
|
|
402
367
|
Awaited<ReturnType<NonNullable<TranscriptionModelV4['doStream']>>>
|
|
403
368
|
> {
|
|
404
|
-
|
|
369
|
+
const entry = getOpenAITranscriptionModelEntry(this.modelId);
|
|
370
|
+
if (entry.capabilities.transcription.streaming !== true) {
|
|
405
371
|
throw new UnsupportedFunctionalityError({
|
|
406
372
|
functionality: `streaming transcription with ${this.modelId}`,
|
|
407
373
|
});
|
|
@@ -420,6 +386,8 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
420
386
|
const rawOpenAIOptions = options.providerOptions?.openai ?? {};
|
|
421
387
|
for (const option of [
|
|
422
388
|
'include',
|
|
389
|
+
'languages',
|
|
390
|
+
'keywords',
|
|
423
391
|
'prompt',
|
|
424
392
|
'temperature',
|
|
425
393
|
'timestampGranularities',
|
|
@@ -434,7 +402,8 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
434
402
|
}
|
|
435
403
|
|
|
436
404
|
// gpt-transcribe transcribes each committed turn, so it has no delay setting:
|
|
437
|
-
const supportsDelay =
|
|
405
|
+
const supportsDelay =
|
|
406
|
+
entry.capabilities.transcription.streamingDelay === true;
|
|
438
407
|
if (!supportsDelay && openAIOptions?.streaming?.delay != null) {
|
|
439
408
|
warnings.push({
|
|
440
409
|
type: 'unsupported',
|
|
@@ -443,7 +412,10 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
443
412
|
});
|
|
444
413
|
}
|
|
445
414
|
|
|
446
|
-
const headers = combineHeaders(
|
|
415
|
+
const headers = combineHeaders(
|
|
416
|
+
await resolve(this.config.headers),
|
|
417
|
+
options.headers,
|
|
418
|
+
);
|
|
447
419
|
const sessionUpdate = buildOpenAIRealtimeTranscriptionSession({
|
|
448
420
|
modelId: this.modelId,
|
|
449
421
|
inputAudioFormat: options.inputAudioFormat,
|
|
@@ -659,7 +631,8 @@ function buildOpenAIRealtimeTranscriptionSession({
|
|
|
659
631
|
transcription: {
|
|
660
632
|
model: modelId,
|
|
661
633
|
...(providerOptions?.language != null
|
|
662
|
-
?
|
|
634
|
+
? getOpenAITranscriptionModelEntry(modelId).requestConfig
|
|
635
|
+
.transcription.languageField === 'languages'
|
|
663
636
|
? { languages: [providerOptions.language] }
|
|
664
637
|
: { language: providerOptions.language }
|
|
665
638
|
: {}),
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
LanguageModelV4CallOptions,
|
|
3
|
-
SharedV4Warning,
|
|
4
|
-
} from '@ai-sdk/provider';
|
|
5
|
-
import { mapReasoningToProviderEffort } from '@ai-sdk/provider-utils';
|
|
6
|
-
|
|
7
|
-
type OpenAIReasoningEffort = Exclude<
|
|
8
|
-
LanguageModelV4CallOptions['reasoning'],
|
|
9
|
-
'provider-default' | undefined
|
|
10
|
-
>;
|
|
11
|
-
|
|
12
|
-
// Unknown model families send the reasoning value directly.
|
|
13
|
-
const ALL_OPENAI_REASONING_EFFORTS: readonly OpenAIReasoningEffort[] = [
|
|
14
|
-
'none',
|
|
15
|
-
'minimal',
|
|
16
|
-
'low',
|
|
17
|
-
'medium',
|
|
18
|
-
'high',
|
|
19
|
-
'xhigh',
|
|
20
|
-
'max',
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
export type OpenAILanguageModelCapabilities = {
|
|
24
|
-
isReasoningModel: boolean;
|
|
25
|
-
systemMessageMode: 'remove' | 'system' | 'developer';
|
|
26
|
-
supportsFlexProcessing: boolean;
|
|
27
|
-
supportsPriorityProcessing: boolean;
|
|
28
|
-
supportsConfigurationUpdate: boolean;
|
|
29
|
-
supportsAsyncToolCalling: boolean;
|
|
30
|
-
/**
|
|
31
|
-
* Reasoning efforts supported by the model family, or `undefined` when they
|
|
32
|
-
* are not known.
|
|
33
|
-
*/
|
|
34
|
-
supportedReasoningEfforts: readonly OpenAIReasoningEffort[] | undefined;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Whether function tools can be combined with a reasoning effort other than
|
|
38
|
-
* `none` in the Chat Completions API.
|
|
39
|
-
*/
|
|
40
|
-
supportsChatFunctionToolsWithReasoning: boolean;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Allow temperature, topP, logProbs when reasoningEffort is none.
|
|
44
|
-
*/
|
|
45
|
-
supportsNonReasoningParameters: boolean;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export function getOpenAILanguageModelCapabilities(
|
|
49
|
-
modelId: string,
|
|
50
|
-
): OpenAILanguageModelCapabilities {
|
|
51
|
-
const oSeriesVersion = getOSeriesVersion(modelId);
|
|
52
|
-
const gptVersion = getGptVersion(modelId);
|
|
53
|
-
const isGptChatModel =
|
|
54
|
-
gptVersion?.minor == null &&
|
|
55
|
-
(gptVersion?.variant?.startsWith('chat') ?? false);
|
|
56
|
-
const isGptNanoModel = gptVersion?.variant?.startsWith('nano') ?? false;
|
|
57
|
-
const isGpt6OrLaterModel = gptVersion != null && gptVersion.major >= 6;
|
|
58
|
-
|
|
59
|
-
// Pro, codex, and chat variants are excluded because their supported
|
|
60
|
-
// efforts are not known.
|
|
61
|
-
const isStandardGpt5Model =
|
|
62
|
-
gptVersion?.major === 5 &&
|
|
63
|
-
(gptVersion.variant == null ||
|
|
64
|
-
/^(?:(?:mini|nano|sol|luna|terra)(?:-\d{4}-\d{2}-\d{2})?|\d{4}-\d{2}-\d{2})$/.test(
|
|
65
|
-
gptVersion.variant,
|
|
66
|
-
));
|
|
67
|
-
|
|
68
|
-
const isGpt6OrLaterModelWithNoneEffort =
|
|
69
|
-
modelId === 'gpt-6-sol' ||
|
|
70
|
-
modelId === 'gpt-6-luna' ||
|
|
71
|
-
modelId === 'gpt-6.1-terra';
|
|
72
|
-
|
|
73
|
-
const supportsFlexProcessing =
|
|
74
|
-
(oSeriesVersion != null && oSeriesVersion >= 3) ||
|
|
75
|
-
(gptVersion != null && gptVersion.major >= 5 && !isGptChatModel);
|
|
76
|
-
|
|
77
|
-
const supportsPriorityProcessing =
|
|
78
|
-
modelId.startsWith('gpt-4') ||
|
|
79
|
-
(gptVersion != null &&
|
|
80
|
-
gptVersion.major >= 5 &&
|
|
81
|
-
!isGptNanoModel &&
|
|
82
|
-
!isGptChatModel) ||
|
|
83
|
-
(oSeriesVersion != null && oSeriesVersion >= 3);
|
|
84
|
-
|
|
85
|
-
// Only recognizable OpenAI model families should use the developer role.
|
|
86
|
-
// Fine-tuned, third-party, and custom model IDs keep conservative defaults.
|
|
87
|
-
const isReasoningModel =
|
|
88
|
-
oSeriesVersion != null ||
|
|
89
|
-
(gptVersion != null && gptVersion.major >= 5 && !isGptChatModel);
|
|
90
|
-
|
|
91
|
-
// https://platform.openai.com/docs/guides/latest-model#gpt-5-1-parameter-compatibility
|
|
92
|
-
// GPT-5.1 through GPT-5.x, GPT-6 Sol/Luna, and GPT-6.1 Terra support
|
|
93
|
-
// temperature, topP, and logProbs when reasoningEffort is none.
|
|
94
|
-
const supportsNonReasoningParameters =
|
|
95
|
-
isGpt6OrLaterModelWithNoneEffort ||
|
|
96
|
-
(!isGpt6OrLaterModel &&
|
|
97
|
-
gptVersion != null &&
|
|
98
|
-
(gptVersion.major > 5 ||
|
|
99
|
-
(gptVersion.major === 5 && (gptVersion.minor ?? 0) >= 1)));
|
|
100
|
-
|
|
101
|
-
const systemMessageMode = isReasoningModel ? 'developer' : 'system';
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
supportsFlexProcessing,
|
|
105
|
-
supportsPriorityProcessing,
|
|
106
|
-
supportsConfigurationUpdate: isGpt6OrLaterModel,
|
|
107
|
-
supportsAsyncToolCalling: isGpt6OrLaterModel,
|
|
108
|
-
supportedReasoningEfforts: isGpt6OrLaterModelWithNoneEffort
|
|
109
|
-
? ['none', 'low', 'medium', 'high', 'xhigh', 'max']
|
|
110
|
-
: isGpt6OrLaterModel
|
|
111
|
-
? ['low', 'medium', 'high', 'xhigh', 'max']
|
|
112
|
-
: isStandardGpt5Model
|
|
113
|
-
? getGpt5ReasoningEfforts(gptVersion.minor ?? 0)
|
|
114
|
-
: undefined,
|
|
115
|
-
// GPT-5.6 rejects function tools with reasoning in the Chat Completions API.
|
|
116
|
-
supportsChatFunctionToolsWithReasoning: !(
|
|
117
|
-
gptVersion?.major === 5 && (gptVersion.minor ?? 0) >= 6
|
|
118
|
-
),
|
|
119
|
-
isReasoningModel,
|
|
120
|
-
systemMessageMode,
|
|
121
|
-
supportsNonReasoningParameters,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function getGpt5ReasoningEfforts(
|
|
126
|
-
minor: number,
|
|
127
|
-
): readonly OpenAIReasoningEffort[] {
|
|
128
|
-
if (minor === 0) {
|
|
129
|
-
return ['minimal', 'low', 'medium', 'high'];
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (minor === 1) {
|
|
133
|
-
return ['none', 'low', 'medium', 'high'];
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (minor < 6) {
|
|
137
|
-
return ['none', 'low', 'medium', 'high', 'xhigh'];
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return ['none', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function getOSeriesVersion(modelId: string): number | undefined {
|
|
144
|
-
const match = /^o(\d+)(?:-|$)/.exec(modelId);
|
|
145
|
-
return match == null ? undefined : Number(match[1]);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function getGptVersion(modelId: string):
|
|
149
|
-
| {
|
|
150
|
-
major: number;
|
|
151
|
-
minor: number | undefined;
|
|
152
|
-
variant: string | undefined;
|
|
153
|
-
}
|
|
154
|
-
| undefined {
|
|
155
|
-
const match = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/.exec(modelId);
|
|
156
|
-
|
|
157
|
-
if (match == null) {
|
|
158
|
-
return undefined;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
major: Number(match[1]),
|
|
163
|
-
minor: match[2] == null ? undefined : Number(match[2]),
|
|
164
|
-
variant: match[3],
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Resolves the reasoning effort to send to OpenAI.
|
|
170
|
-
*
|
|
171
|
-
* A reasoning effort from provider options is sent unchanged. Otherwise the
|
|
172
|
-
* top-level reasoning value is mapped to the closest effort that the model
|
|
173
|
-
* family supports, or sent directly when the supported efforts are not known.
|
|
174
|
-
*/
|
|
175
|
-
export function resolveOpenAIReasoningEffort({
|
|
176
|
-
providerReasoningEffort,
|
|
177
|
-
reasoning,
|
|
178
|
-
supportedReasoningEfforts,
|
|
179
|
-
warnings,
|
|
180
|
-
}: {
|
|
181
|
-
providerReasoningEffort: string | null | undefined;
|
|
182
|
-
reasoning: LanguageModelV4CallOptions['reasoning'];
|
|
183
|
-
supportedReasoningEfforts: readonly OpenAIReasoningEffort[] | undefined;
|
|
184
|
-
warnings: SharedV4Warning[];
|
|
185
|
-
}): string | undefined {
|
|
186
|
-
return mapReasoningToProviderEffort({
|
|
187
|
-
reasoning,
|
|
188
|
-
providerOptionsEffort: providerReasoningEffort,
|
|
189
|
-
effortMap: Object.fromEntries(
|
|
190
|
-
(supportedReasoningEfforts ?? ALL_OPENAI_REASONING_EFFORTS).map(
|
|
191
|
-
effort => [effort, effort],
|
|
192
|
-
),
|
|
193
|
-
),
|
|
194
|
-
warnings,
|
|
195
|
-
});
|
|
196
|
-
}
|