@ai-sdk/openai 4.0.62 → 4.0.64

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.
@@ -2078,65 +2078,16 @@ The metadata includes the following fields:
2078
2078
  - **itemId** _string_ — The ID of the compaction item in the Responses API
2079
2079
  - **encryptedContent** _string_ (optional) — The encrypted compaction state. This is automatically sent back to the API when the message is included in subsequent requests.
2080
2080
 
2081
- ### Text Batches
2081
+ ### Batch
2082
2082
 
2083
2083
  <Note type="warning">
2084
- 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.
2085
2085
  </Note>
2086
2086
 
2087
- The OpenAI provider supports asynchronous text generation through the
2088
- [Batch API](https://developers.openai.com/api/docs/guides/batch). Use the
2089
- experimental text batch APIs to start a batch, poll its status, and stream its
2090
- results:
2091
-
2092
- ```ts
2093
- import { openai } from '@ai-sdk/openai';
2094
- import {
2095
- experimental_getBatchResults as getBatchResults,
2096
- experimental_getBatchStatus as getBatchStatus,
2097
- experimental_startBatch as startBatch,
2098
- } from 'ai';
2099
- import { setTimeout } from 'node:timers/promises';
2100
-
2101
- const model = 'gpt-4.1-nano';
2102
-
2103
- const batch = await startBatch({
2104
- provider: openai,
2105
- requests: [
2106
- {
2107
- id: 'capital-france',
2108
- type: 'text',
2109
- model,
2110
- prompt: 'What is the capital of France?',
2111
- },
2112
- {
2113
- id: 'capital-germany',
2114
- type: 'text',
2115
- model,
2116
- prompt: 'What is the capital of Germany?',
2117
- },
2118
- ],
2119
- });
2120
-
2121
- let status = batch.status;
2122
- while (status === 'pending') {
2123
- await setTimeout(60_000);
2124
- ({ status } = await getBatchStatus({ provider: openai, batch }));
2125
- }
2126
-
2127
- for await (const item of getBatchResults({ provider: openai, batch })) {
2128
- if (item.status === 'succeeded') {
2129
- console.log(item.id, item.text);
2130
- } else {
2131
- console.error(item.id, item.error);
2132
- }
2133
- }
2134
- ```
2135
-
2136
- `startBatch` returns a serializable batch reference. Persist this reference
2137
- to check the batch status or retrieve its results from another process. Results
2138
- can arrive in a different order from the input requests, so match each result by
2139
- 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.
2140
2091
 
2141
2092
  Each request specifies its `type` and `model`. OpenAI requires every text
2142
2093
  request in a batch to use the same model and throws before submission when the
@@ -2994,7 +2945,7 @@ Transform an existing image using text prompts:
2994
2945
  const imageBuffer = readFileSync('./input-image.png');
2995
2946
 
2996
2947
  const { images } = await generateImage({
2997
- model: openai.image('gpt-image-2'),
2948
+ model: openai.image('gpt-image-2.5-sunburst'),
2998
2949
  prompt: {
2999
2950
  text: 'Turn the cat into a dog but retain the style of the original image',
3000
2951
  images: [imageBuffer],
@@ -3011,7 +2962,7 @@ const image = readFileSync('./input-image.png');
3011
2962
  const mask = readFileSync('./mask.png'); // Transparent areas = edit regions
3012
2963
 
3013
2964
  const { images } = await generateImage({
3014
- model: openai.image('gpt-image-2'),
2965
+ model: openai.image('gpt-image-2.5-sunburst'),
3015
2966
  prompt: {
3016
2967
  text: 'A sunlit indoor lounge area with a pool containing a flamingo',
3017
2968
  images: [image],
@@ -3056,7 +3007,7 @@ const owl = readFileSync('./owl.png');
3056
3007
  const bear = readFileSync('./bear.png');
3057
3008
 
3058
3009
  const { images } = await generateImage({
3059
- model: openai.image('gpt-image-2'),
3010
+ model: openai.image('gpt-image-2.5-sunburst'),
3060
3011
  prompt: {
3061
3012
  text: 'Combine these animals into a group photo, retaining the original style',
3062
3013
  images: [cat, dog, owl, bear],
@@ -3072,14 +3023,23 @@ const { images } = await generateImage({
3072
3023
 
3073
3024
  ### Model Capabilities
3074
3025
 
3075
- | Model | Sizes |
3076
- | ------------------ | ------------------------------- |
3077
- | `gpt-image-2` | 1024x1024, 1536x1024, 1024x1536 |
3078
- | `gpt-image-1.5` | 1024x1024, 1536x1024, 1024x1536 |
3079
- | `gpt-image-1-mini` | 1024x1024, 1536x1024, 1024x1536 |
3080
- | `gpt-image-1` | 1024x1024, 1536x1024, 1024x1536 |
3081
- | `dall-e-3` | 1024x1024, 1792x1024, 1024x1792 |
3082
- | `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.
3083
3043
 
3084
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:
3085
3045
 
@@ -3088,7 +3048,7 @@ import { openai, type OpenAIImageModelGenerationOptions } from '@ai-sdk/openai';
3088
3048
  import { generateImage } from 'ai';
3089
3049
 
3090
3050
  const { image, providerMetadata } = await generateImage({
3091
- model: openai.image('gpt-image-2'),
3051
+ model: openai.image('gpt-image-2.5-flare'),
3092
3052
  prompt: 'A salamander at sunrise in a forest pond in the Seychelles.',
3093
3053
  providerOptions: {
3094
3054
  openai: { quality: 'high' } satisfies OpenAIImageModelGenerationOptions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai",
3
- "version": "4.0.62",
3
+ "version": "4.0.64",
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.11",
39
- "@ai-sdk/provider-utils": "5.0.37"
38
+ "@ai-sdk/provider": "4.0.12",
39
+ "@ai-sdk/provider-utils": "5.0.38"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.1",
@@ -1,13 +1,14 @@
1
- import type {
2
- LanguageModelV4,
3
- LanguageModelV4CallOptions,
4
- LanguageModelV4Content,
5
- LanguageModelV4FinishReason,
6
- LanguageModelV4GenerateResult,
7
- LanguageModelV4StreamPart,
8
- LanguageModelV4StreamResult,
9
- SharedV4ProviderMetadata,
10
- SharedV4Warning,
1
+ import {
2
+ InvalidResponseDataError,
3
+ type LanguageModelV4,
4
+ type LanguageModelV4CallOptions,
5
+ type LanguageModelV4Content,
6
+ type LanguageModelV4FinishReason,
7
+ type LanguageModelV4GenerateResult,
8
+ type LanguageModelV4StreamPart,
9
+ type LanguageModelV4StreamResult,
10
+ type SharedV4ProviderMetadata,
11
+ type SharedV4Warning,
11
12
  } from '@ai-sdk/provider';
12
13
  import {
13
14
  StreamingToolCallTracker,
@@ -396,6 +397,13 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
396
397
  });
397
398
 
398
399
  const choice = response.choices[0];
400
+ if (choice == null) {
401
+ throw new InvalidResponseDataError({
402
+ data: rawResponse,
403
+ message: 'Response did not contain any choices.',
404
+ });
405
+ }
406
+
399
407
  const content: Array<LanguageModelV4Content> = [];
400
408
 
401
409
  // text content:
@@ -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
 
@@ -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
@@ -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;
@@ -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,
@@ -110,8 +110,9 @@ type ImageGenerationArgs = {
110
110
 
111
111
  /**
112
112
  * The size of the generated image.
113
- * One of 1024x1024, 1024x1536, 1536x1024, or auto. gpt-image-2 also accepts
114
- * 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.
115
116
  * Default: auto.
116
117
  */
117
118
  size?: 'auto' | '1024x1024' | '1024x1536' | '1536x1024' | (string & {});