@ai-sdk/openai 3.0.108 → 3.0.110

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.
@@ -206,9 +206,23 @@ The following provider options are available:
206
206
 
207
207
  <Note>
208
208
  Supported reasoning efforts vary by model. GPT-5.6 supports `'none'`, `'low'`,
209
- `'medium'`, `'high'`, `'xhigh'`, and `'max'`.
209
+ `'medium'`, `'high'`, `'xhigh'`, and `'max'`. GPT-6 and later models support
210
+ `'low'`, `'medium'`, `'high'`, `'xhigh'`, and `'max'`.
210
211
  </Note>
211
212
 
213
+ <Note>
214
+ GPT-6 and later models do not support `temperature`, `topP`, `logprobs`, or
215
+ the legacy `promptCacheRetention` option. The provider removes these settings
216
+ and returns a warning. Use the Responses API for GPT-6 tool calling.
217
+ </Note>
218
+
219
+ - **reasoningEffortUpdate** _'low' | 'medium' | 'high' | 'xhigh' | 'max'_
220
+ Updates the reasoning effort for GPT-6 and later models starting with the
221
+ current response without changing the request-level effort. Use this with
222
+ `previousResponseId` to preserve the original prompt prefix for caching.
223
+ Configuration updates require standard, single-agent mode and cannot be
224
+ combined with automatic truncation.
225
+
212
226
  - **reasoningMode** _'standard' | 'pro'_
213
227
  Controls how much model work GPT-5.6 performs before returning a final answer. `'standard'` is the default. Use `'pro'` for difficult tasks where quality matters more than latency and token usage.
214
228
 
@@ -308,6 +322,58 @@ The following OpenAI-specific metadata may be returned:
308
322
  - **reasoningContext** _(optional)_
309
323
  Effective persisted-reasoning context returned by GPT-5.6 (`'current_turn'` or `'all_turns'`).
310
324
 
325
+ #### Changing Reasoning Effort Mid-Conversation
326
+
327
+ GPT-6 and later models can change reasoning effort between responses without
328
+ changing the request-level `reasoningEffort` setting. The provider sends
329
+ `reasoningEffortUpdate` as an OpenAI `configuration_update` input item before
330
+ the next user message. Keeping the request-level effort unchanged preserves the
331
+ original prompt prefix for prompt caching.
332
+
333
+ ```ts highlight="13,32,34"
334
+ import {
335
+ openai,
336
+ type OpenAILanguageModelResponsesOptions,
337
+ type OpenaiResponsesProviderMetadata,
338
+ } from '@ai-sdk/openai';
339
+ import { generateText } from 'ai';
340
+
341
+ const first = await generateText({
342
+ model: openai.responses('gpt-6-astra'),
343
+ prompt: 'Draft a database migration plan.',
344
+ providerOptions: {
345
+ openai: {
346
+ reasoningEffort: 'low',
347
+ } satisfies OpenAILanguageModelResponsesOptions,
348
+ },
349
+ });
350
+
351
+ const metadata = first.providerMetadata as
352
+ | OpenaiResponsesProviderMetadata
353
+ | undefined;
354
+ const previousResponseId = metadata?.openai.responseId;
355
+
356
+ if (!previousResponseId) {
357
+ throw new Error('OpenAI did not return a response ID.');
358
+ }
359
+
360
+ const second = await generateText({
361
+ model: openai.responses('gpt-6-astra'),
362
+ prompt: 'Analyze the failure modes and propose rollback steps.',
363
+ providerOptions: {
364
+ openai: {
365
+ previousResponseId,
366
+ reasoningEffort: 'low',
367
+ reasoningEffortUpdate: 'high',
368
+ } satisfies OpenAILanguageModelResponsesOptions,
369
+ },
370
+ });
371
+ ```
372
+
373
+ The response metadata continues to report the request-level reasoning effort,
374
+ not the effective effort selected by `reasoningEffortUpdate`. Configuration
375
+ updates are not supported with `reasoningMode: 'pro'` or `truncation: 'auto'`.
376
+
311
377
  #### Reasoning Output
312
378
 
313
379
  For reasoning models like `gpt-5`, you can enable reasoning summaries to see the model's thought process. Different models support different summarizers—for example, `o4-mini` supports detailed summaries. Set `reasoningSummary: "auto"` to automatically receive the richest level available.
@@ -482,6 +548,55 @@ metadata on tool-call parts. The SDK uses `providerMetadata.openai.namespace` or
482
548
  `providerOptions.openai.namespace` to round-trip the namespace back to OpenAI on
483
549
  subsequent requests.
484
550
 
551
+ #### Async Tool Calling
552
+
553
+ GPT-6 Astra and later Responses models support
554
+ [async tool calling](https://developers.openai.com/api/docs/guides/async-tool-calling).
555
+ An async tool lets the model continue generating independent output after issuing
556
+ the call instead of waiting for its result. Your application still executes the
557
+ tool and sends its result in a later request using the original tool call ID.
558
+
559
+ Enable async calling on a function tool with `providerOptions.openai.async`:
560
+
561
+ ```ts
562
+ import { openai, type OpenAIToolOptions } from '@ai-sdk/openai';
563
+ import { generateText, tool } from 'ai';
564
+ import { z } from 'zod';
565
+
566
+ const result = await generateText({
567
+ model: openai.responses('gpt-6-astra'),
568
+ tools: {
569
+ getWeather: tool({
570
+ description: 'Get the weather for a city.',
571
+ inputSchema: z.object({ city: z.string() }),
572
+ outputSchema: z.object({
573
+ city: z.string(),
574
+ temperatureC: z.number(),
575
+ }),
576
+ providerOptions: {
577
+ openai: { async: true } satisfies OpenAIToolOptions,
578
+ },
579
+ }),
580
+ },
581
+ prompt:
582
+ 'Start the weather lookup for Paris, then list three general packing essentials without waiting.',
583
+ });
584
+ ```
585
+
586
+ The generated tool call exposes the provider marker as
587
+ `providerMetadata.openai.async`. Use `providerMetadata.openai.responseId` as the
588
+ next request's `previousResponseId`, and submit the result in a tool message with
589
+ the original `toolCallId`.
590
+
591
+ With `streamText`, OpenAI can continue streaming text after the completed
592
+ `tool-call` part. Use the tool's `onInputAvailable` callback to start work as soon
593
+ as that part arrives. If `execute` returns the same already-running promise, tool
594
+ execution overlaps the rest of the model stream. Omit `execute` when the job
595
+ should outlive the current generation and submit its result in a later request.
596
+
597
+ Async calling applies to directly called function and custom tools. It does not
598
+ apply to hosted tools.
599
+
485
600
  #### Web Search Tool
486
601
 
487
602
  The OpenAI responses API supports web search through the `openai.tools.webSearch` tool.
@@ -670,6 +785,16 @@ for await (const part of result.fullStream) {
670
785
  setting `store: false`.
671
786
  </Note>
672
787
 
788
+ To use `xhigh` or `max` quality, select `gpt-image-2.5-flare` or
789
+ `gpt-image-2.5-sunburst` as the image generation tool's model:
790
+
791
+ ```ts
792
+ openai.tools.imageGeneration({
793
+ model: 'gpt-image-2.5-flare',
794
+ quality: 'xhigh',
795
+ });
796
+ ```
797
+
673
798
  For complete details on model availability, image quality controls, supported sizes, and tool-specific parameters,
674
799
  refer to the OpenAI documentation:
675
800
 
@@ -2599,6 +2724,19 @@ const { image, providerMetadata } = await generateImage({
2599
2724
  });
2600
2725
  ```
2601
2726
 
2727
+ The `gpt-image-2.5-flare` and `gpt-image-2.5-sunburst` models also support
2728
+ `quality: 'xhigh'` and `quality: 'max'` for both image generation and editing:
2729
+
2730
+ ```ts
2731
+ const { image } = await generateImage({
2732
+ model: openai.image('gpt-image-2.5-sunburst'),
2733
+ prompt: 'A salamander at sunrise in a forest pond in the Seychelles.',
2734
+ providerOptions: {
2735
+ openai: { quality: 'max' } satisfies OpenAIImageModelGenerationOptions,
2736
+ },
2737
+ });
2738
+ ```
2739
+
2602
2740
  For more on `generateImage()` see [Image Generation](/docs/ai-sdk-core/image-generation).
2603
2741
 
2604
2742
  OpenAI's image models return additional metadata in the response that can be
@@ -2612,7 +2750,7 @@ is available:
2612
2750
  - `revisedPrompt` _string_ - The revised prompt that was actually used to generate the image (OpenAI may modify your prompt for safety or clarity)
2613
2751
  - `created` _number_ - The Unix timestamp (in seconds) of when the image was created
2614
2752
  - `size` _string_ - The size of the generated image. One of `1024x1024`, `1024x1536`, or `1536x1024`
2615
- - `quality` _string_ - The quality of the generated image. One of `low`, `medium`, or `high`
2753
+ - `quality` _string_ - The quality of the generated image. One of `low`, `medium`, `high`, `xhigh`, or `max`
2616
2754
  - `background` _string_ - The background parameter used for the image generation. Either `transparent` or `opaque`
2617
2755
  - `outputFormat` _string_ - The output format of the generated image. One of `png`, `webp`, or `jpeg`
2618
2756
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai",
3
- "version": "3.0.108",
3
+ "version": "3.0.110",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -97,6 +97,23 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
97
97
  const isReasoningModel =
98
98
  openaiOptions.forceReasoning ?? modelCapabilities.isReasoningModel;
99
99
 
100
+ let resolvedReasoningEffort = openaiOptions.reasoningEffort;
101
+
102
+ if (
103
+ resolvedReasoningEffort != null &&
104
+ modelCapabilities.supportedReasoningEfforts != null &&
105
+ !modelCapabilities.supportedReasoningEfforts.includes(
106
+ resolvedReasoningEffort,
107
+ )
108
+ ) {
109
+ warnings.push({
110
+ type: 'unsupported',
111
+ feature: 'reasoningEffort',
112
+ details: `${this.modelId} only supports the following reasoning efforts: ${modelCapabilities.supportedReasoningEfforts.join(', ')}`,
113
+ });
114
+ resolvedReasoningEffort = undefined;
115
+ }
116
+
100
117
  if (topK != null) {
101
118
  warnings.push({ type: 'unsupported', feature: 'topK' });
102
119
  }
@@ -168,7 +185,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
168
185
  store: openaiOptions.store,
169
186
  metadata: openaiOptions.metadata,
170
187
  prediction: openaiOptions.prediction,
171
- reasoning_effort: openaiOptions.reasoningEffort,
188
+ reasoning_effort: resolvedReasoningEffort,
172
189
  service_tier: openaiOptions.serviceTier,
173
190
  prompt_cache_key: openaiOptions.promptCacheKey,
174
191
  prompt_cache_options: openaiOptions.promptCacheOptions,
@@ -179,6 +196,19 @@ export class OpenAIChatLanguageModel implements LanguageModelV3 {
179
196
  messages,
180
197
  };
181
198
 
199
+ if (
200
+ modelCapabilities.supportedReasoningEfforts != null &&
201
+ baseArgs.prompt_cache_retention != null
202
+ ) {
203
+ baseArgs.prompt_cache_retention = undefined;
204
+ warnings.push({
205
+ type: 'unsupported',
206
+ feature: 'promptCacheRetention',
207
+ details:
208
+ 'promptCacheRetention is not supported by GPT-6 and later models; use promptCacheOptions instead',
209
+ });
210
+ }
211
+
182
212
  // remove unsupported settings for reasoning models
183
213
  // see https://platform.openai.com/docs/guides/reasoning#limitations
184
214
  if (isReasoningModel) {
@@ -108,6 +108,7 @@ export const openaiLanguageModelChatOptions = lazySchema(() =>
108
108
 
109
109
  /**
110
110
  * Reasoning effort for reasoning models. Defaults to `medium`.
111
+ * GPT-6 and later models support 'low' | 'medium' | 'high' | 'xhigh' | 'max'.
111
112
  */
112
113
  reasoningEffort: z
113
114
  .enum(['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'])
@@ -45,10 +45,11 @@ const baseImageModelOptionsObject = z.object({
45
45
  /**
46
46
  * Quality of the generated image(s).
47
47
  *
48
- * Valid values: `standard`, `hd`, `low`, `medium`, `high`, `auto`.
48
+ * Valid values: `standard`, `hd`, `low`, `medium`, `high`, `xhigh`, `max`, `auto`.
49
+ * `xhigh` and `max` are supported by GPT Image 2.5 models.
49
50
  */
50
51
  quality: z
51
- .enum(['standard', 'hd', 'low', 'medium', 'high', 'auto'])
52
+ .enum(['standard', 'hd', 'low', 'medium', 'high', 'xhigh', 'max', 'auto'])
52
53
  .optional(),
53
54
 
54
55
  /**
package/src/index.ts CHANGED
@@ -19,9 +19,11 @@ export type { OpenAILanguageModelCompletionOptions } from './completion/openai-c
19
19
  export type { OpenAIEmbeddingModelOptions } from './embedding/openai-embedding-options';
20
20
  export type { OpenAISpeechModelOptions } from './speech/openai-speech-options';
21
21
  export type { OpenAITranscriptionModelOptions } from './transcription/openai-transcription-options';
22
+ export type { OpenAIToolOptions } from './responses/openai-responses-prepare-tools';
22
23
  export type {
23
24
  OpenaiResponsesProviderMetadata,
24
25
  OpenaiResponsesReasoningProviderMetadata,
26
+ OpenaiResponsesToolCallProviderMetadata,
25
27
  OpenaiResponsesTextProviderMetadata,
26
28
  OpenaiResponsesSourceDocumentProviderMetadata,
27
29
  } from './responses/openai-responses-provider-metadata';
@@ -3,6 +3,9 @@ export type OpenAILanguageModelCapabilities = {
3
3
  systemMessageMode: 'remove' | 'system' | 'developer';
4
4
  supportsFlexProcessing: boolean;
5
5
  supportsPriorityProcessing: boolean;
6
+ supportsConfigurationUpdate: boolean;
7
+ supportsAsyncToolCalling: boolean;
8
+ supportedReasoningEfforts: readonly string[] | undefined;
6
9
 
7
10
  /**
8
11
  * Allow temperature, topP, logProbs when reasoningEffort is none.
@@ -19,6 +22,7 @@ export function getOpenAILanguageModelCapabilities(
19
22
  gptVersion?.minor == null &&
20
23
  (gptVersion?.variant?.startsWith('chat') ?? false);
21
24
  const isGptNanoModel = gptVersion?.variant?.startsWith('nano') ?? false;
25
+ const isGpt6OrLaterModel = gptVersion != null && gptVersion.major >= 6;
22
26
 
23
27
  const supportsFlexProcessing =
24
28
  (oSeriesVersion != null && oSeriesVersion >= 3) ||
@@ -41,6 +45,7 @@ export function getOpenAILanguageModelCapabilities(
41
45
  // https://platform.openai.com/docs/guides/latest-model#gpt-5-1-parameter-compatibility
42
46
  // GPT-5.1 and later model families support temperature, topP, logProbs when reasoningEffort is none.
43
47
  const supportsNonReasoningParameters =
48
+ !isGpt6OrLaterModel &&
44
49
  gptVersion != null &&
45
50
  (gptVersion.major > 5 ||
46
51
  (gptVersion.major === 5 && (gptVersion.minor ?? 0) >= 1));
@@ -50,6 +55,11 @@ export function getOpenAILanguageModelCapabilities(
50
55
  return {
51
56
  supportsFlexProcessing,
52
57
  supportsPriorityProcessing,
58
+ supportsConfigurationUpdate: isGpt6OrLaterModel,
59
+ supportsAsyncToolCalling: isGpt6OrLaterModel,
60
+ supportedReasoningEfforts: isGpt6OrLaterModel
61
+ ? ['low', 'medium', 'high', 'xhigh', 'max']
62
+ : undefined,
53
63
  isReasoningModel,
54
64
  systemMessageMode,
55
65
  supportsNonReasoningParameters,
@@ -27,6 +27,7 @@ export const openaiTools = {
27
27
  *
28
28
  * @param name - The name of the custom tool.
29
29
  * @param description - An optional description of the tool.
30
+ * @param async - Whether the model can continue without waiting for the tool result.
30
31
  * @param format - The output format constraint (grammar type, syntax, and definition).
31
32
  */
32
33
  customTool,
@@ -65,7 +66,7 @@ export const openaiTools = {
65
66
  * @param outputCompression - Compression level for the output image (0-100).
66
67
  * @param outputFormat - The output format of the generated image. One of 'png', 'jpeg', or 'webp'.
67
68
  * @param partialImages - Number of partial images to generate in streaming mode (0-3).
68
- * @param quality - The quality of the generated image. One of 'auto', 'low', 'medium', or 'high'.
69
+ * @param quality - The quality of the generated image. One of 'auto', 'low', 'medium', 'high', 'xhigh', or 'max'. 'xhigh' and 'max' require a GPT Image 2.5 model.
69
70
  * @param size - The size of the generated image. One of 'auto', '1024x1024', '1024x1536', or '1536x1024'.
70
71
  */
71
72
  imageGeneration,
@@ -589,6 +589,18 @@ export async function convertToOpenAIResponsesInput({
589
589
  | string
590
590
  | undefined;
591
591
 
592
+ const isAsync = (part.providerOptions?.[providerOptionsName]
593
+ ?.async ??
594
+ (
595
+ part as {
596
+ providerMetadata?: {
597
+ [providerOptionsName]?: { async?: boolean };
598
+ };
599
+ }
600
+ ).providerMetadata?.[providerOptionsName]?.async) as
601
+ | boolean
602
+ | undefined;
603
+
592
604
  if (hasConversation && id != null) {
593
605
  break;
594
606
  }
@@ -739,6 +751,7 @@ export async function convertToOpenAIResponsesInput({
739
751
  typeof part.input === 'string'
740
752
  ? part.input
741
753
  : JSON.stringify(part.input),
754
+ ...(isAsync != null && { async: isAsync }),
742
755
  id,
743
756
  });
744
757
  break;
@@ -749,6 +762,7 @@ export async function convertToOpenAIResponsesInput({
749
762
  call_id: part.toolCallId,
750
763
  name: resolvedToolName,
751
764
  arguments: serializeToolCallArguments(part.input),
765
+ ...(isAsync != null && { async: isAsync }),
752
766
  ...(namespace != null && { namespace }),
753
767
  });
754
768
  break;
@@ -87,7 +87,8 @@ export type OpenAIResponsesInputItem =
87
87
  | OpenAIResponsesToolSearchCall
88
88
  | OpenAIResponsesToolSearchOutput
89
89
  | OpenAIResponsesReasoning
90
- | OpenAIResponsesItemReference;
90
+ | OpenAIResponsesItemReference
91
+ | OpenAIResponsesConfigurationUpdate;
91
92
 
92
93
  export type OpenAIResponsesIncludeValue =
93
94
  | 'web_search_call.action.sources'
@@ -179,6 +180,7 @@ export type OpenAIResponsesFunctionCall = {
179
180
  call_id: string;
180
181
  name: string;
181
182
  arguments: string;
183
+ async?: boolean;
182
184
  id?: string;
183
185
  namespace?: string;
184
186
  };
@@ -219,6 +221,7 @@ export type OpenAIResponsesCustomToolCall = {
219
221
  call_id: string;
220
222
  name: string;
221
223
  input: string;
224
+ async?: boolean;
222
225
  };
223
226
 
224
227
  export type OpenAIResponsesCustomToolCallOutput = {
@@ -372,6 +375,7 @@ export type OpenAIResponsesFunctionTool = {
372
375
  name: string;
373
376
  description: string | undefined;
374
377
  parameters: JSONSchema7;
378
+ async?: boolean;
375
379
  strict?: boolean;
376
380
  defer_loading?: boolean;
377
381
  };
@@ -466,7 +470,7 @@ export type OpenAIResponsesTool =
466
470
  output_compression: number | undefined;
467
471
  output_format: 'png' | 'jpeg' | 'webp' | undefined;
468
472
  partial_images: number | undefined;
469
- quality: 'auto' | 'low' | 'medium' | 'high' | undefined;
473
+ quality: 'auto' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | undefined;
470
474
  size: 'auto' | '1024x1024' | '1024x1536' | '1536x1024' | undefined;
471
475
  }
472
476
 
@@ -500,6 +504,7 @@ export type OpenAIResponsesTool =
500
504
  type: 'custom';
501
505
  name: string;
502
506
  description?: string;
507
+ async?: boolean;
503
508
  format?:
504
509
  | {
505
510
  type: 'grammar';
@@ -579,6 +584,13 @@ export type OpenAIResponsesReasoning = {
579
584
  }>;
580
585
  };
581
586
 
587
+ export type OpenAIResponsesConfigurationUpdate = {
588
+ type: 'configuration_update';
589
+ reasoning: {
590
+ effort: 'low' | 'medium' | 'high' | 'xhigh' | 'max';
591
+ };
592
+ };
593
+
582
594
  // Captured from the Responses API when OpenAI returned an early
583
595
  // insufficient_quota stream error after HTTP 200. This shape differs from the
584
596
  // currently documented ResponseErrorEvent below.
@@ -764,6 +776,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
764
776
  call_id: z.string(),
765
777
  name: z.string(),
766
778
  arguments: z.string(),
779
+ async: z.boolean().nullish(),
767
780
  namespace: z.string().nullish(),
768
781
  }),
769
782
  z.object({
@@ -842,6 +855,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
842
855
  call_id: z.string(),
843
856
  name: z.string(),
844
857
  input: z.string(),
858
+ async: z.boolean().nullish(),
845
859
  }),
846
860
  z.object({
847
861
  type: z.literal('shell_call'),
@@ -909,6 +923,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
909
923
  call_id: z.string(),
910
924
  name: z.string(),
911
925
  arguments: z.string(),
926
+ async: z.boolean().nullish(),
912
927
  status: z.literal('completed'),
913
928
  namespace: z.string().nullish(),
914
929
  }),
@@ -918,6 +933,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
918
933
  call_id: z.string(),
919
934
  name: z.string(),
920
935
  input: z.string(),
936
+ async: z.boolean().nullish(),
921
937
  status: z.literal('completed'),
922
938
  }),
923
939
  z.object({
@@ -1402,6 +1418,7 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1402
1418
  name: z.string(),
1403
1419
  arguments: z.string(),
1404
1420
  id: z.string(),
1421
+ async: z.boolean().nullish(),
1405
1422
  namespace: z.string().nullish(),
1406
1423
  }),
1407
1424
  z.object({
@@ -1410,6 +1427,7 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1410
1427
  name: z.string(),
1411
1428
  input: z.string(),
1412
1429
  id: z.string(),
1430
+ async: z.boolean().nullish(),
1413
1431
  }),
1414
1432
  z.object({
1415
1433
  type: z.literal('computer_call'),