@ai-sdk/openai 4.0.61 → 4.0.63

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.
@@ -827,6 +827,16 @@ for await (const part of result.stream) {
827
827
  setting `store: false`.
828
828
  </Note>
829
829
 
830
+ To use `xhigh` or `max` quality, select `gpt-image-2.5-flare` or
831
+ `gpt-image-2.5-sunburst` as the image generation tool's model:
832
+
833
+ ```ts
834
+ openai.tools.imageGeneration({
835
+ model: 'gpt-image-2.5-flare',
836
+ quality: 'xhigh',
837
+ });
838
+ ```
839
+
830
840
  For complete details on model availability, image quality controls, supported sizes, and tool-specific parameters,
831
841
  refer to the OpenAI documentation:
832
842
 
@@ -2068,65 +2078,16 @@ The metadata includes the following fields:
2068
2078
  - **itemId** _string_ — The ID of the compaction item in the Responses API
2069
2079
  - **encryptedContent** _string_ (optional) — The encrypted compaction state. This is automatically sent back to the API when the message is included in subsequent requests.
2070
2080
 
2071
- ### Text Batches
2081
+ ### Batch
2072
2082
 
2073
2083
  <Note type="warning">
2074
- Text batch support is experimental and the API may change in patch releases.
2084
+ Batch support is experimental and the API may change in patch releases.
2075
2085
  </Note>
2076
2086
 
2077
- The OpenAI provider supports asynchronous text generation through the
2078
- [Batch API](https://developers.openai.com/api/docs/guides/batch). Use the
2079
- experimental text batch APIs to start a batch, poll its status, and stream its
2080
- results:
2081
-
2082
- ```ts
2083
- import { openai } from '@ai-sdk/openai';
2084
- import {
2085
- experimental_getBatchResults as getBatchResults,
2086
- experimental_getBatchStatus as getBatchStatus,
2087
- experimental_startBatch as startBatch,
2088
- } from 'ai';
2089
- import { setTimeout } from 'node:timers/promises';
2090
-
2091
- const model = 'gpt-4.1-nano';
2092
-
2093
- const batch = await startBatch({
2094
- provider: openai,
2095
- requests: [
2096
- {
2097
- id: 'capital-france',
2098
- type: 'text',
2099
- model,
2100
- prompt: 'What is the capital of France?',
2101
- },
2102
- {
2103
- id: 'capital-germany',
2104
- type: 'text',
2105
- model,
2106
- prompt: 'What is the capital of Germany?',
2107
- },
2108
- ],
2109
- });
2110
-
2111
- let status = batch.status;
2112
- while (status === 'pending') {
2113
- await setTimeout(60_000);
2114
- ({ status } = await getBatchStatus({ provider: openai, batch }));
2115
- }
2116
-
2117
- for await (const item of getBatchResults({ provider: openai, batch })) {
2118
- if (item.status === 'succeeded') {
2119
- console.log(item.id, item.text);
2120
- } else {
2121
- console.error(item.id, item.error);
2122
- }
2123
- }
2124
- ```
2125
-
2126
- `startBatch` returns a serializable batch reference. Persist this reference
2127
- to check the batch status or retrieve its results from another process. Results
2128
- can arrive in a different order from the input requests, so match each result by
2129
- its `id`.
2087
+ The OpenAI provider supports asynchronous text generation through the [Batch
2088
+ API](https://developers.openai.com/api/docs/guides/batch). Pass the OpenAI
2089
+ provider to the AI SDK's [Batch](/docs/ai-sdk-core/batch) API for
2090
+ the complete workflow, including polling, persistence, and result handling.
2130
2091
 
2131
2092
  Each request specifies its `type` and `model`. OpenAI requires every text
2132
2093
  request in a batch to use the same model and throws before submission when the
@@ -2984,7 +2945,7 @@ Transform an existing image using text prompts:
2984
2945
  const imageBuffer = readFileSync('./input-image.png');
2985
2946
 
2986
2947
  const { images } = await generateImage({
2987
- model: openai.image('gpt-image-2'),
2948
+ model: openai.image('gpt-image-2.5-sunburst'),
2988
2949
  prompt: {
2989
2950
  text: 'Turn the cat into a dog but retain the style of the original image',
2990
2951
  images: [imageBuffer],
@@ -3001,7 +2962,7 @@ const image = readFileSync('./input-image.png');
3001
2962
  const mask = readFileSync('./mask.png'); // Transparent areas = edit regions
3002
2963
 
3003
2964
  const { images } = await generateImage({
3004
- model: openai.image('gpt-image-2'),
2965
+ model: openai.image('gpt-image-2.5-sunburst'),
3005
2966
  prompt: {
3006
2967
  text: 'A sunlit indoor lounge area with a pool containing a flamingo',
3007
2968
  images: [image],
@@ -3046,7 +3007,7 @@ const owl = readFileSync('./owl.png');
3046
3007
  const bear = readFileSync('./bear.png');
3047
3008
 
3048
3009
  const { images } = await generateImage({
3049
- model: openai.image('gpt-image-2'),
3010
+ model: openai.image('gpt-image-2.5-sunburst'),
3050
3011
  prompt: {
3051
3012
  text: 'Combine these animals into a group photo, retaining the original style',
3052
3013
  images: [cat, dog, owl, bear],
@@ -3062,14 +3023,23 @@ const { images } = await generateImage({
3062
3023
 
3063
3024
  ### Model Capabilities
3064
3025
 
3065
- | Model | Sizes |
3066
- | ------------------ | ------------------------------- |
3067
- | `gpt-image-2` | 1024x1024, 1536x1024, 1024x1536 |
3068
- | `gpt-image-1.5` | 1024x1024, 1536x1024, 1024x1536 |
3069
- | `gpt-image-1-mini` | 1024x1024, 1536x1024, 1024x1536 |
3070
- | `gpt-image-1` | 1024x1024, 1536x1024, 1024x1536 |
3071
- | `dall-e-3` | 1024x1024, 1792x1024, 1024x1792 |
3072
- | `dall-e-2` | 256x256, 512x512, 1024x1024 |
3026
+ | Model | Sizes |
3027
+ | ------------------------ | -------------------------------------- |
3028
+ | `gpt-image-2.5-flare` | Standard presets and custom dimensions |
3029
+ | `gpt-image-2.5-sunburst` | Standard presets and custom dimensions |
3030
+ | `gpt-image-2` | 1024x1024, 1536x1024, 1024x1536 |
3031
+ | `gpt-image-1.5` | 1024x1024, 1536x1024, 1024x1536 |
3032
+ | `gpt-image-1-mini` | 1024x1024, 1536x1024, 1024x1536 |
3033
+ | `gpt-image-1` | 1024x1024, 1536x1024, 1024x1536 |
3034
+ | `dall-e-3` | 1024x1024, 1792x1024, 1024x1792 |
3035
+ | `dall-e-2` | 256x256, 512x512, 1024x1024 |
3036
+
3037
+ Use `gpt-image-2.5-flare` for fast, general-purpose image generation and
3038
+ `gpt-image-2.5-sunburst` when editing precision and instruction following are
3039
+ the priority. Both models accept custom `WIDTHxHEIGHT` sizes whose dimensions
3040
+ are multiples of 16, with aspect ratios from 1:3 through 3:1, no edge longer
3041
+ than 3840 pixels, and a total area from 655,360 through 8,294,400 pixels.
3042
+ Resolutions above 2560x1440 are experimental.
3073
3043
 
3074
3044
  You can pass optional `providerOptions` to the image model. These are prone to change by OpenAI and are model dependent. For example, the `gpt-image-*` models support the `quality` option:
3075
3045
 
@@ -3078,7 +3048,7 @@ import { openai, type OpenAIImageModelGenerationOptions } from '@ai-sdk/openai';
3078
3048
  import { generateImage } from 'ai';
3079
3049
 
3080
3050
  const { image, providerMetadata } = await generateImage({
3081
- model: openai.image('gpt-image-2'),
3051
+ model: openai.image('gpt-image-2.5-flare'),
3082
3052
  prompt: 'A salamander at sunrise in a forest pond in the Seychelles.',
3083
3053
  providerOptions: {
3084
3054
  openai: { quality: 'high' } satisfies OpenAIImageModelGenerationOptions,
@@ -3086,6 +3056,19 @@ const { image, providerMetadata } = await generateImage({
3086
3056
  });
3087
3057
  ```
3088
3058
 
3059
+ The `gpt-image-2.5-flare` and `gpt-image-2.5-sunburst` models also support
3060
+ `quality: 'xhigh'` and `quality: 'max'` for both image generation and editing:
3061
+
3062
+ ```ts
3063
+ const { image } = await generateImage({
3064
+ model: openai.image('gpt-image-2.5-sunburst'),
3065
+ prompt: 'A salamander at sunrise in a forest pond in the Seychelles.',
3066
+ providerOptions: {
3067
+ openai: { quality: 'max' } satisfies OpenAIImageModelGenerationOptions,
3068
+ },
3069
+ });
3070
+ ```
3071
+
3089
3072
  For more on `generateImage()` see [Image Generation](/docs/ai-sdk-core/image-generation).
3090
3073
 
3091
3074
  OpenAI's image models return additional metadata in the response that can be
@@ -3098,7 +3081,7 @@ is available:
3098
3081
  - `revisedPrompt` _string_ - The revised prompt that was actually used to generate the image (OpenAI may modify your prompt for safety or clarity)
3099
3082
  - `created` _number_ - The Unix timestamp (in seconds) of when the image was created
3100
3083
  - `size` _string_ - The size of the generated image. One of `1024x1024`, `1024x1536`, or `1536x1024`
3101
- - `quality` _string_ - The quality of the generated image. One of `low`, `medium`, or `high`
3084
+ - `quality` _string_ - The quality of the generated image. One of `low`, `medium`, `high`, `xhigh`, or `max`
3102
3085
  - `background` _string_ - The background parameter used for the image generation. Either `transparent` or `opaque`
3103
3086
  - `outputFormat` _string_ - The output format of the generated image. One of `png`, `webp`, or `jpeg`
3104
3087
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai",
3
- "version": "4.0.61",
3
+ "version": "4.0.63",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -12,6 +12,10 @@ export type OpenAIImageModelId =
12
12
  | 'gpt-image-1-mini'
13
13
  | 'gpt-image-1.5'
14
14
  | 'gpt-image-2'
15
+ | 'gpt-image-2.5-flare'
16
+ | 'gpt-image-2.5-flare-2026-09-08'
17
+ | 'gpt-image-2.5-sunburst'
18
+ | 'gpt-image-2.5-sunburst-2026-09-08'
15
19
  | 'chatgpt-image-latest'
16
20
  | (string & {});
17
21
 
@@ -23,6 +27,10 @@ export const modelMaxImagesPerCall: Record<OpenAIImageModelId, number> = {
23
27
  'gpt-image-1-mini': 10,
24
28
  'gpt-image-1.5': 10,
25
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,
26
34
  'chatgpt-image-latest': 10,
27
35
  };
28
36
 
@@ -45,10 +53,11 @@ const baseImageModelOptionsObject = z.object({
45
53
  /**
46
54
  * Quality of the generated image(s).
47
55
  *
48
- * Valid values: `standard`, `hd`, `low`, `medium`, `high`, `auto`.
56
+ * Valid values: `standard`, `hd`, `low`, `medium`, `high`, `xhigh`, `max`, `auto`.
57
+ * `xhigh` and `max` are supported by GPT Image 2.5 models.
49
58
  */
50
59
  quality: z
51
- .enum(['standard', 'hd', 'low', 'medium', 'high', 'auto'])
60
+ .enum(['standard', 'hd', 'low', 'medium', 'high', 'xhigh', 'max', 'auto'])
52
61
  .optional(),
53
62
 
54
63
  /**
@@ -17,6 +17,13 @@ export type OpenAIConfig = {
17
17
  fetch?: FetchFunction;
18
18
  webSocket?: WebSocketConstructor;
19
19
  generateId?: () => string;
20
+ /**
21
+ * Whether Responses API message input items must include an explicit
22
+ * `type: 'message'` discriminator.
23
+ *
24
+ * @see https://github.com/vercel/ai/issues/20180
25
+ */
26
+ explicitMessageItemType?: boolean;
20
27
  /**
21
28
  * This is soft-deprecated. Use provider references (e.g. `{ openai: 'file-abc123' }`)
22
29
  * in file part data instead. File ID prefixes used to identify file IDs
@@ -77,7 +77,7 @@ export const openaiTools = {
77
77
  * @param outputCompression - Compression level for the output image (0-100).
78
78
  * @param outputFormat - The output format of the generated image. One of 'png', 'jpeg', or 'webp'.
79
79
  * @param partialImages - Number of partial images to generate in streaming mode (0-3).
80
- * @param quality - The quality of the generated image. One of 'auto', 'low', 'medium', or 'high'.
80
+ * @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.
81
81
  * @param size - The size of the generated image. One of 'auto', '1024x1024', '1024x1536', or '1536x1024'.
82
82
  */
83
83
  imageGeneration,
@@ -341,6 +341,7 @@ export async function convertToOpenAIResponsesInput({
341
341
  toolNameMapping,
342
342
  systemMessageMode,
343
343
  providerOptionsName,
344
+ explicitMessageItemType = false,
344
345
  fileIdPrefixes,
345
346
  passThroughUnsupportedFiles = false,
346
347
  store,
@@ -358,6 +359,7 @@ export async function convertToOpenAIResponsesInput({
358
359
  toolNameMapping: ToolNameMapping;
359
360
  systemMessageMode: 'system' | 'developer' | 'remove';
360
361
  providerOptionsName: string;
362
+ explicitMessageItemType?: boolean;
361
363
  /** @deprecated Use provider references instead. */
362
364
  fileIdPrefixes?: readonly string[];
363
365
  passThroughUnsupportedFiles?: boolean;
@@ -378,6 +380,7 @@ export async function convertToOpenAIResponsesInput({
378
380
  let input: OpenAIResponsesInput = [];
379
381
  const warnings: Array<SharedV4Warning> = [];
380
382
  const processedApprovalIds = new Set<string>();
383
+ const programmaticToolCallIds = new Set<string>();
381
384
  const parallelToolResultGroups =
382
385
  hasConversation || hasPreviousResponseId
383
386
  ? collectCompleteParallelToolResultGroups({
@@ -398,6 +401,7 @@ export async function convertToOpenAIResponsesInput({
398
401
  providerOptionsName,
399
402
  );
400
403
  input.push({
404
+ ...(explicitMessageItemType && { type: 'message' as const }),
401
405
  role: 'system',
402
406
  content:
403
407
  promptCacheBreakpoint == null
@@ -418,6 +422,7 @@ export async function convertToOpenAIResponsesInput({
418
422
  providerOptionsName,
419
423
  );
420
424
  input.push({
425
+ ...(explicitMessageItemType && { type: 'message' as const }),
421
426
  role: 'developer',
422
427
  content:
423
428
  promptCacheBreakpoint == null
@@ -451,6 +456,7 @@ export async function convertToOpenAIResponsesInput({
451
456
 
452
457
  case 'user': {
453
458
  input.push({
459
+ ...(explicitMessageItemType && { type: 'message' as const }),
454
460
  role: 'user',
455
461
  content: content.map((part, index) => {
456
462
  switch (part.type) {
@@ -603,6 +609,7 @@ export async function convertToOpenAIResponsesInput({
603
609
  }
604
610
 
605
611
  input.push({
612
+ ...(explicitMessageItemType && { type: 'message' as const }),
606
613
  role: 'assistant',
607
614
  content: [{ type: 'output_text', text: part.text }],
608
615
  id,
@@ -694,6 +701,10 @@ export async function convertToOpenAIResponsesInput({
694
701
  | { type: 'program'; callerId: string }
695
702
  | undefined;
696
703
 
704
+ if (caller?.type === 'program') {
705
+ programmaticToolCallIds.add(part.toolCallId);
706
+ }
707
+
697
708
  if (hasConversation && id != null) {
698
709
  break;
699
710
  }
@@ -1568,6 +1579,23 @@ export async function convertToOpenAIResponsesInput({
1568
1579
  continue;
1569
1580
  }
1570
1581
 
1582
+ const resultCaller = part.providerOptions?.[providerOptionsName]
1583
+ ?.caller as
1584
+ | { type: 'direct' }
1585
+ | { type: 'program'; callerId: string }
1586
+ | undefined;
1587
+
1588
+ if (
1589
+ output.type === 'execution-denied' &&
1590
+ (resultCaller?.type === 'program' ||
1591
+ programmaticToolCallIds.has(part.toolCallId))
1592
+ ) {
1593
+ throw new UnsupportedFunctionalityError({
1594
+ functionality:
1595
+ 'execution-denied results for programmatic tool calls',
1596
+ });
1597
+ }
1598
+
1571
1599
  const contentValue = await convertFunctionToolResultOutput({
1572
1600
  output,
1573
1601
  toolName: part.toolName,
@@ -1581,12 +1609,7 @@ export async function convertToOpenAIResponsesInput({
1581
1609
  warnings,
1582
1610
  });
1583
1611
 
1584
- const caller = mapToolCaller(
1585
- part.providerOptions?.[providerOptionsName]?.caller as
1586
- | { type: 'direct' }
1587
- | { type: 'program'; callerId: string }
1588
- | undefined,
1589
- );
1612
+ const caller = mapToolCaller(resultCaller);
1590
1613
 
1591
1614
  input.push({
1592
1615
  type: 'function_call_output',
@@ -214,6 +214,7 @@ export type OpenAIResponsesApplyPatchOperationDiffDoneChunk = {
214
214
  };
215
215
 
216
216
  export type OpenAIResponsesSystemMessage = {
217
+ type?: 'message';
217
218
  role: 'system' | 'developer';
218
219
  content:
219
220
  | string
@@ -225,6 +226,7 @@ export type OpenAIResponsesSystemMessage = {
225
226
  };
226
227
 
227
228
  export type OpenAIResponsesUserMessage = {
229
+ type?: 'message';
228
230
  role: 'user';
229
231
  content: Array<
230
232
  | {
@@ -262,6 +264,7 @@ export type OpenAIResponsesUserMessage = {
262
264
  };
263
265
 
264
266
  export type OpenAIResponsesAssistantMessage = {
267
+ type?: 'message';
265
268
  role: 'assistant';
266
269
  content: Array<{ type: 'output_text'; text: string }>;
267
270
  id?: string;
@@ -626,7 +629,7 @@ export type OpenAIResponsesTool =
626
629
  output_compression: number | undefined;
627
630
  output_format: 'png' | 'jpeg' | 'webp' | undefined;
628
631
  partial_images: number | undefined;
629
- quality: 'auto' | 'low' | 'medium' | 'high' | undefined;
632
+ quality: 'auto' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | undefined;
630
633
  size:
631
634
  | 'auto'
632
635
  | '1024x1024'
@@ -391,6 +391,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
391
391
  ? 'developer'
392
392
  : modelCapabilities.systemMessageMode),
393
393
  providerOptionsName,
394
+ explicitMessageItemType: config.explicitMessageItemType,
394
395
  fileIdPrefixes: config.fileIdPrefixes,
395
396
  passThroughUnsupportedFiles:
396
397
  openaiOptions?.passThroughUnsupportedFiles ?? false,
@@ -1349,6 +1350,8 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1349
1350
  }
1350
1351
 
1351
1352
  case 'apply_patch_call': {
1353
+ hasFunctionCall = true;
1354
+
1352
1355
  content.push({
1353
1356
  type: 'tool-call',
1354
1357
  toolCallId: part.call_id,
@@ -2291,6 +2294,8 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2291
2294
 
2292
2295
  // Emit the final tool-call with complete diff when status is 'completed'
2293
2296
  if (toolCall && value.item.status === 'completed') {
2297
+ hasFunctionCall = true;
2298
+
2294
2299
  controller.enqueue({
2295
2300
  type: 'tool-call',
2296
2301
  toolCallId: toolCall.toolCallId,
@@ -23,7 +23,9 @@ export const imageGenerationArgsSchema = lazySchema(() =>
23
23
  outputCompression: z.number().int().min(0).max(100).optional(),
24
24
  outputFormat: z.enum(['png', 'jpeg', 'webp']).optional(),
25
25
  partialImages: z.number().int().min(0).max(3).optional(),
26
- quality: z.enum(['auto', 'low', 'medium', 'high']).optional(),
26
+ quality: z
27
+ .enum(['auto', 'low', 'medium', 'high', 'xhigh', 'max'])
28
+ .optional(),
27
29
  size: z
28
30
  .union([
29
31
  z.enum(['1024x1024', '1024x1536', '1536x1024', 'auto']),
@@ -101,14 +103,16 @@ type ImageGenerationArgs = {
101
103
 
102
104
  /**
103
105
  * The quality of the generated image.
104
- * One of low, medium, high, or auto. Default: auto.
106
+ * One of low, medium, high, xhigh, max, or auto. Default: auto.
107
+ * xhigh and max are supported by GPT Image 2.5 models.
105
108
  */
106
- quality?: 'auto' | 'low' | 'medium' | 'high';
109
+ quality?: 'auto' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
107
110
 
108
111
  /**
109
112
  * The size of the generated image.
110
- * One of 1024x1024, 1024x1536, 1536x1024, or auto. gpt-image-2 also accepts
111
- * arbitrary WIDTHxHEIGHT sizes where both are divisible by 16, e.g. 1536x864.
113
+ * One of 1024x1024, 1024x1536, 1536x1024, or auto. GPT Image 2 and 2.5
114
+ * models also accept arbitrary WIDTHxHEIGHT sizes where both are divisible
115
+ * by 16, e.g. 1536x864.
112
116
  * Default: auto.
113
117
  */
114
118
  size?: 'auto' | '1024x1024' | '1024x1536' | '1536x1024' | (string & {});