@ai-sdk/openai 4.0.107 → 4.0.109
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 +38 -0
- package/dist/index.d.ts +67 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +620 -378
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +244 -4
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +481 -345
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +156 -18
- package/package.json +3 -3
- package/src/chat/convert-to-openai-chat-messages.ts +29 -4
- package/src/chat/openai-chat-language-model-options.ts +13 -0
- package/src/chat/openai-chat-language-model.ts +10 -10
- package/src/chat/openai-chat-prompt.ts +4 -0
- package/src/image/openai-image-model-options.ts +48 -0
- package/src/image/openai-image-model.ts +49 -4
- package/src/index.ts +1 -0
- package/src/internal/index.ts +2 -0
- package/src/live/openai-realtime-model-live.ts +21 -2
- package/src/openai-model-catalog.ts +3 -1
- package/src/openai-tools.ts +4 -3
- package/src/realtime/openai-realtime-event-mapper.ts +172 -11
- package/src/realtime/openai-realtime-model.ts +57 -3
- package/src/responses/openai-responses-api.ts +19 -1
- package/src/responses/openai-responses-prepare-tools.ts +18 -1
- package/src/tool/code-interpreter.ts +38 -2
- package/src/tool/mcp.ts +27 -8
- package/src/transcription/openai-transcription-model.ts +43 -8
package/docs/03-openai.mdx
CHANGED
|
@@ -1005,7 +1005,20 @@ const result = await generateText({
|
|
|
1005
1005
|
|
|
1006
1006
|
The code interpreter tool can be configured with:
|
|
1007
1007
|
|
|
1008
|
-
- **container**: Either
|
|
1008
|
+
- **container**: Either:
|
|
1009
|
+
- a container ID (`string`) to reuse an existing container, or
|
|
1010
|
+
- a configuration `object` to create and configure a new container.
|
|
1011
|
+
|
|
1012
|
+
- **container configuration object**
|
|
1013
|
+
- **fileIds** _string[]_ — File IDs to make available in the container at startup
|
|
1014
|
+
- **memoryLimit** _'1g' | '4g' | '16g' | '64g'_ — Memory limit for the container
|
|
1015
|
+
- **networkPolicy** — Network access policy for outbound requests:
|
|
1016
|
+
- `{ type: 'disabled' }` — No outbound network access
|
|
1017
|
+
- `{ type: 'allowlist', allowedDomains: string[], domainSecrets?: Array<{ domain, name, value }> }`
|
|
1018
|
+
— Allow outbound access only to the specified domains, with optional per-domain secrets
|
|
1019
|
+
|
|
1020
|
+
The organization's hosted tool settings must permit the requested network policy.
|
|
1021
|
+
An allowlist request cannot enable networking when the organization disables it.
|
|
1009
1022
|
|
|
1010
1023
|
<Note>
|
|
1011
1024
|
When working with files generated by the Code Interpreter, reference
|
|
@@ -1016,7 +1029,7 @@ The code interpreter tool can be configured with:
|
|
|
1016
1029
|
|
|
1017
1030
|
#### MCP Tool
|
|
1018
1031
|
|
|
1019
|
-
The OpenAI responses API supports connecting to [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers through the `openai.tools.mcp` tool. This allows models to call tools exposed by remote MCP servers or
|
|
1032
|
+
The OpenAI responses API supports connecting to [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers through the `openai.tools.mcp` tool. This allows models to call tools exposed by remote MCP servers, service connectors, or [Secure MCP tunnels](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels).
|
|
1020
1033
|
|
|
1021
1034
|
```ts
|
|
1022
1035
|
import { openai } from '@ai-sdk/openai';
|
|
@@ -1035,19 +1048,47 @@ const result = await generateText({
|
|
|
1035
1048
|
});
|
|
1036
1049
|
```
|
|
1037
1050
|
|
|
1051
|
+
For a private MCP server exposed through an OpenAI Secure MCP tunnel, provide
|
|
1052
|
+
its tunnel ID instead of a server URL or connector ID. First configure the tunnel
|
|
1053
|
+
in OpenAI and run its tunnel client against your MCP server. The SDK uses the
|
|
1054
|
+
existing tunnel; it does not create or discover one:
|
|
1055
|
+
|
|
1056
|
+
```ts
|
|
1057
|
+
const result = await generateText({
|
|
1058
|
+
model: openai.responses('gpt-5-mini'),
|
|
1059
|
+
prompt: 'Search the private knowledge base',
|
|
1060
|
+
tools: {
|
|
1061
|
+
mcp: openai.tools.mcp({
|
|
1062
|
+
serverLabel: 'private-knowledge',
|
|
1063
|
+
tunnelId: 'tunnel_0123456789abcdef0123456789abcdef',
|
|
1064
|
+
serverDescription: 'A private knowledge base',
|
|
1065
|
+
allowedTools: { readOnly: true },
|
|
1066
|
+
requireApproval: 'always',
|
|
1067
|
+
}),
|
|
1068
|
+
},
|
|
1069
|
+
});
|
|
1070
|
+
```
|
|
1071
|
+
|
|
1072
|
+
When reading a tunnel ID from an environment variable, check that it is defined
|
|
1073
|
+
before passing it to `tunnelId`, which requires a string.
|
|
1074
|
+
|
|
1038
1075
|
The MCP tool can be configured with:
|
|
1039
1076
|
|
|
1040
1077
|
- **serverLabel** _string_ (required)
|
|
1041
1078
|
|
|
1042
1079
|
A label to identify the MCP server. This label is used in tool calls to distinguish between multiple MCP servers.
|
|
1043
1080
|
|
|
1044
|
-
- **serverUrl** _string_ (required if `connectorId`
|
|
1081
|
+
- **serverUrl** _string_ (required if `connectorId` and `tunnelId` are not provided)
|
|
1082
|
+
|
|
1083
|
+
The URL for the MCP server. Cannot be combined with `tunnelId`.
|
|
1084
|
+
|
|
1085
|
+
- **connectorId** _string_ (required if `serverUrl` and `tunnelId` are not provided)
|
|
1045
1086
|
|
|
1046
|
-
|
|
1087
|
+
Identifier for a service connector. Cannot be combined with `tunnelId`.
|
|
1047
1088
|
|
|
1048
|
-
- **
|
|
1089
|
+
- **tunnelId** _string_ (required if `serverUrl` and `connectorId` are not provided)
|
|
1049
1090
|
|
|
1050
|
-
Identifier for a
|
|
1091
|
+
Identifier for a configured OpenAI Secure MCP tunnel. Cannot be combined with `serverUrl` or `connectorId`. At least one target must be provided; existing URL and connector configurations remain compatible.
|
|
1051
1092
|
|
|
1052
1093
|
- **serverDescription** _string_ (optional)
|
|
1053
1094
|
|
|
@@ -1067,7 +1108,7 @@ The MCP tool can be configured with:
|
|
|
1067
1108
|
|
|
1068
1109
|
- **authorization** _string_ (optional)
|
|
1069
1110
|
|
|
1070
|
-
OAuth access token for authenticating with the MCP server or connector.
|
|
1111
|
+
OAuth access token for authenticating with the MCP server or connector (including tunneled servers).
|
|
1071
1112
|
|
|
1072
1113
|
- **headers** _Record<string, string>_ (optional)
|
|
1073
1114
|
|
|
@@ -2501,6 +2542,49 @@ The following optional provider options are available for OpenAI chat models:
|
|
|
2501
2542
|
|
|
2502
2543
|
Force treating this model as a reasoning model. This is useful for "stealth" reasoning models (e.g. via a custom baseURL) where the model ID is not recognized by the SDK's allowlist. When enabled, the SDK applies reasoning-model parameter compatibility rules and defaults `systemMessageMode` to `developer` unless overridden.
|
|
2503
2544
|
|
|
2545
|
+
#### Message Names
|
|
2546
|
+
|
|
2547
|
+
Chat Completions accepts an optional participant name on each system, developer,
|
|
2548
|
+
user, or assistant message. Set `providerOptions.openai.name` on the message to
|
|
2549
|
+
help the model distinguish participants with the same role. Use pseudonyms instead
|
|
2550
|
+
of personal identifiers when possible; names are sent to the provider with the
|
|
2551
|
+
message.
|
|
2552
|
+
|
|
2553
|
+
```ts
|
|
2554
|
+
import { openai, type OpenAIChatMessageProviderOptions } from '@ai-sdk/openai';
|
|
2555
|
+
import { generateText } from 'ai';
|
|
2556
|
+
|
|
2557
|
+
const result = await generateText({
|
|
2558
|
+
model: openai.chat('gpt-4.1-mini'),
|
|
2559
|
+
messages: [
|
|
2560
|
+
{
|
|
2561
|
+
role: 'user',
|
|
2562
|
+
content: 'My favorite color is blue.',
|
|
2563
|
+
providerOptions: {
|
|
2564
|
+
openai: { name: 'alice' } satisfies OpenAIChatMessageProviderOptions,
|
|
2565
|
+
},
|
|
2566
|
+
},
|
|
2567
|
+
{
|
|
2568
|
+
role: 'user',
|
|
2569
|
+
content: 'My favorite color is green. What are our favorite colors?',
|
|
2570
|
+
providerOptions: {
|
|
2571
|
+
openai: { name: 'bob' } satisfies OpenAIChatMessageProviderOptions,
|
|
2572
|
+
},
|
|
2573
|
+
},
|
|
2574
|
+
],
|
|
2575
|
+
});
|
|
2576
|
+
```
|
|
2577
|
+
|
|
2578
|
+
Names also work with `streamText`. Configure them on the message, rather than on
|
|
2579
|
+
individual content parts or in the call-level provider options. Non-string values
|
|
2580
|
+
are rejected during provider option validation. Tool messages do not support
|
|
2581
|
+
participant names: a configured name is omitted with an `unsupported` warning.
|
|
2582
|
+
|
|
2583
|
+
This option applies to Chat Completions through `openai.chat()`, including Azure
|
|
2584
|
+
Chat models using `providerOptions.openai.name`. The default OpenAI Responses
|
|
2585
|
+
model does not support message names. A name identifies a conversation participant;
|
|
2586
|
+
it is separate from the call-level `user` and `safetyIdentifier` options.
|
|
2587
|
+
|
|
2504
2588
|
#### Reasoning
|
|
2505
2589
|
|
|
2506
2590
|
OpenAI has introduced the `o1`,`o3`, and `o4` series of [reasoning models](https://platform.openai.com/docs/guides/reasoning).
|
|
@@ -3164,6 +3248,8 @@ is supported.
|
|
|
3164
3248
|
|
|
3165
3249
|
## Realtime Models
|
|
3166
3250
|
|
|
3251
|
+
OpenAI Realtime also supports trusted [server-owned connections](/docs/ai-sdk-core/realtime#server-owned-connections) through `getServerWebSocketConfig({ intent })`, with conversation and transcription intents and normalized completion usage. Use the same intent for connection setup and event serialization. OpenAI Live retains its conversation-only server connection API and duration snapshots.
|
|
3252
|
+
|
|
3167
3253
|
<Note type="warning">Realtime is an experimental feature.</Note>
|
|
3168
3254
|
|
|
3169
3255
|
Use `.experimental_realtime()` for both [OpenAI Realtime](https://platform.openai.com/docs/guides/realtime)
|
|
@@ -3422,6 +3508,31 @@ const { images } = await generateImage({
|
|
|
3422
3508
|
});
|
|
3423
3509
|
```
|
|
3424
3510
|
|
|
3511
|
+
#### Moderation
|
|
3512
|
+
|
|
3513
|
+
GPT Image models accept a `moderation` level on edits as well as on generations. The default, `auto`, is the standard filtering; `low` applies less restrictive filtering:
|
|
3514
|
+
|
|
3515
|
+
```ts
|
|
3516
|
+
import { readFileSync } from 'node:fs';
|
|
3517
|
+
import { openai, type OpenAIImageModelEditOptions } from '@ai-sdk/openai';
|
|
3518
|
+
import { generateImage } from 'ai';
|
|
3519
|
+
|
|
3520
|
+
const imageBuffer = readFileSync('./input-image.png');
|
|
3521
|
+
|
|
3522
|
+
const { images } = await generateImage({
|
|
3523
|
+
model: openai.image('gpt-image-2.5-flare'),
|
|
3524
|
+
prompt: {
|
|
3525
|
+
text: 'Draw the same scene at dusk',
|
|
3526
|
+
images: [imageBuffer],
|
|
3527
|
+
},
|
|
3528
|
+
providerOptions: {
|
|
3529
|
+
openai: {
|
|
3530
|
+
moderation: 'low',
|
|
3531
|
+
} satisfies OpenAIImageModelEditOptions,
|
|
3532
|
+
},
|
|
3533
|
+
});
|
|
3534
|
+
```
|
|
3535
|
+
|
|
3425
3536
|
#### Multi-Image Combining
|
|
3426
3537
|
|
|
3427
3538
|
Combine multiple reference images into a single output. `gpt-image-*` models support up to 16 input images:
|
|
@@ -3453,6 +3564,8 @@ const { images } = await generateImage({
|
|
|
3453
3564
|
|
|
3454
3565
|
| Model | Sizes |
|
|
3455
3566
|
| ------------------------ | -------------------------------------- |
|
|
3567
|
+
| `gpt-image-2.6-flare` | Standard presets and custom dimensions |
|
|
3568
|
+
| `gpt-image-2.6-sunburst` | Standard presets and custom dimensions |
|
|
3456
3569
|
| `gpt-image-2.5-flare` | Standard presets and custom dimensions |
|
|
3457
3570
|
| `gpt-image-2.5-sunburst` | Standard presets and custom dimensions |
|
|
3458
3571
|
| `gpt-image-2` | 1024x1024, 1536x1024, 1024x1536 |
|
|
@@ -3499,6 +3612,28 @@ const { image } = await generateImage({
|
|
|
3499
3612
|
});
|
|
3500
3613
|
```
|
|
3501
3614
|
|
|
3615
|
+
The `gpt-image-2.6-flare` and `gpt-image-2.6-sunburst` models support these additional options:
|
|
3616
|
+
|
|
3617
|
+
- **complexity** _'low' | 'medium' | 'high'_ - how much detail the model puts into the image (generation and editing). The API defaults to `high`.
|
|
3618
|
+
- **outputFormat: 'gif'** _(generation only)_ - generates an animated GIF. Use **frameCount** (2-64, API default 9) and **frameDurationMs** (20-2000, API default 200) to control the animation.
|
|
3619
|
+
- **markup** _string_ _(editing only)_ - a transparent PNG (HTTPS URL or base64 data URL) overlaid on the first input image to mark up the regions to change. Supported by `gpt-image-2.5-*` and `gpt-image-2.6-*` models. It cannot be combined with `mask`; providing both throws an error.
|
|
3620
|
+
|
|
3621
|
+
```ts
|
|
3622
|
+
const { image } = await generateImage({
|
|
3623
|
+
model: openai.image('gpt-image-2.6-flare'),
|
|
3624
|
+
prompt: 'A red panda waving hello, flat illustration',
|
|
3625
|
+
providerOptions: {
|
|
3626
|
+
openai: {
|
|
3627
|
+
outputFormat: 'gif',
|
|
3628
|
+
frameCount: 9,
|
|
3629
|
+
frameDurationMs: 200,
|
|
3630
|
+
} satisfies OpenAIImageModelGenerationOptions,
|
|
3631
|
+
},
|
|
3632
|
+
});
|
|
3633
|
+
```
|
|
3634
|
+
|
|
3635
|
+
All image options also accept `safetyIdentifier` (max 128 characters) to help OpenAI detect abuse by your end users.
|
|
3636
|
+
|
|
3502
3637
|
For more on `generateImage()` see [Image Generation](/docs/ai-sdk-core/image-generation).
|
|
3503
3638
|
|
|
3504
3639
|
OpenAI's image models return additional metadata in the response that can be
|
|
@@ -3569,27 +3704,30 @@ The API validates model support and language codes. These two options currently
|
|
|
3569
3704
|
apply to REST transcription; the provider reports unsupported-option warnings
|
|
3570
3705
|
if they are supplied to `experimental_streamTranscribe`.
|
|
3571
3706
|
|
|
3572
|
-
To get word
|
|
3707
|
+
To get word and caption timings together, use the shared setting with a
|
|
3708
|
+
model that supports `verbose_json`, such as Whisper:
|
|
3573
3709
|
|
|
3574
|
-
```ts
|
|
3710
|
+
```ts
|
|
3575
3711
|
import { transcribe } from 'ai';
|
|
3576
|
-
import { openai
|
|
3712
|
+
import { openai } from '@ai-sdk/openai';
|
|
3577
3713
|
|
|
3578
3714
|
const result = await transcribe({
|
|
3579
3715
|
model: openai.transcription('whisper-1'),
|
|
3580
3716
|
audio: new Uint8Array([1, 2, 3, 4]),
|
|
3581
|
-
|
|
3582
|
-
openai: {
|
|
3583
|
-
//timestampGranularities: ['word'],
|
|
3584
|
-
timestampGranularities: ['segment'],
|
|
3585
|
-
} satisfies OpenAITranscriptionModelOptions,
|
|
3586
|
-
},
|
|
3717
|
+
timestampGranularities: ['word', 'segment'],
|
|
3587
3718
|
});
|
|
3588
3719
|
|
|
3589
|
-
|
|
3590
|
-
console.log(result.segments);
|
|
3720
|
+
console.log(result.words);
|
|
3721
|
+
console.log(result.segments);
|
|
3591
3722
|
```
|
|
3592
3723
|
|
|
3724
|
+
`providerOptions.openai.timestampGranularities` takes precedence over the shared
|
|
3725
|
+
setting. If neither is set, `verbose_json` defaults to segment timings.
|
|
3726
|
+
`timestamp_granularities` is sent only for `verbose_json`; other formats warn
|
|
3727
|
+
about unavailable requested timings. `diarized_json` provides segment timings
|
|
3728
|
+
but does not provide word timings. Existing segment behavior is preserved:
|
|
3729
|
+
if the response has words but no segments, `segments` also contains those words.
|
|
3730
|
+
|
|
3593
3731
|
`gpt-4o-transcribe-diarize` identifies speakers in a recording. It defaults to
|
|
3594
3732
|
the `diarized_json` response format and automatic chunking:
|
|
3595
3733
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/openai",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.109",
|
|
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.36",
|
|
39
|
+
"@ai-sdk/provider-utils": "5.0.75"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@ai-sdk/test-server": "2.0.4",
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from '@ai-sdk/provider';
|
|
7
7
|
import type { OpenAIChatPrompt } from './openai-chat-prompt';
|
|
8
8
|
import {
|
|
9
|
+
parseProviderOptions,
|
|
9
10
|
secureJsonParse,
|
|
10
11
|
stringifyWithBigIntAndCycles,
|
|
11
12
|
convertToBase64,
|
|
@@ -14,6 +15,8 @@ import {
|
|
|
14
15
|
resolveProviderReference,
|
|
15
16
|
} from '@ai-sdk/provider-utils';
|
|
16
17
|
|
|
18
|
+
import { openaiChatMessageProviderOptions } from './openai-chat-language-model-options';
|
|
19
|
+
|
|
17
20
|
function serializeToolCallArguments(input: unknown): string {
|
|
18
21
|
if (typeof input === 'string') {
|
|
19
22
|
try {
|
|
@@ -47,20 +50,27 @@ function getPromptCacheBreakpoint(
|
|
|
47
50
|
| undefined;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
export function convertToOpenAIChatMessages({
|
|
53
|
+
export async function convertToOpenAIChatMessages({
|
|
51
54
|
prompt,
|
|
52
55
|
systemMessageMode = 'system',
|
|
53
56
|
}: {
|
|
54
57
|
prompt: LanguageModelV4Prompt;
|
|
55
58
|
systemMessageMode?: 'system' | 'developer' | 'remove';
|
|
56
|
-
}): {
|
|
59
|
+
}): Promise<{
|
|
57
60
|
messages: OpenAIChatPrompt;
|
|
58
61
|
warnings: Array<SharedV4Warning>;
|
|
59
|
-
} {
|
|
62
|
+
}> {
|
|
60
63
|
const messages: OpenAIChatPrompt = [];
|
|
61
64
|
const warnings: Array<SharedV4Warning> = [];
|
|
62
65
|
|
|
63
66
|
for (const { role, content, providerOptions } of prompt) {
|
|
67
|
+
const messageOptions = await parseProviderOptions({
|
|
68
|
+
provider: 'openai',
|
|
69
|
+
providerOptions,
|
|
70
|
+
schema: openaiChatMessageProviderOptions,
|
|
71
|
+
});
|
|
72
|
+
const name = messageOptions?.name;
|
|
73
|
+
|
|
64
74
|
switch (role) {
|
|
65
75
|
case 'system': {
|
|
66
76
|
switch (systemMessageMode) {
|
|
@@ -69,6 +79,7 @@ export function convertToOpenAIChatMessages({
|
|
|
69
79
|
getPromptCacheBreakpoint(providerOptions);
|
|
70
80
|
messages.push({
|
|
71
81
|
role: 'system',
|
|
82
|
+
...(name != null && { name }),
|
|
72
83
|
content:
|
|
73
84
|
promptCacheBreakpoint == null
|
|
74
85
|
? content
|
|
@@ -87,6 +98,7 @@ export function convertToOpenAIChatMessages({
|
|
|
87
98
|
getPromptCacheBreakpoint(providerOptions);
|
|
88
99
|
messages.push({
|
|
89
100
|
role: 'developer',
|
|
101
|
+
...(name != null && { name }),
|
|
90
102
|
content:
|
|
91
103
|
promptCacheBreakpoint == null
|
|
92
104
|
? content
|
|
@@ -123,12 +135,17 @@ export function convertToOpenAIChatMessages({
|
|
|
123
135
|
content[0].type === 'text' &&
|
|
124
136
|
getPromptCacheBreakpoint(content[0].providerOptions) == null
|
|
125
137
|
) {
|
|
126
|
-
messages.push({
|
|
138
|
+
messages.push({
|
|
139
|
+
role: 'user',
|
|
140
|
+
content: content[0].text,
|
|
141
|
+
...(name != null && { name }),
|
|
142
|
+
});
|
|
127
143
|
break;
|
|
128
144
|
}
|
|
129
145
|
|
|
130
146
|
messages.push({
|
|
131
147
|
role: 'user',
|
|
148
|
+
...(name != null && { name }),
|
|
132
149
|
content: content.map((part, index) => {
|
|
133
150
|
switch (part.type) {
|
|
134
151
|
case 'text': {
|
|
@@ -311,6 +328,7 @@ export function convertToOpenAIChatMessages({
|
|
|
311
328
|
|
|
312
329
|
messages.push({
|
|
313
330
|
role: 'assistant',
|
|
331
|
+
...(name != null && { name }),
|
|
314
332
|
content: hasPromptCacheBreakpoint
|
|
315
333
|
? textParts
|
|
316
334
|
: toolCalls.length > 0
|
|
@@ -323,6 +341,13 @@ export function convertToOpenAIChatMessages({
|
|
|
323
341
|
}
|
|
324
342
|
|
|
325
343
|
case 'tool': {
|
|
344
|
+
if (name != null) {
|
|
345
|
+
warnings.push({
|
|
346
|
+
type: 'unsupported',
|
|
347
|
+
feature: 'message name on tool messages',
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
326
351
|
for (const toolResponse of content) {
|
|
327
352
|
if (toolResponse.type === 'tool-approval-response') {
|
|
328
353
|
continue;
|
|
@@ -232,3 +232,16 @@ export const openaiLanguageModelChatOptions = lazySchema(() =>
|
|
|
232
232
|
export type OpenAILanguageModelChatOptions = InferSchema<
|
|
233
233
|
typeof openaiLanguageModelChatOptions
|
|
234
234
|
>;
|
|
235
|
+
|
|
236
|
+
export const openaiChatMessageProviderOptions = lazySchema(() =>
|
|
237
|
+
zodSchema(
|
|
238
|
+
z.object({
|
|
239
|
+
/** The participant name on a system, developer, user, or assistant message. */
|
|
240
|
+
name: z.string().optional(),
|
|
241
|
+
}),
|
|
242
|
+
),
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
export type OpenAIChatMessageProviderOptions = InferSchema<
|
|
246
|
+
typeof openaiChatMessageProviderOptions
|
|
247
|
+
>;
|
|
@@ -178,16 +178,15 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
const { messages, warnings: messageWarnings } =
|
|
182
|
-
{
|
|
181
|
+
const { messages, warnings: messageWarnings } =
|
|
182
|
+
await convertToOpenAIChatMessages({
|
|
183
183
|
prompt,
|
|
184
184
|
systemMessageMode:
|
|
185
185
|
openaiOptions.systemMessageMode ??
|
|
186
186
|
(isReasoningModel
|
|
187
187
|
? 'developer'
|
|
188
188
|
: modelRequestConfig.systemMessageMode),
|
|
189
|
-
}
|
|
190
|
-
);
|
|
189
|
+
});
|
|
191
190
|
|
|
192
191
|
warnings.push(...messageWarnings);
|
|
193
192
|
|
|
@@ -414,16 +413,17 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
414
413
|
options: LanguageModelV4CallOptions,
|
|
415
414
|
): Promise<LanguageModelV4GenerateResult> {
|
|
416
415
|
const { args: body, warnings } = await this.getArgs(options);
|
|
416
|
+
const url = this.config.url({
|
|
417
|
+
path: '/chat/completions',
|
|
418
|
+
modelId: this.config.requestModelId ?? this.modelId,
|
|
419
|
+
});
|
|
417
420
|
|
|
418
421
|
const {
|
|
419
422
|
responseHeaders,
|
|
420
423
|
value: response,
|
|
421
424
|
rawValue: rawResponse,
|
|
422
425
|
} = await postJsonToApi({
|
|
423
|
-
url
|
|
424
|
-
path: '/chat/completions',
|
|
425
|
-
modelId: this.config.requestModelId ?? this.modelId,
|
|
426
|
-
}),
|
|
426
|
+
url,
|
|
427
427
|
headers: combineHeaders(
|
|
428
428
|
await resolve(this.config.headers),
|
|
429
429
|
options.headers,
|
|
@@ -499,7 +499,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
499
499
|
raw: choice.finish_reason ?? undefined,
|
|
500
500
|
},
|
|
501
501
|
usage: convertOpenAIChatUsage(response.usage),
|
|
502
|
-
request: { body },
|
|
502
|
+
request: { body, url },
|
|
503
503
|
response: {
|
|
504
504
|
...getResponseMetadata(response),
|
|
505
505
|
headers: responseHeaders,
|
|
@@ -705,7 +705,7 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
705
705
|
},
|
|
706
706
|
}),
|
|
707
707
|
),
|
|
708
|
-
request: { body },
|
|
708
|
+
request: { body, url },
|
|
709
709
|
response: { headers: responseHeaders },
|
|
710
710
|
};
|
|
711
711
|
|
|
@@ -8,16 +8,19 @@ export type ChatCompletionMessage =
|
|
|
8
8
|
| ChatCompletionToolMessage;
|
|
9
9
|
|
|
10
10
|
export interface ChatCompletionSystemMessage {
|
|
11
|
+
name?: string;
|
|
11
12
|
role: 'system';
|
|
12
13
|
content: string | Array<ChatCompletionContentPartText>;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export interface ChatCompletionDeveloperMessage {
|
|
17
|
+
name?: string;
|
|
16
18
|
role: 'developer';
|
|
17
19
|
content: string | Array<ChatCompletionContentPartText>;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export interface ChatCompletionUserMessage {
|
|
23
|
+
name?: string;
|
|
21
24
|
role: 'user';
|
|
22
25
|
content: string | Array<ChatCompletionContentPart>;
|
|
23
26
|
}
|
|
@@ -53,6 +56,7 @@ export interface ChatCompletionContentPartFile {
|
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
export interface ChatCompletionAssistantMessage {
|
|
59
|
+
name?: string;
|
|
56
60
|
role: 'assistant';
|
|
57
61
|
content?: string | null | Array<ChatCompletionContentPartText>;
|
|
58
62
|
tool_calls?: Array<ChatCompletionMessageToolCall>;
|
|
@@ -16,6 +16,8 @@ export type OpenAIImageModelId =
|
|
|
16
16
|
| 'gpt-image-1-mini'
|
|
17
17
|
| 'gpt-image-1.5'
|
|
18
18
|
| 'gpt-image-2'
|
|
19
|
+
| 'gpt-image-2.6-flare'
|
|
20
|
+
| 'gpt-image-2.6-sunburst'
|
|
19
21
|
| 'gpt-image-2.5-flare'
|
|
20
22
|
| 'gpt-image-2.5-flare-2026-09-08'
|
|
21
23
|
| 'gpt-image-2.5-sunburst'
|
|
@@ -64,6 +66,12 @@ const baseImageModelOptionsObject = () =>
|
|
|
64
66
|
*/
|
|
65
67
|
outputFormat: z.enum(['png', 'jpeg', 'webp']).optional(),
|
|
66
68
|
|
|
69
|
+
/**
|
|
70
|
+
* How much detail the model puts into the image. Supported by GPT Image
|
|
71
|
+
* 2.6 models. The API defaults to `high`.
|
|
72
|
+
*/
|
|
73
|
+
complexity: z.enum(['low', 'medium', 'high']).optional(),
|
|
74
|
+
|
|
67
75
|
/**
|
|
68
76
|
* Compression level (0-100) for the generated image(s). Applies to the
|
|
69
77
|
* `jpeg` and `webp` output formats.
|
|
@@ -75,6 +83,13 @@ const baseImageModelOptionsObject = () =>
|
|
|
75
83
|
* to monitor and detect abuse.
|
|
76
84
|
*/
|
|
77
85
|
user: z.string().optional(),
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A stable identifier used to help detect users of your application that
|
|
89
|
+
* may be violating OpenAI's usage policies. Should be a hashed username
|
|
90
|
+
* or email address.
|
|
91
|
+
*/
|
|
92
|
+
safetyIdentifier: z.string().max(128).optional(),
|
|
78
93
|
});
|
|
79
94
|
|
|
80
95
|
export const openaiImageModelOptions = lazySchema(() =>
|
|
@@ -95,6 +110,25 @@ export const openaiImageModelGenerationOptions = lazySchema(() =>
|
|
|
95
110
|
*/
|
|
96
111
|
style: z.enum(['vivid', 'natural']).optional(),
|
|
97
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Format in which the generated image(s) are returned. `gif` produces
|
|
115
|
+
* an animated GIF and is supported by GPT Image 2.6 models.
|
|
116
|
+
*/
|
|
117
|
+
outputFormat: z.enum(['png', 'jpeg', 'webp', 'gif']).optional(),
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Number of frames (2-64) of an animated GIF. Only applies to the `gif`
|
|
121
|
+
* output format. The API defaults to 9.
|
|
122
|
+
*/
|
|
123
|
+
frameCount: z.number().int().min(2).max(64).optional(),
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Duration of each frame in milliseconds (20-2000) of an animated GIF.
|
|
127
|
+
* The API rounds it to the nearest 10 ms. Only applies to the `gif`
|
|
128
|
+
* output format. The API defaults to 200.
|
|
129
|
+
*/
|
|
130
|
+
frameDurationMs: z.number().int().min(20).max(2000).optional(),
|
|
131
|
+
|
|
98
132
|
/**
|
|
99
133
|
* Content moderation level for the generated image(s). `low` applies
|
|
100
134
|
* less restrictive filtering.
|
|
@@ -115,6 +149,20 @@ export const openaiImageModelEditOptions = lazySchema(() =>
|
|
|
115
149
|
* Fidelity of the output image(s) to the input image(s).
|
|
116
150
|
*/
|
|
117
151
|
inputFidelity: z.enum(['high', 'low']).optional(),
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* A transparent PNG overlaid on the first input image to mark up the
|
|
155
|
+
* regions to change, as an HTTPS URL or a base64 data URL. It must match
|
|
156
|
+
* the dimensions of the first image. Supported by GPT Image 2.5 and 2.6
|
|
157
|
+
* models. Cannot be combined with a `mask`.
|
|
158
|
+
*/
|
|
159
|
+
markup: z.string().optional(),
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Content moderation level for the edited image(s). `low` applies
|
|
163
|
+
* less restrictive filtering. Supported by GPT Image models.
|
|
164
|
+
*/
|
|
165
|
+
moderation: z.enum(['auto', 'low']).optional(),
|
|
118
166
|
}),
|
|
119
167
|
),
|
|
120
168
|
);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
InvalidArgumentError,
|
|
3
|
+
type ImageModelV4,
|
|
4
|
+
type ImageModelV4File,
|
|
5
|
+
type SharedV4Warning,
|
|
5
6
|
} from '@ai-sdk/provider';
|
|
6
7
|
import {
|
|
7
8
|
resolve,
|
|
@@ -128,6 +129,13 @@ export class OpenAIImageModel implements ImageModelV4 {
|
|
|
128
129
|
schema: openaiImageModelEditOptions,
|
|
129
130
|
})) ?? {};
|
|
130
131
|
|
|
132
|
+
if (mask != null && openaiOptions.markup != null) {
|
|
133
|
+
throw new InvalidArgumentError({
|
|
134
|
+
argument: 'providerOptions.openai.markup',
|
|
135
|
+
message: '`markup` cannot be combined with `mask`.',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
131
139
|
const { value: response, responseHeaders } = await postFormDataToApi({
|
|
132
140
|
url: this.config.url({
|
|
133
141
|
path: '/images/edits',
|
|
@@ -156,14 +164,21 @@ export class OpenAIImageModel implements ImageModelV4 {
|
|
|
156
164
|
),
|
|
157
165
|
),
|
|
158
166
|
mask: mask != null ? await fileToBlob(mask, abortSignal) : undefined,
|
|
167
|
+
markup:
|
|
168
|
+
openaiOptions.markup != null
|
|
169
|
+
? await markupToBlob(openaiOptions.markup, abortSignal)
|
|
170
|
+
: undefined,
|
|
159
171
|
n,
|
|
160
172
|
size,
|
|
161
173
|
quality: openaiOptions.quality,
|
|
162
174
|
background: openaiOptions.background,
|
|
163
175
|
output_format: openaiOptions.outputFormat,
|
|
164
176
|
output_compression: openaiOptions.outputCompression,
|
|
177
|
+
complexity: openaiOptions.complexity,
|
|
165
178
|
input_fidelity: openaiOptions.inputFidelity,
|
|
179
|
+
moderation: openaiOptions.moderation,
|
|
166
180
|
user: openaiOptions.user,
|
|
181
|
+
safety_identifier: openaiOptions.safetyIdentifier,
|
|
167
182
|
}),
|
|
168
183
|
failedResponseHandler: openaiFailedResponseHandler,
|
|
169
184
|
successfulResponseHandler: createJsonResponseHandler(
|
|
@@ -235,7 +250,11 @@ export class OpenAIImageModel implements ImageModelV4 {
|
|
|
235
250
|
moderation: openaiOptions.moderation,
|
|
236
251
|
output_format: openaiOptions.outputFormat,
|
|
237
252
|
output_compression: openaiOptions.outputCompression,
|
|
253
|
+
complexity: openaiOptions.complexity,
|
|
254
|
+
frame_count: openaiOptions.frameCount,
|
|
255
|
+
frame_duration_ms: openaiOptions.frameDurationMs,
|
|
238
256
|
user: openaiOptions.user,
|
|
257
|
+
safety_identifier: openaiOptions.safetyIdentifier,
|
|
239
258
|
...(!hasDefaultResponseFormat(this.modelId)
|
|
240
259
|
? { response_format: 'b64_json' }
|
|
241
260
|
: {}),
|
|
@@ -326,16 +345,42 @@ type OpenAIImageEditInput = {
|
|
|
326
345
|
prompt?: string;
|
|
327
346
|
image: Blob | Blob[];
|
|
328
347
|
mask?: Blob;
|
|
348
|
+
markup?: Blob;
|
|
329
349
|
n?: number;
|
|
330
350
|
size?: `${number}x${number}`;
|
|
331
351
|
quality?: OpenAIImageModelEditOptions['quality'];
|
|
332
352
|
background?: OpenAIImageModelEditOptions['background'];
|
|
333
353
|
output_format?: OpenAIImageModelEditOptions['outputFormat'];
|
|
334
354
|
output_compression?: OpenAIImageModelEditOptions['outputCompression'];
|
|
355
|
+
complexity?: OpenAIImageModelEditOptions['complexity'];
|
|
335
356
|
input_fidelity?: OpenAIImageModelEditOptions['inputFidelity'];
|
|
357
|
+
moderation?: OpenAIImageModelEditOptions['moderation'];
|
|
336
358
|
user?: OpenAIImageModelEditOptions['user'];
|
|
359
|
+
safety_identifier?: OpenAIImageModelEditOptions['safetyIdentifier'];
|
|
337
360
|
};
|
|
338
361
|
|
|
362
|
+
async function markupToBlob(
|
|
363
|
+
markup: string,
|
|
364
|
+
abortSignal: AbortSignal | undefined,
|
|
365
|
+
): Promise<Blob> {
|
|
366
|
+
const dataUrl = /^data:([^;,]*)(;base64)?,(.*)$/s.exec(markup);
|
|
367
|
+
|
|
368
|
+
if (dataUrl == null) {
|
|
369
|
+
return downloadBlob(markup, { abortSignal });
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (dataUrl[2] == null) {
|
|
373
|
+
throw new InvalidArgumentError({
|
|
374
|
+
argument: 'providerOptions.openai.markup',
|
|
375
|
+
message: '`markup` data URLs must be base64 encoded.',
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return new Blob([convertBase64ToUint8Array(dataUrl[3]) as BlobPart], {
|
|
380
|
+
type: dataUrl[1] || 'image/png',
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
339
384
|
async function fileToBlob(
|
|
340
385
|
file: ImageModelV4File | undefined,
|
|
341
386
|
abortSignal: AbortSignal | undefined,
|
package/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type {
|
|
|
22
22
|
export type { OpenAIToolOptions } from './responses/openai-responses-prepare-tools';
|
|
23
23
|
export type {
|
|
24
24
|
OpenAILanguageModelChatOptions,
|
|
25
|
+
OpenAIChatMessageProviderOptions,
|
|
25
26
|
/** @deprecated Use `OpenAILanguageModelChatOptions` instead. */
|
|
26
27
|
OpenAILanguageModelChatOptions as OpenAIChatLanguageModelOptions,
|
|
27
28
|
} from './chat/openai-chat-language-model-options';
|