@ai-sdk/openai 4.0.104 → 4.0.106

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 (33) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/index.d.ts +50 -9
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +651 -185
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +4 -4
  7. package/dist/internal/index.d.ts.map +1 -1
  8. package/dist/internal/index.js +573 -142
  9. package/dist/internal/index.js.map +1 -1
  10. package/docs/03-openai.mdx +101 -9
  11. package/package.json +3 -3
  12. package/src/chat/openai-chat-language-model.ts +50 -28
  13. package/src/completion/openai-completion-language-model.ts +11 -3
  14. package/src/embedding/openai-embedding-model.ts +2 -1
  15. package/src/files/openai-files.ts +10 -8
  16. package/src/image/openai-image-model-options.ts +10 -20
  17. package/src/image/openai-image-model.ts +10 -21
  18. package/src/live/openai-realtime-model-live.ts +15 -16
  19. package/src/openai-batch.ts +22 -6
  20. package/src/openai-config.ts +2 -1
  21. package/src/openai-decision-model-options.ts +1 -0
  22. package/src/openai-decision-model.ts +48 -6
  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/openai-responses-language-model.ts +35 -36
  29. package/src/skills/openai-skills.ts +4 -2
  30. package/src/speech/openai-speech-model.ts +5 -1
  31. package/src/speech-translation/openai-speech-translation-model.ts +5 -1
  32. package/src/transcription/openai-transcription-model.ts +25 -56
  33. package/src/openai-language-model-capabilities.ts +0 -101
@@ -12,6 +12,8 @@ import type {
12
12
  Experimental_SpeechTranslationModelV4 as SpeechTranslationModelV4,
13
13
  } from '@ai-sdk/provider';
14
14
  import {
15
+ type Resolvable,
16
+ resolveWith,
15
17
  loadApiKey,
16
18
  loadOptionalSetting,
17
19
  validateBaseURL,
@@ -175,8 +177,9 @@ export interface OpenAIProviderSettings {
175
177
 
176
178
  /**
177
179
  * API key for authenticating requests.
180
+ * A function is called before each request and may return a promise.
178
181
  */
179
- apiKey?: string;
182
+ apiKey?: Resolvable<string>;
180
183
 
181
184
  /**
182
185
  * OpenAI Organization.
@@ -230,18 +233,20 @@ export function createOpenAI(
230
233
  const providerName = options.name ?? 'openai';
231
234
 
232
235
  const getHeaders = () =>
233
- withUserAgentSuffix(
234
- {
235
- Authorization: `Bearer ${loadApiKey({
236
- apiKey: options.apiKey,
237
- environmentVariableName: 'OPENAI_API_KEY',
238
- description: 'OpenAI',
239
- })}`,
240
- 'OpenAI-Organization': options.organization,
241
- 'OpenAI-Project': options.project,
242
- ...options.headers,
243
- },
244
- `ai-sdk-openai/${VERSION}`,
236
+ resolveWith(options.apiKey, apiKey =>
237
+ withUserAgentSuffix(
238
+ {
239
+ Authorization: `Bearer ${loadApiKey({
240
+ apiKey,
241
+ environmentVariableName: 'OPENAI_API_KEY',
242
+ description: 'OpenAI',
243
+ })}`,
244
+ 'OpenAI-Organization': options.organization,
245
+ 'OpenAI-Project': options.project,
246
+ ...options.headers,
247
+ },
248
+ `ai-sdk-openai/${VERSION}`,
249
+ ),
245
250
  );
246
251
 
247
252
  const createChatModel = (modelId: OpenAIChatModelId) =>
@@ -5,13 +5,15 @@ import {
5
5
  type Experimental_RealtimeModelV4 as RealtimeModelV4,
6
6
  } from '@ai-sdk/provider';
7
7
  import { OpenAIRealtimeModelLive } from '../live/openai-realtime-model-live';
8
+ import {
9
+ getOpenAIRealtimeModelEntry,
10
+ type OpenAIKnownLiveModelId,
11
+ } from '../openai-model-catalog';
8
12
  import {
9
13
  OpenAIRealtimeModel,
10
14
  type OpenAIRealtimeModelConfig,
11
15
  } from './openai-realtime-model';
12
16
 
13
- const knownLiveModelIds = ['gpt-live-1'] as const;
14
-
15
17
  export type OpenAIRealtimeOptions = {
16
18
  /** Overrides model ID routing, including for early-access models. */
17
19
  api?: 'live' | 'realtime';
@@ -21,7 +23,7 @@ export interface OpenAIRealtimeFactory extends RealtimeFactoryV4 {
21
23
  (modelId: string, options: { api: 'live' }): OpenAIRealtimeModelLive;
22
24
  (modelId: string, options: { api: 'realtime' }): OpenAIRealtimeModel;
23
25
  (
24
- modelId: (typeof knownLiveModelIds)[number],
26
+ modelId: OpenAIKnownLiveModelId,
25
27
  options?: { api?: undefined },
26
28
  ): OpenAIRealtimeModelLive;
27
29
  (modelId: string, options?: OpenAIRealtimeOptions): RealtimeModelV4;
@@ -46,9 +48,7 @@ function resolveRealtimeApi(
46
48
  return api;
47
49
  }
48
50
 
49
- return knownLiveModelIds.some(knownModelId => knownModelId === modelId)
50
- ? 'live'
51
- : 'realtime';
51
+ return getOpenAIRealtimeModelEntry(modelId).requestConfig.realtime.api;
52
52
  }
53
53
 
54
54
  export function createOpenAIRealtimeFactory(
@@ -1,3 +1,8 @@
1
+ import {
2
+ type Resolvable,
3
+ resolve,
4
+ type FetchFunction,
5
+ } from '@ai-sdk/provider-utils';
1
6
  import type {
2
7
  Experimental_RealtimeModelV4 as RealtimeModelV4,
3
8
  Experimental_RealtimeModelV4ClientEvent as RealtimeModelV4ClientEvent,
@@ -6,7 +11,6 @@ import type {
6
11
  Experimental_RealtimeModelV4ServerEvent as RealtimeModelV4ServerEvent,
7
12
  Experimental_RealtimeModelV4SessionConfig as RealtimeModelV4SessionConfig,
8
13
  } from '@ai-sdk/provider';
9
- import type { FetchFunction } from '@ai-sdk/provider-utils';
10
14
  import {
11
15
  buildOpenAISessionConfig,
12
16
  parseOpenAIRealtimeServerEvent,
@@ -16,7 +20,7 @@ import {
16
20
  export type OpenAIRealtimeModelConfig = {
17
21
  provider: string;
18
22
  baseURL: string;
19
- headers: () => Record<string, string | undefined>;
23
+ headers: Resolvable<Record<string, string | undefined>>;
20
24
  fetch?: FetchFunction;
21
25
  };
22
26
 
@@ -47,7 +51,7 @@ export class OpenAIRealtimeModel implements RealtimeModelV4 {
47
51
  const response = await fetchFn(url, {
48
52
  method: 'POST',
49
53
  headers: {
50
- ...this.config.headers(),
54
+ ...(await resolve(this.config.headers)),
51
55
  'Content-Type': 'application/json',
52
56
  },
53
57
  body: JSON.stringify({
@@ -0,0 +1,45 @@
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
+ const allOpenAIReasoningEfforts: readonly OpenAIReasoningEffort[] = [
13
+ 'none',
14
+ 'minimal',
15
+ 'low',
16
+ 'medium',
17
+ 'high',
18
+ 'xhigh',
19
+ 'max',
20
+ ];
21
+
22
+ /** Resolve portable reasoning against the efforts declared by the catalog. */
23
+ export function resolveOpenAIReasoningEffort({
24
+ providerReasoningEffort,
25
+ reasoning,
26
+ supportedReasoningEfforts,
27
+ warnings,
28
+ }: {
29
+ providerReasoningEffort: string | null | undefined;
30
+ reasoning: LanguageModelV4CallOptions['reasoning'];
31
+ supportedReasoningEfforts: readonly string[] | undefined;
32
+ warnings: SharedV4Warning[];
33
+ }): string | undefined {
34
+ return mapReasoningToProviderEffort({
35
+ reasoning,
36
+ providerOptionsEffort: providerReasoningEffort,
37
+ effortMap: Object.fromEntries(
38
+ (supportedReasoningEfforts ?? allOpenAIReasoningEfforts).map(effort => [
39
+ effort,
40
+ effort,
41
+ ]),
42
+ ),
43
+ warnings,
44
+ });
45
+ }
@@ -17,12 +17,12 @@ import {
17
17
  type SharedV4Warning,
18
18
  } from '@ai-sdk/provider';
19
19
  import {
20
+ resolve,
20
21
  combineHeaders,
21
22
  createEventSourceResponseHandler,
22
23
  createJsonResponseHandler,
23
24
  createToolNameMapping,
24
25
  generateId,
25
- isCustomReasoning,
26
26
  parseProviderOptions,
27
27
  postJsonToApi,
28
28
  serializeModelOptions,
@@ -37,11 +37,12 @@ import {
37
37
  type OpenAIConfig,
38
38
  } from '../openai-config';
39
39
  import { openaiFailedResponseHandler } from '../openai-error';
40
- import { getOpenAILanguageModelCapabilities } from '../openai-language-model-capabilities';
40
+ import { getOpenAILanguageModelEntry } from '../openai-model-catalog';
41
41
  import {
42
42
  createOpenAIProviderStreamError,
43
43
  throwIfOpenAIStreamErrorBeforeOutput,
44
44
  } from '../openai-stream-error';
45
+ import { resolveOpenAIReasoningEffort } from '../resolve-openai-reasoning-effort';
45
46
  import type { applyPatchInputSchema } from '../tool/apply-patch';
46
47
  import type {
47
48
  codeInterpreterInputSchema,
@@ -224,10 +225,10 @@ function getConfigurationUpdateUnsupportedReason({
224
225
  modelCapabilities,
225
226
  options,
226
227
  }: {
227
- modelCapabilities: { supportsConfigurationUpdate: boolean };
228
+ modelCapabilities: { configurationUpdates: boolean };
228
229
  options: OpenAILanguageModelResponsesOptions | undefined;
229
230
  }): string | undefined {
230
- if (!modelCapabilities.supportsConfigurationUpdate) {
231
+ if (!modelCapabilities.configurationUpdates) {
231
232
  return 'reasoningEffortUpdate is only supported by GPT-6 and later models';
232
233
  }
233
234
 
@@ -302,7 +303,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
302
303
  options: LanguageModelV4CallOptions;
303
304
  }) {
304
305
  const warnings: SharedV4Warning[] = [];
305
- const modelCapabilities = getOpenAILanguageModelCapabilities(modelId);
306
+ const {
307
+ capabilities: { language: modelCapabilities },
308
+ requestConfig: { language: modelRequestConfig },
309
+ } = getOpenAILanguageModelEntry(modelId);
306
310
 
307
311
  if (topK != null) {
308
312
  warnings.push({ type: 'unsupported', feature: 'topK' });
@@ -341,24 +345,12 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
341
345
  });
342
346
  }
343
347
 
344
- let resolvedReasoningEffort =
345
- openaiOptions?.reasoningEffort ??
346
- (isCustomReasoning(reasoning) ? reasoning : undefined);
347
-
348
- if (
349
- resolvedReasoningEffort != null &&
350
- modelCapabilities.supportedReasoningEfforts != null &&
351
- !modelCapabilities.supportedReasoningEfforts.includes(
352
- resolvedReasoningEffort,
353
- )
354
- ) {
355
- warnings.push({
356
- type: 'unsupported',
357
- feature: 'reasoningEffort',
358
- details: `${modelId} only supports the following reasoning efforts: ${modelCapabilities.supportedReasoningEfforts.join(', ')}`,
359
- });
360
- resolvedReasoningEffort = undefined;
361
- }
348
+ const resolvedReasoningEffort = resolveOpenAIReasoningEffort({
349
+ providerReasoningEffort: openaiOptions?.reasoningEffort,
350
+ reasoning,
351
+ supportedReasoningEfforts: modelCapabilities.reasoning.efforts,
352
+ warnings,
353
+ });
362
354
 
363
355
  const resolvedReasoningSummary =
364
356
  openaiOptions?.reasoningSummary !== undefined
@@ -368,7 +360,8 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
368
360
  : undefined;
369
361
 
370
362
  const isReasoningModel =
371
- openaiOptions?.forceReasoning ?? modelCapabilities.isReasoningModel;
363
+ openaiOptions?.forceReasoning ??
364
+ modelCapabilities.reasoning.support === true;
372
365
 
373
366
  if (openaiOptions?.conversation && openaiOptions?.previousResponseId) {
374
367
  warnings.push({
@@ -412,7 +405,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
412
405
  toolNameMapping,
413
406
  customProviderToolNames,
414
407
  outputSchemaToolNames,
415
- supportsAsyncToolCalling: modelCapabilities.supportsAsyncToolCalling,
408
+ supportsAsyncToolCalling: modelCapabilities.tools.async,
416
409
  providerOptionsName,
417
410
  });
418
411
 
@@ -430,7 +423,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
430
423
  openaiOptions?.systemMessageMode ??
431
424
  (isReasoningModel
432
425
  ? 'developer'
433
- : modelCapabilities.systemMessageMode),
426
+ : modelRequestConfig.systemMessageMode),
434
427
  providerOptionsName,
435
428
  configurationUpdateUnsupportedReason,
436
429
  explicitMessageItemType: config.explicitMessageItemType,
@@ -458,10 +451,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
458
451
 
459
452
  // The schema accepts update efforts supported by any supported model. Check
460
453
  // whether this specific model supports the requested effort.
454
+ const supportedReasoningEfforts = modelCapabilities.reasoning.efforts;
461
455
  const getUpdateEffortUnsupportedReason = (effort: string | undefined) =>
462
- effort != null &&
463
- modelCapabilities.supportedReasoningEfforts?.includes(effort) === false
464
- ? `${modelId} only supports the following reasoning efforts: ${modelCapabilities.supportedReasoningEfforts.join(', ')}`
456
+ effort != null && supportedReasoningEfforts?.includes(effort) === false
457
+ ? `${modelId} only supports the following reasoning efforts: ${supportedReasoningEfforts.join(', ')}`
465
458
  : undefined;
466
459
 
467
460
  // Reject configuration updates whose effort value is unsupported by the selected model.
@@ -675,7 +668,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
675
668
  };
676
669
 
677
670
  if (
678
- modelCapabilities.supportsConfigurationUpdate &&
671
+ !modelCapabilities.promptCache.retention &&
679
672
  baseArgs.prompt_cache_retention != null
680
673
  ) {
681
674
  baseArgs.prompt_cache_retention = undefined;
@@ -703,7 +696,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
703
696
  if (
704
697
  !(
705
698
  effectiveReasoningEffort === 'none' &&
706
- modelCapabilities.supportsNonReasoningParameters
699
+ modelCapabilities.reasoning.sampling === 'reasoning-effort-none'
707
700
  )
708
701
  ) {
709
702
  if (baseArgs.temperature != null) {
@@ -725,7 +718,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
725
718
  }
726
719
 
727
720
  if (
728
- modelCapabilities.supportedReasoningEfforts != null &&
721
+ modelCapabilities.endpoints.responses.logprobsRequireNonReasoning &&
729
722
  (baseArgs.top_logprobs != null ||
730
723
  baseArgs.include?.includes('message.output_text.logprobs'))
731
724
  ) {
@@ -781,7 +774,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
781
774
  // Validate flex processing support
782
775
  if (
783
776
  openaiOptions?.serviceTier === 'flex' &&
784
- !modelCapabilities.supportsFlexProcessing
777
+ !modelCapabilities.serviceTiers.flex
785
778
  ) {
786
779
  warnings.push({
787
780
  type: 'unsupported',
@@ -797,7 +790,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
797
790
  if (
798
791
  (openaiOptions?.serviceTier === 'priority' ||
799
792
  openaiOptions?.serviceTier === 'fast') &&
800
- !modelCapabilities.supportsPriorityProcessing
793
+ !modelCapabilities.serviceTiers.priority
801
794
  ) {
802
795
  warnings.push({
803
796
  type: 'unsupported',
@@ -867,7 +860,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
867
860
  rawValue: rawResponse,
868
861
  } = await postJsonToApi({
869
862
  url,
870
- headers: combineHeaders(this.config.headers?.(), options.headers),
863
+ headers: combineHeaders(
864
+ await resolve(this.config.headers),
865
+ options.headers,
866
+ ),
871
867
  body,
872
868
  failedResponseHandler: openaiFailedResponseHandler,
873
869
  successfulResponseHandler: createJsonResponseHandler(
@@ -1570,7 +1566,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1570
1566
 
1571
1567
  const { responseHeaders, value: response } = await postJsonToApi({
1572
1568
  url,
1573
- headers: combineHeaders(this.config.headers?.(), options.headers),
1569
+ headers: combineHeaders(
1570
+ await resolve(this.config.headers),
1571
+ options.headers,
1572
+ ),
1574
1573
  body: {
1575
1574
  ...body,
1576
1575
  stream: true,
@@ -1,5 +1,7 @@
1
1
  import type { SkillsV4, SharedV4Warning } from '@ai-sdk/provider';
2
2
  import {
3
+ type Resolvable,
4
+ resolve,
3
5
  combineHeaders,
4
6
  convertInlineFileDataToUint8Array,
5
7
  createJsonResponseHandler,
@@ -12,7 +14,7 @@ import { openaiSkillResponseSchema } from './openai-skills-api';
12
14
  interface OpenAISkillsConfig {
13
15
  provider: string;
14
16
  url: (options: { path: string }) => string;
15
- headers: () => Record<string, string | undefined>;
17
+ headers: Resolvable<Record<string, string | undefined>>;
16
18
  fetch?: FetchFunction;
17
19
  }
18
20
 
@@ -50,7 +52,7 @@ export class OpenAISkills implements SkillsV4 {
50
52
 
51
53
  const { value: response } = await postFormDataToApi({
52
54
  url: this.config.url({ path: '/skills' }),
53
- headers: combineHeaders(this.config.headers()),
55
+ headers: combineHeaders(await resolve(this.config.headers)),
54
56
  formData,
55
57
  failedResponseHandler: openaiFailedResponseHandler,
56
58
  successfulResponseHandler: createJsonResponseHandler(
@@ -1,5 +1,6 @@
1
1
  import type { SpeechModelV4, SharedV4Warning } from '@ai-sdk/provider';
2
2
  import {
3
+ resolve,
3
4
  combineHeaders,
4
5
  createBinaryResponseHandler,
5
6
  parseProviderOptions,
@@ -131,7 +132,10 @@ export class OpenAISpeechModel implements SpeechModelV4 {
131
132
  path: '/audio/speech',
132
133
  modelId: this.modelId,
133
134
  }),
134
- headers: combineHeaders(this.config.headers?.(), options.headers),
135
+ headers: combineHeaders(
136
+ await resolve(this.config.headers),
137
+ options.headers,
138
+ ),
135
139
  body: requestBody,
136
140
  failedResponseHandler: openaiFailedResponseHandler,
137
141
  successfulResponseHandler: createBinaryResponseHandler(),
@@ -6,6 +6,7 @@ import {
6
6
  type SharedV4Warning,
7
7
  } from '@ai-sdk/provider';
8
8
  import {
9
+ resolve,
9
10
  combineHeaders,
10
11
  connectToWebSocket,
11
12
  convertToBase64,
@@ -101,7 +102,10 @@ export class OpenAISpeechTranslationModel implements SpeechTranslationModelV4 {
101
102
  });
102
103
  }
103
104
 
104
- const headers = combineHeaders(this.config.headers?.(), options.headers);
105
+ const headers = combineHeaders(
106
+ await resolve(this.config.headers),
107
+ options.headers,
108
+ );
105
109
  const sessionUpdate = buildOpenAIRealtimeSpeechTranslationSession({
106
110
  targetLanguage: options.targetLanguage,
107
111
  });
@@ -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,8 +201,7 @@ 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,
@@ -296,7 +253,10 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
296
253
  async doGenerate(
297
254
  options: OpenAITranscriptionCallOptions,
298
255
  ): Promise<Awaited<ReturnType<TranscriptionModelV4['doGenerate']>>> {
299
- if (isRealtimeTranscriptionModelId(this.modelId)) {
256
+ if (
257
+ getOpenAITranscriptionModelEntry(this.modelId).capabilities.transcription
258
+ .transcription === false
259
+ ) {
300
260
  throw new UnsupportedFunctionalityError({
301
261
  functionality: `non-streaming transcription with ${this.modelId}`,
302
262
  });
@@ -314,7 +274,10 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
314
274
  path: '/audio/transcriptions',
315
275
  modelId: this.modelId,
316
276
  }),
317
- headers: combineHeaders(this.config.headers?.(), options.headers),
277
+ headers: combineHeaders(
278
+ await resolve(this.config.headers),
279
+ options.headers,
280
+ ),
318
281
  formData,
319
282
  failedResponseHandler: openaiFailedResponseHandler,
320
283
  successfulResponseHandler: createJsonResponseHandler(
@@ -401,7 +364,8 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
401
364
  ): Promise<
402
365
  Awaited<ReturnType<NonNullable<TranscriptionModelV4['doStream']>>>
403
366
  > {
404
- if (!supportsStreamingTranscription(this.modelId)) {
367
+ const entry = getOpenAITranscriptionModelEntry(this.modelId);
368
+ if (entry.capabilities.transcription.streaming !== true) {
405
369
  throw new UnsupportedFunctionalityError({
406
370
  functionality: `streaming transcription with ${this.modelId}`,
407
371
  });
@@ -434,7 +398,8 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
434
398
  }
435
399
 
436
400
  // gpt-transcribe transcribes each committed turn, so it has no delay setting:
437
- const supportsDelay = !isModelOrSnapshot(this.modelId, 'gpt-transcribe');
401
+ const supportsDelay =
402
+ entry.capabilities.transcription.streamingDelay === true;
438
403
  if (!supportsDelay && openAIOptions?.streaming?.delay != null) {
439
404
  warnings.push({
440
405
  type: 'unsupported',
@@ -443,7 +408,10 @@ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
443
408
  });
444
409
  }
445
410
 
446
- const headers = combineHeaders(this.config.headers?.(), options.headers);
411
+ const headers = combineHeaders(
412
+ await resolve(this.config.headers),
413
+ options.headers,
414
+ );
447
415
  const sessionUpdate = buildOpenAIRealtimeTranscriptionSession({
448
416
  modelId: this.modelId,
449
417
  inputAudioFormat: options.inputAudioFormat,
@@ -659,7 +627,8 @@ function buildOpenAIRealtimeTranscriptionSession({
659
627
  transcription: {
660
628
  model: modelId,
661
629
  ...(providerOptions?.language != null
662
- ? usesLanguagesList(modelId)
630
+ ? getOpenAITranscriptionModelEntry(modelId).requestConfig
631
+ .transcription.languageField === 'languages'
663
632
  ? { languages: [providerOptions.language] }
664
633
  : { language: providerOptions.language }
665
634
  : {}),
@@ -1,101 +0,0 @@
1
- export type OpenAILanguageModelCapabilities = {
2
- isReasoningModel: boolean;
3
- systemMessageMode: 'remove' | 'system' | 'developer';
4
- supportsFlexProcessing: boolean;
5
- supportsPriorityProcessing: boolean;
6
- supportsConfigurationUpdate: boolean;
7
- supportsAsyncToolCalling: boolean;
8
- supportedReasoningEfforts: readonly string[] | undefined;
9
-
10
- /**
11
- * Allow temperature, topP, logProbs when reasoningEffort is none.
12
- */
13
- supportsNonReasoningParameters: boolean;
14
- };
15
-
16
- export function getOpenAILanguageModelCapabilities(
17
- modelId: string,
18
- ): OpenAILanguageModelCapabilities {
19
- const oSeriesVersion = getOSeriesVersion(modelId);
20
- const gptVersion = getGptVersion(modelId);
21
- const isGptChatModel =
22
- gptVersion?.minor == null &&
23
- (gptVersion?.variant?.startsWith('chat') ?? false);
24
- const isGptNanoModel = gptVersion?.variant?.startsWith('nano') ?? false;
25
- const isGpt6OrLaterModel = gptVersion != null && gptVersion.major >= 6;
26
-
27
- const isGpt6OrLaterModelWithNoneEffort =
28
- modelId === 'gpt-6-sol' ||
29
- modelId === 'gpt-6-luna' ||
30
- modelId === 'gpt-6.1-terra';
31
-
32
- const supportsFlexProcessing =
33
- (oSeriesVersion != null && oSeriesVersion >= 3) ||
34
- (gptVersion != null && gptVersion.major >= 5 && !isGptChatModel);
35
-
36
- const supportsPriorityProcessing =
37
- modelId.startsWith('gpt-4') ||
38
- (gptVersion != null &&
39
- gptVersion.major >= 5 &&
40
- !isGptNanoModel &&
41
- !isGptChatModel) ||
42
- (oSeriesVersion != null && oSeriesVersion >= 3);
43
-
44
- // Only recognizable OpenAI model families should use the developer role.
45
- // Fine-tuned, third-party, and custom model IDs keep conservative defaults.
46
- const isReasoningModel =
47
- oSeriesVersion != null ||
48
- (gptVersion != null && gptVersion.major >= 5 && !isGptChatModel);
49
-
50
- // https://platform.openai.com/docs/guides/latest-model#gpt-5-1-parameter-compatibility
51
- // GPT-5.1 through GPT-5.x, GPT-6 Sol/Luna, and GPT-6.1 Terra support
52
- // temperature, topP, and logProbs when reasoningEffort is none.
53
- const supportsNonReasoningParameters =
54
- isGpt6OrLaterModelWithNoneEffort ||
55
- (!isGpt6OrLaterModel &&
56
- gptVersion != null &&
57
- (gptVersion.major > 5 ||
58
- (gptVersion.major === 5 && (gptVersion.minor ?? 0) >= 1)));
59
-
60
- const systemMessageMode = isReasoningModel ? 'developer' : 'system';
61
-
62
- return {
63
- supportsFlexProcessing,
64
- supportsPriorityProcessing,
65
- supportsConfigurationUpdate: isGpt6OrLaterModel,
66
- supportsAsyncToolCalling: isGpt6OrLaterModel,
67
- supportedReasoningEfforts: isGpt6OrLaterModelWithNoneEffort
68
- ? ['none', 'low', 'medium', 'high', 'xhigh', 'max']
69
- : isGpt6OrLaterModel
70
- ? ['low', 'medium', 'high', 'xhigh', 'max']
71
- : undefined,
72
- isReasoningModel,
73
- systemMessageMode,
74
- supportsNonReasoningParameters,
75
- };
76
- }
77
-
78
- function getOSeriesVersion(modelId: string): number | undefined {
79
- const match = /^o(\d+)(?:-|$)/.exec(modelId);
80
- return match == null ? undefined : Number(match[1]);
81
- }
82
-
83
- function getGptVersion(modelId: string):
84
- | {
85
- major: number;
86
- minor: number | undefined;
87
- variant: string | undefined;
88
- }
89
- | undefined {
90
- const match = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/.exec(modelId);
91
-
92
- if (match == null) {
93
- return undefined;
94
- }
95
-
96
- return {
97
- major: Number(match[1]),
98
- minor: match[2] == null ? undefined : Number(match[2]),
99
- variant: match[3],
100
- };
101
- }