@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.
Files changed (35) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/index.d.ts +51 -9
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +611 -202
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +6 -4
  7. package/dist/internal/index.d.ts.map +1 -1
  8. package/dist/internal/index.js +565 -164
  9. package/dist/internal/index.js.map +1 -1
  10. package/docs/03-openai.mdx +127 -5
  11. package/package.json +3 -3
  12. package/src/chat/convert-to-openai-chat-messages.ts +22 -3
  13. package/src/chat/openai-chat-language-model.ts +27 -20
  14. package/src/completion/openai-completion-language-model.ts +11 -3
  15. package/src/embedding/openai-embedding-model.ts +2 -1
  16. package/src/files/openai-files.ts +10 -8
  17. package/src/image/openai-image-model-options.ts +10 -20
  18. package/src/image/openai-image-model.ts +10 -21
  19. package/src/live/openai-realtime-model-live.ts +15 -16
  20. package/src/openai-batch.ts +22 -6
  21. package/src/openai-config.ts +2 -1
  22. package/src/openai-decision-model.ts +2 -1
  23. package/src/openai-model-catalog.ts +935 -0
  24. package/src/openai-provider.ts +18 -13
  25. package/src/realtime/openai-realtime-factory.ts +6 -6
  26. package/src/realtime/openai-realtime-model.ts +7 -3
  27. package/src/resolve-openai-reasoning-effort.ts +45 -0
  28. package/src/responses/convert-to-openai-responses-input.ts +29 -11
  29. package/src/responses/openai-responses-language-model.ts +28 -22
  30. package/src/skills/openai-skills.ts +4 -2
  31. package/src/speech/openai-speech-model.ts +5 -1
  32. package/src/speech-translation/openai-speech-translation-model.ts +5 -1
  33. package/src/transcription/openai-transcription-model-options.ts +12 -0
  34. package/src/transcription/openai-transcription-model.ts +29 -56
  35. 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 isDiarizationModel = this.modelId === 'gpt-4o-transcribe-diarize';
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 (isRealtimeTranscriptionModelId(this.modelId)) {
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(this.config.headers?.(), options.headers),
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
- if (!supportsStreamingTranscription(this.modelId)) {
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 = !isModelOrSnapshot(this.modelId, 'gpt-transcribe');
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(this.config.headers?.(), options.headers);
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
- ? usesLanguagesList(modelId)
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
- }