@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
@@ -99,11 +99,14 @@ You can use the following optional settings to customize the OpenAI provider ins
99
99
  chat models with `openai.chat('model-id')` instead, or use the
100
100
  [OpenAI-compatible provider](/providers/openai-compatible-providers).
101
101
 
102
- - **apiKey** _string_
102
+ - **apiKey** _string | PromiseLike<string> | (() => string | PromiseLike<string>)_
103
103
 
104
104
  API key that is being sent using the `Authorization` header.
105
105
  It defaults to the `OPENAI_API_KEY` environment variable.
106
106
 
107
+ A function is called before each request and may return a promise. Use it to
108
+ supply refreshed credentials; a promise value resolves to the same key each time.
109
+
107
110
  - **name** _string_
108
111
 
109
112
  The provider name. You can set this when using OpenAI compatible providers
@@ -3065,6 +3068,8 @@ The following optional provider options are available for OpenAI completion mode
3065
3068
 
3066
3069
  ### Model Capabilities
3067
3070
 
3071
+ {/* model-capabilities:openai-language:start */}
3072
+
3068
3073
  | Model | Image Input | Audio Input | Object Generation | Tool Usage |
3069
3074
  | --------------------- | ----------- | ----------- | ----------------- | ---------- |
3070
3075
  | `gpt-6-astra` | <Check /> | <Cross /> | <Check /> | <Check /> |
@@ -3101,6 +3106,8 @@ The following optional provider options are available for OpenAI completion mode
3101
3106
  | `gpt-4o` | <Check /> | <Cross /> | <Check /> | <Check /> |
3102
3107
  | `gpt-4o-mini` | <Check /> | <Cross /> | <Check /> | <Check /> |
3103
3108
 
3109
+ {/* model-capabilities:openai-language:end */}
3110
+
3104
3111
  <Note>
3105
3112
  The table above lists popular models. Please see the [OpenAI
3106
3113
  docs](https://platform.openai.com/docs/models) for a full list of available
@@ -3108,6 +3115,53 @@ The following optional provider options are available for OpenAI completion mode
3108
3115
  provider model ID as a string if needed.
3109
3116
  </Note>
3110
3117
 
3118
+ #### Request Capabilities
3119
+
3120
+ Reasoning and sampling support depend on the selected model and request options.
3121
+ Conditional cells describe when a parameter is supported. "Not declared" means
3122
+ the catalog does not enumerate reasoning efforts; it does not mean every effort
3123
+ is supported.
3124
+
3125
+ {/* model-capabilities:openai-language-request:start */}
3126
+
3127
+ | Model | Reasoning | Reasoning Efforts | Temperature | Top P |
3128
+ | --------------------- | --------- | ----------------------------------------------- | ------------------------------------------------ | ------------------------------------------------ |
3129
+ | `gpt-6-astra` | <Check /> | `low`, `medium`, `high`, `xhigh`, `max` | <Cross /> | <Cross /> |
3130
+ | `gpt-6.1-sol` | <Check /> | `low`, `medium`, `high`, `xhigh`, `max` | <Cross /> | <Cross /> |
3131
+ | `gpt-6.1-terra` | <Check /> | `none`, `low`, `medium`, `high`, `xhigh`, `max` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3132
+ | `gpt-6-luna` | <Check /> | `none`, `low`, `medium`, `high`, `xhigh`, `max` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3133
+ | `gpt-6-sol` | <Check /> | `none`, `low`, `medium`, `high`, `xhigh`, `max` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3134
+ | `gpt-5.6` | <Check /> | `none`, `low`, `medium`, `high`, `xhigh`, `max` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3135
+ | `gpt-5.6-luna` | <Check /> | `none`, `low`, `medium`, `high`, `xhigh`, `max` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3136
+ | `gpt-5.6-sol` | <Check /> | `none`, `low`, `medium`, `high`, `xhigh`, `max` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3137
+ | `gpt-5.6-terra` | <Check /> | `none`, `low`, `medium`, `high`, `xhigh`, `max` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3138
+ | `gpt-5.5` | <Check /> | `none`, `low`, `medium`, `high`, `xhigh` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3139
+ | `gpt-5.4-pro` | <Check /> | Not declared | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3140
+ | `gpt-5.4` | <Check /> | `none`, `low`, `medium`, `high`, `xhigh` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3141
+ | `gpt-5.4-mini` | <Check /> | `none`, `low`, `medium`, `high`, `xhigh` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3142
+ | `gpt-5.4-nano` | <Check /> | `none`, `low`, `medium`, `high`, `xhigh` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3143
+ | `gpt-5.3-chat-latest` | <Check /> | Not declared | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3144
+ | `gpt-5.2-pro` | <Check /> | Not declared | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3145
+ | `gpt-5.2-chat-latest` | <Check /> | Not declared | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3146
+ | `gpt-5.2` | <Check /> | `none`, `low`, `medium`, `high`, `xhigh` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3147
+ | `gpt-5.1-codex-mini` | <Check /> | Not declared | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3148
+ | `gpt-5.1-codex` | <Check /> | Not declared | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3149
+ | `gpt-5.1-chat-latest` | <Check /> | Not declared | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3150
+ | `gpt-5.1` | <Check /> | `none`, `low`, `medium`, `high` | Conditional: Only when reasoning effort is none. | Conditional: Only when reasoning effort is none. |
3151
+ | `gpt-5-pro` | <Check /> | Not declared | <Cross /> | <Cross /> |
3152
+ | `gpt-5` | <Check /> | `minimal`, `low`, `medium`, `high` | <Cross /> | <Cross /> |
3153
+ | `gpt-5-mini` | <Check /> | `minimal`, `low`, `medium`, `high` | <Cross /> | <Cross /> |
3154
+ | `gpt-5-nano` | <Check /> | `minimal`, `low`, `medium`, `high` | <Cross /> | <Cross /> |
3155
+ | `gpt-5-codex` | <Check /> | Not declared | <Cross /> | <Cross /> |
3156
+ | `gpt-5-chat-latest` | <Cross /> | Not declared | <Check /> | <Check /> |
3157
+ | `gpt-4.1` | <Cross /> | Not declared | <Check /> | <Check /> |
3158
+ | `gpt-4.1-mini` | <Cross /> | Not declared | <Check /> | <Check /> |
3159
+ | `gpt-4.1-nano` | <Cross /> | Not declared | <Check /> | <Check /> |
3160
+ | `gpt-4o` | <Cross /> | Not declared | <Check /> | <Check /> |
3161
+ | `gpt-4o-mini` | <Cross /> | Not declared | <Check /> | <Check /> |
3162
+
3163
+ {/* model-capabilities:openai-language-request:end */}
3164
+
3111
3165
  ## Realtime Models
3112
3166
 
3113
3167
  <Note type="warning">Realtime is an experimental feature.</Note>
@@ -3136,8 +3190,9 @@ Live supports **browser WSS sessions through an application-owned relay with cli
3136
3190
  delegation**, using `experimental_useRealtime` for the browser runtime, and optional
3137
3191
  browser-direct WebRTC. Your application owns the agent and tool execution; Live
3138
3192
  delegations do not invoke `onToolCall`. For a low-level server adapter, obtain server-only connection
3139
- settings with `liveModel.getServerWebSocketConfig()` and serialize commands with
3140
- `liveModel.serializeClientEvent()`. Create one parser per connection with
3193
+ settings with `await liveModel.getServerWebSocketConfig()` and serialize commands with
3194
+ `liveModel.serializeClientEvent()`. Connection settings resolve asynchronously when
3195
+ using an asynchronous API key supplier. Create one parser per connection with
3141
3196
  `liveModel.createServerEventParser()` and pass incoming JSON to the returned
3142
3197
  function. It returns arrays of normalized events and retains each original event
3143
3198
  in `raw`.
@@ -3227,6 +3282,17 @@ relays. Continuous conversation semantics do not imply browser support for every
3227
3282
  provider codec or transport. The [Realtime guide](/docs/ai-sdk-core/realtime) covers
3228
3283
  legacy token-based, turn-based sessions.
3229
3284
 
3285
+ ### Model Capabilities
3286
+
3287
+ {/* model-capabilities:openai-realtime:start */}
3288
+
3289
+ | Model | Conversation | Transports |
3290
+ | -------------- | ------------ | ----------------- |
3291
+ | `gpt-live-1` | continuous | websocket, webrtc |
3292
+ | `gpt-realtime` | turn-based | websocket |
3293
+
3294
+ {/* model-capabilities:openai-realtime:end */}
3295
+
3230
3296
  ## Embedding Models
3231
3297
 
3232
3298
  You can create models that call the [OpenAI embeddings API](https://platform.openai.com/docs/api-reference/embeddings)
@@ -3269,12 +3335,16 @@ The following optional provider options are available for OpenAI embedding model
3269
3335
 
3270
3336
  ### Model Capabilities
3271
3337
 
3338
+ {/* model-capabilities:openai-embedding:start */}
3339
+
3272
3340
  | Model | Default Dimensions | Custom Dimensions |
3273
3341
  | ------------------------ | ------------------ | ----------------- |
3274
3342
  | `text-embedding-3-large` | 3072 | <Check /> |
3275
3343
  | `text-embedding-3-small` | 1536 | <Check /> |
3276
3344
  | `text-embedding-ada-002` | 1536 | <Cross /> |
3277
3345
 
3346
+ {/* model-capabilities:openai-embedding:end */}
3347
+
3278
3348
  ## Image Models
3279
3349
 
3280
3350
  You can create models that call the [OpenAI image generation API](https://platform.openai.com/docs/api-reference/images)
@@ -3379,6 +3449,8 @@ const { images } = await generateImage({
3379
3449
 
3380
3450
  ### Model Capabilities
3381
3451
 
3452
+ {/* model-capabilities:openai-image:start */}
3453
+
3382
3454
  | Model | Sizes |
3383
3455
  | ------------------------ | -------------------------------------- |
3384
3456
  | `gpt-image-2.5-flare` | Standard presets and custom dimensions |
@@ -3390,6 +3462,8 @@ const { images } = await generateImage({
3390
3462
  | `dall-e-3` | 1024x1024, 1792x1024, 1024x1792 |
3391
3463
  | `dall-e-2` | 256x256, 512x512, 1024x1024 |
3392
3464
 
3465
+ {/* model-capabilities:openai-image:end */}
3466
+
3393
3467
  Use `gpt-image-2.5-flare` for fast, general-purpose image generation and
3394
3468
  `gpt-image-2.5-sunburst` when editing precision and instruction following are
3395
3469
  the priority. Both models accept custom `WIDTHxHEIGHT` sizes whose dimensions
@@ -3469,6 +3543,32 @@ const result = await transcribe({
3469
3543
  });
3470
3544
  ```
3471
3545
 
3546
+ For `gpt-transcribe`, use `languages` for expected languages and `keywords` for
3547
+ names, acronyms, and domain vocabulary. These hints are sent as repeated
3548
+ `languages[]` and `keywords[]` multipart fields:
3549
+
3550
+ ```ts
3551
+ import { transcribe } from 'ai';
3552
+ import { openai, type OpenAITranscriptionModelOptions } from '@ai-sdk/openai';
3553
+
3554
+ const result = await transcribe({
3555
+ model: openai.transcription('gpt-transcribe'),
3556
+ audio: new Uint8Array([1, 2, 3, 4]),
3557
+ providerOptions: {
3558
+ openai: {
3559
+ languages: ['en', 'fr'],
3560
+ keywords: ['premium plan', 'AC-42'],
3561
+ prompt: 'A customer support call.',
3562
+ } satisfies OpenAITranscriptionModelOptions,
3563
+ },
3564
+ });
3565
+ ```
3566
+
3567
+ Use `languages` instead of `language` with `gpt-transcribe`; do not send both.
3568
+ The API validates model support and language codes. These two options currently
3569
+ apply to REST transcription; the provider reports unsupported-option warnings
3570
+ if they are supplied to `experimental_streamTranscribe`.
3571
+
3472
3572
  To get word-level timestamps, specify the granularity:
3473
3573
 
3474
3574
  ```ts highlight="8-9"
@@ -3518,6 +3618,16 @@ The following provider options are available:
3518
3618
  For streaming with `gpt-transcribe` and `gpt-live-transcribe`, it is sent as a single-entry `languages` list.
3519
3619
  Optional.
3520
3620
 
3621
+ - **languages** _string[]_
3622
+ Expected languages for REST transcription, such as `['en', 'fr']` for `gpt-transcribe`.
3623
+ Use this instead of `language`; do not send both. The API validates model support.
3624
+ Optional.
3625
+
3626
+ - **keywords** _string[]_
3627
+ Vocabulary hints for REST transcription, such as product names, acronyms, or technical terms.
3628
+ Supported by `gpt-transcribe`; the API validates support for other models.
3629
+ Optional.
3630
+
3521
3631
  - **prompt** _string_
3522
3632
  An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.
3523
3633
  Optional.
@@ -3547,6 +3657,8 @@ The following provider options are available:
3547
3657
 
3548
3658
  ### Model Capabilities
3549
3659
 
3660
+ {/* model-capabilities:openai-transcription:start */}
3661
+
3550
3662
  | Model | Transcription | Streaming | Duration | Segments | Language |
3551
3663
  | --------------------------- | ------------- | --------- | --------- | --------- | --------- |
3552
3664
  | `whisper-1` | <Check /> | <Cross /> | <Check /> | <Check /> | <Check /> |
@@ -3557,6 +3669,8 @@ The following provider options are available:
3557
3669
  | `gpt-live-transcribe` | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> |
3558
3670
  | `gpt-realtime-whisper` | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> |
3559
3671
 
3672
+ {/* model-capabilities:openai-transcription:end */}
3673
+
3560
3674
  ## Translation Models
3561
3675
 
3562
3676
  <Note type="warning">Speech translation is an experimental feature.</Note>
@@ -3597,10 +3711,14 @@ Unsupported source and output settings surface as call warnings.
3597
3711
 
3598
3712
  ### Model Capabilities
3599
3713
 
3714
+ {/* model-capabilities:openai-speech-translation:start */}
3715
+
3600
3716
  | Model | Translated Audio | Translated Text | Source Transcript |
3601
3717
  | ------------------------ | ---------------- | --------------- | ----------------- |
3602
3718
  | `gpt-realtime-translate` | <Check /> | <Check /> | <Check /> |
3603
3719
 
3720
+ {/* model-capabilities:openai-speech-translation:end */}
3721
+
3604
3722
  ## Speech Models
3605
3723
 
3606
3724
  You can create models that call the [OpenAI speech API](https://platform.openai.com/docs/api-reference/audio/speech)
@@ -3656,12 +3774,16 @@ const result = await generateSpeech({
3656
3774
 
3657
3775
  ### Model Capabilities
3658
3776
 
3777
+ {/* model-capabilities:openai-speech:start */}
3778
+
3659
3779
  | Model | Instructions |
3660
3780
  | ----------------- | ------------ |
3661
- | `tts-1` | <Check /> |
3662
- | `tts-1-hd` | <Check /> |
3781
+ | `tts-1` | <Cross /> |
3782
+ | `tts-1-hd` | <Cross /> |
3663
3783
  | `gpt-4o-mini-tts` | <Check /> |
3664
3784
 
3785
+ {/* model-capabilities:openai-speech:end */}
3786
+
3665
3787
  ## Decision Models
3666
3788
 
3667
3789
  Create an experimental decision model with `openai.decisionModel(modelId)`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai",
3
- "version": "4.0.105",
3
+ "version": "4.0.107",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -35,8 +35,8 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@ai-sdk/provider": "4.0.34",
39
- "@ai-sdk/provider-utils": "5.0.71"
38
+ "@ai-sdk/provider": "4.0.35",
39
+ "@ai-sdk/provider-utils": "5.0.73"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.4",
@@ -6,6 +6,8 @@ import {
6
6
  } from '@ai-sdk/provider';
7
7
  import type { OpenAIChatPrompt } from './openai-chat-prompt';
8
8
  import {
9
+ secureJsonParse,
10
+ stringifyWithBigIntAndCycles,
9
11
  convertToBase64,
10
12
  getTopLevelMediaType,
11
13
  resolveFullMediaType,
@@ -13,7 +15,22 @@ import {
13
15
  } from '@ai-sdk/provider-utils';
14
16
 
15
17
  function serializeToolCallArguments(input: unknown): string {
16
- return JSON.stringify(
18
+ if (typeof input === 'string') {
19
+ try {
20
+ const parsed = secureJsonParse(input);
21
+ if (
22
+ typeof parsed !== 'object' ||
23
+ parsed === null ||
24
+ Array.isArray(parsed)
25
+ ) {
26
+ return '{}';
27
+ }
28
+ return input;
29
+ } catch {
30
+ return '{}';
31
+ }
32
+ }
33
+ return stringifyWithBigIntAndCycles(
17
34
  typeof input === 'object' && input !== null && !Array.isArray(input)
18
35
  ? input
19
36
  : {},
@@ -326,14 +343,16 @@ export function convertToOpenAIChatMessages({
326
343
  break;
327
344
  case 'error-text':
328
345
  case 'error-json':
329
- contentValue = JSON.stringify({ error: output.value });
346
+ contentValue = stringifyWithBigIntAndCycles({
347
+ error: output.value,
348
+ });
330
349
  break;
331
350
  case 'execution-denied':
332
351
  contentValue = output.reason ?? 'Tool call execution denied.';
333
352
  break;
334
353
  case 'content':
335
354
  case 'json':
336
- contentValue = JSON.stringify(output.value);
355
+ contentValue = stringifyWithBigIntAndCycles(output.value);
337
356
  break;
338
357
  }
339
358
 
@@ -11,6 +11,8 @@ import {
11
11
  type SharedV4Warning,
12
12
  } from '@ai-sdk/provider';
13
13
  import {
14
+ type Resolvable,
15
+ resolve,
14
16
  StreamingToolCallTracker,
15
17
  combineHeaders,
16
18
  createEventSourceResponseHandler,
@@ -26,14 +28,12 @@ import {
26
28
  type ParseResult,
27
29
  } from '@ai-sdk/provider-utils';
28
30
  import { openaiFailedResponseHandler } from '../openai-error';
29
- import {
30
- getOpenAILanguageModelCapabilities,
31
- resolveOpenAIReasoningEffort,
32
- } from '../openai-language-model-capabilities';
31
+ import { getOpenAILanguageModelEntry } from '../openai-model-catalog';
33
32
  import {
34
33
  createOpenAIProviderStreamError,
35
34
  throwIfOpenAIStreamErrorBeforeOutput,
36
35
  } from '../openai-stream-error';
36
+ import { resolveOpenAIReasoningEffort } from '../resolve-openai-reasoning-effort';
37
37
  import {
38
38
  convertOpenAIChatUsage,
39
39
  type OpenAIChatUsage,
@@ -57,7 +57,7 @@ type OpenAIChatConfig = {
57
57
  provider: string;
58
58
  /** Model ID sent to the API. Defaults to the model's reported ID. */
59
59
  requestModelId?: string;
60
- headers?: () => Record<string, string | undefined>;
60
+ headers?: Resolvable<Record<string, string | undefined>>;
61
61
  url: (options: { modelId: string; path: string }) => string;
62
62
  fetch?: FetchFunction;
63
63
  };
@@ -122,12 +122,15 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
122
122
  schema: openaiLanguageModelChatOptions,
123
123
  })) ?? {};
124
124
 
125
- const modelCapabilities = getOpenAILanguageModelCapabilities(this.modelId);
125
+ const {
126
+ capabilities: { language: modelCapabilities },
127
+ requestConfig: { language: modelRequestConfig },
128
+ } = getOpenAILanguageModelEntry(this.modelId);
126
129
 
127
130
  // Some models reject function tools unless reasoning is disabled, even
128
131
  // when no reasoning effort is sent, so reasoning is set to 'none'.
129
132
  const requiresNoReasoningEffort =
130
- !modelCapabilities.supportsChatFunctionToolsWithReasoning &&
133
+ !modelCapabilities.endpoints.chat.functionToolsWithReasoning &&
131
134
  openaiOptions.reasoningEffort == null &&
132
135
  (tools?.some(tool => tool.type === 'function') ?? false);
133
136
 
@@ -150,14 +153,15 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
150
153
  reasoning,
151
154
  // Only the Responses API supports `max`.
152
155
  supportedReasoningEfforts:
153
- modelCapabilities.supportedReasoningEfforts?.filter(
156
+ modelCapabilities.reasoning.efforts?.filter(
154
157
  effort => effort !== 'max',
155
158
  ),
156
159
  warnings,
157
160
  });
158
161
 
159
162
  const isReasoningModel =
160
- openaiOptions.forceReasoning ?? modelCapabilities.isReasoningModel;
163
+ openaiOptions.forceReasoning ??
164
+ modelCapabilities.reasoning.support === true;
161
165
 
162
166
  if (topK != null) {
163
167
  warnings.push({ type: 'unsupported', feature: 'topK' });
@@ -181,7 +185,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
181
185
  openaiOptions.systemMessageMode ??
182
186
  (isReasoningModel
183
187
  ? 'developer'
184
- : modelCapabilities.systemMessageMode),
188
+ : modelRequestConfig.systemMessageMode),
185
189
  },
186
190
  );
187
191
 
@@ -261,7 +265,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
261
265
  };
262
266
 
263
267
  if (
264
- modelCapabilities.supportsConfigurationUpdate &&
268
+ !modelCapabilities.promptCache.retention &&
265
269
  baseArgs.prompt_cache_retention != null
266
270
  ) {
267
271
  baseArgs.prompt_cache_retention = undefined;
@@ -280,7 +284,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
280
284
  // https://platform.openai.com/docs/guides/latest-model#gpt-5-1-parameter-compatibility
281
285
  if (
282
286
  resolvedReasoningEffort !== 'none' ||
283
- !modelCapabilities.supportsNonReasoningParameters
287
+ modelCapabilities.reasoning.sampling !== 'reasoning-effort-none'
284
288
  ) {
285
289
  if (baseArgs.temperature != null) {
286
290
  baseArgs.temperature = undefined;
@@ -346,10 +350,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
346
350
  }
347
351
  baseArgs.max_tokens = undefined;
348
352
  }
349
- } else if (
350
- this.modelId.startsWith('gpt-4o-search-preview') ||
351
- this.modelId.startsWith('gpt-4o-mini-search-preview')
352
- ) {
353
+ } else if (!modelCapabilities.endpoints.chat.temperatureWithoutReasoning) {
353
354
  if (baseArgs.temperature != null) {
354
355
  baseArgs.temperature = undefined;
355
356
  warnings.push({
@@ -364,7 +365,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
364
365
  // Validate flex processing support
365
366
  if (
366
367
  openaiOptions.serviceTier === 'flex' &&
367
- !modelCapabilities.supportsFlexProcessing
368
+ !modelCapabilities.serviceTiers.flex
368
369
  ) {
369
370
  warnings.push({
370
371
  type: 'unsupported',
@@ -379,7 +380,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
379
380
  if (
380
381
  (openaiOptions.serviceTier === 'priority' ||
381
382
  openaiOptions.serviceTier === 'fast') &&
382
- !modelCapabilities.supportsPriorityProcessing
383
+ !modelCapabilities.serviceTiers.priority
383
384
  ) {
384
385
  warnings.push({
385
386
  type: 'unsupported',
@@ -423,7 +424,10 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
423
424
  path: '/chat/completions',
424
425
  modelId: this.config.requestModelId ?? this.modelId,
425
426
  }),
426
- headers: combineHeaders(this.config.headers?.(), options.headers),
427
+ headers: combineHeaders(
428
+ await resolve(this.config.headers),
429
+ options.headers,
430
+ ),
427
431
  body,
428
432
  failedResponseHandler: openaiFailedResponseHandler,
429
433
  successfulResponseHandler: createJsonResponseHandler(
@@ -526,7 +530,10 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
526
530
 
527
531
  const { responseHeaders, value: response } = await postJsonToApi({
528
532
  url,
529
- headers: combineHeaders(this.config.headers?.(), options.headers),
533
+ headers: combineHeaders(
534
+ await resolve(this.config.headers),
535
+ options.headers,
536
+ ),
530
537
  body,
531
538
  failedResponseHandler: openaiFailedResponseHandler,
532
539
  successfulResponseHandler: createEventSourceResponseHandler(
@@ -9,6 +9,8 @@ import type {
9
9
  SharedV4Warning,
10
10
  } from '@ai-sdk/provider';
11
11
  import {
12
+ type Resolvable,
13
+ resolve,
12
14
  combineHeaders,
13
15
  createEventSourceResponseHandler,
14
16
  createJsonResponseHandler,
@@ -45,7 +47,7 @@ type OpenAICompletionConfig = {
45
47
  provider: string;
46
48
  /** Model ID sent to the API. Defaults to the model's reported ID. */
47
49
  requestModelId?: string;
48
- headers?: () => Record<string, string | undefined>;
50
+ headers?: Resolvable<Record<string, string | undefined>>;
49
51
  url: (options: { modelId: string; path: string }) => string;
50
52
  fetch?: FetchFunction;
51
53
  };
@@ -196,7 +198,10 @@ export class OpenAICompletionLanguageModel implements LanguageModelV4 {
196
198
  path: '/completions',
197
199
  modelId: this.config.requestModelId ?? this.modelId,
198
200
  }),
199
- headers: combineHeaders(this.config.headers?.(), options.headers),
201
+ headers: combineHeaders(
202
+ await resolve(this.config.headers),
203
+ options.headers,
204
+ ),
200
205
  body: args,
201
206
  failedResponseHandler: openaiFailedResponseHandler,
202
207
  successfulResponseHandler: createJsonResponseHandler(
@@ -253,7 +258,10 @@ export class OpenAICompletionLanguageModel implements LanguageModelV4 {
253
258
 
254
259
  const { responseHeaders, value: response } = await postJsonToApi({
255
260
  url,
256
- headers: combineHeaders(this.config.headers?.(), options.headers),
261
+ headers: combineHeaders(
262
+ await resolve(this.config.headers),
263
+ options.headers,
264
+ ),
257
265
  body,
258
266
  failedResponseHandler: openaiFailedResponseHandler,
259
267
  successfulResponseHandler: createEventSourceResponseHandler(
@@ -3,6 +3,7 @@ import {
3
3
  type EmbeddingModelV4,
4
4
  } from '@ai-sdk/provider';
5
5
  import {
6
+ resolve,
6
7
  combineHeaders,
7
8
  createJsonResponseHandler,
8
9
  EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL,
@@ -87,7 +88,7 @@ export class OpenAIEmbeddingModel implements EmbeddingModelV4 {
87
88
  path: '/embeddings',
88
89
  modelId: this.modelId,
89
90
  }),
90
- headers: combineHeaders(this.config.headers?.(), headers),
91
+ headers: combineHeaders(await resolve(this.config.headers), headers),
91
92
  body: {
92
93
  model: this.modelId,
93
94
  input: values,
@@ -12,6 +12,8 @@ import {
12
12
  type SharedV4ProviderReference,
13
13
  } from '@ai-sdk/provider';
14
14
  import {
15
+ type Resolvable,
16
+ resolve,
15
17
  combineHeaders,
16
18
  convertInlineFileDataToUint8Array,
17
19
  createBinaryStreamResponseHandler,
@@ -39,7 +41,7 @@ import {
39
41
  interface OpenAIFilesConfig {
40
42
  provider: string;
41
43
  baseURL: string;
42
- headers: () => Record<string, string | undefined>;
44
+ headers: Resolvable<Record<string, string | undefined>>;
43
45
  fetch?: FetchFunction;
44
46
  }
45
47
 
@@ -65,10 +67,10 @@ export class OpenAIFiles implements FilesV4 {
65
67
  return fileId;
66
68
  }
67
69
 
68
- private getHeaders(
70
+ private async getHeaders(
69
71
  headers: Record<string, string | undefined> | undefined,
70
- ): Record<string, string | undefined> {
71
- return combineHeaders(this.config.headers(), headers);
72
+ ): Promise<Record<string, string | undefined>> {
73
+ return combineHeaders(await resolve(this.config.headers), headers);
72
74
  }
73
75
 
74
76
  async uploadFile({
@@ -95,7 +97,7 @@ export class OpenAIFiles implements FilesV4 {
95
97
  }
96
98
 
97
99
  const purpose = openaiOptions?.purpose ?? 'assistants';
98
- const requestHeaders = this.getHeaders(headers);
100
+ const requestHeaders = await this.getHeaders(headers);
99
101
  const url = `${this.config.baseURL}/files`;
100
102
 
101
103
  let response: OpenAIFilesResponse;
@@ -200,7 +202,7 @@ export class OpenAIFiles implements FilesV4 {
200
202
 
201
203
  const { value: response } = await getFromApi({
202
204
  url: `${this.config.baseURL}/files/${encodePathSegment(fileId)}`,
203
- headers: this.getHeaders(headers),
205
+ headers: await this.getHeaders(headers),
204
206
  failedResponseHandler: openaiFailedResponseHandler,
205
207
  successfulResponseHandler: createJsonResponseHandler(
206
208
  openaiFilesResponseSchema,
@@ -236,7 +238,7 @@ export class OpenAIFiles implements FilesV4 {
236
238
 
237
239
  const { value: content, responseHeaders } = await getFromApi({
238
240
  url: `${this.config.baseURL}/files/${encodePathSegment(fileId)}/content`,
239
- headers: this.getHeaders(headers),
241
+ headers: await this.getHeaders(headers),
240
242
  failedResponseHandler: openaiFailedResponseHandler,
241
243
  successfulResponseHandler: createBinaryStreamResponseHandler(),
242
244
  abortSignal,
@@ -263,7 +265,7 @@ export class OpenAIFiles implements FilesV4 {
263
265
 
264
266
  const { value: response } = await deleteFromApi({
265
267
  url: `${this.config.baseURL}/files/${encodePathSegment(fileId)}`,
266
- headers: this.getHeaders(headers),
268
+ headers: await this.getHeaders(headers),
267
269
  failedResponseHandler: openaiFailedResponseHandler,
268
270
  successfulResponseHandler: createJsonResponseHandler(
269
271
  openaiFileDeleteResponseSchema,
@@ -4,6 +4,10 @@ import {
4
4
  type InferSchema,
5
5
  } from '@ai-sdk/provider-utils';
6
6
  import { z } from 'zod/v4';
7
+ import {
8
+ getOpenAIImageModelEntry,
9
+ openaiImageModelMaxImagesPerCall,
10
+ } from '../openai-model-catalog';
7
11
 
8
12
  export type OpenAIImageModelId =
9
13
  | 'dall-e-3'
@@ -19,33 +23,19 @@ export type OpenAIImageModelId =
19
23
  | 'chatgpt-image-latest'
20
24
  | (string & {});
21
25
 
22
- // https://platform.openai.com/docs/guides/images
23
- export const modelMaxImagesPerCall: Record<OpenAIImageModelId, number> = {
24
- 'dall-e-3': 1,
25
- 'dall-e-2': 10,
26
- 'gpt-image-1': 10,
27
- 'gpt-image-1-mini': 10,
28
- 'gpt-image-1.5': 10,
29
- 'gpt-image-2': 10,
30
- 'gpt-image-2.5-flare': 10,
31
- 'gpt-image-2.5-flare-2026-09-08': 10,
32
- 'gpt-image-2.5-sunburst': 10,
33
- 'gpt-image-2.5-sunburst-2026-09-08': 10,
34
- 'chatgpt-image-latest': 10,
35
- };
36
-
37
- const defaultResponseFormatPrefixes = ['chatgpt-image-', 'gpt-image-'];
26
+ export const modelMaxImagesPerCall: Record<OpenAIImageModelId, number> =
27
+ openaiImageModelMaxImagesPerCall;
38
28
 
39
29
  export function hasDefaultResponseFormat(modelId: string): boolean {
40
- return defaultResponseFormatPrefixes.some(prefix =>
41
- modelId.startsWith(prefix),
30
+ return (
31
+ getOpenAIImageModelEntry(modelId).requestConfig.image.responseFormat ===
32
+ 'provider-default'
42
33
  );
43
34
  }
44
35
 
45
36
  export function getMaxImagesPerCall(modelId: OpenAIImageModelId): number {
46
37
  return (
47
- modelMaxImagesPerCall[modelId] ??
48
- (modelId.startsWith('gpt-image-') ? 10 : 1)
38
+ getOpenAIImageModelEntry(modelId).capabilities.image.maxImagesPerCall ?? 1
49
39
  );
50
40
  }
51
41