@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.
- package/CHANGELOG.md +18 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +35 -6
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +14 -3
- package/dist/internal/index.js +32 -3
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +27 -67
- package/package.json +3 -3
- package/src/chat/openai-chat-language-model.ts +18 -10
- package/src/image/openai-image-model-options.ts +8 -0
- package/src/openai-config.ts +7 -0
- package/src/responses/convert-to-openai-responses-input.ts +29 -6
- package/src/responses/openai-responses-api.ts +3 -0
- package/src/responses/openai-responses-language-model.ts +5 -0
- package/src/tool/image-generation.ts +3 -2
package/docs/03-openai.mdx
CHANGED
|
@@ -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
|
-
###
|
|
2081
|
+
### Batch
|
|
2082
2082
|
|
|
2083
2083
|
<Note type="warning">
|
|
2084
|
-
|
|
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
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
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
|
|
3076
|
-
|
|
|
3077
|
-
| `gpt-image-2`
|
|
3078
|
-
| `gpt-image-
|
|
3079
|
-
| `gpt-image-
|
|
3080
|
-
| `gpt-image-1`
|
|
3081
|
-
| `
|
|
3082
|
-
| `
|
|
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.
|
|
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.
|
|
39
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
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
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
package/src/openai-config.ts
CHANGED
|
@@ -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.
|
|
114
|
-
* arbitrary WIDTHxHEIGHT sizes where both are divisible
|
|
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 & {});
|