@ai-sdk/openai 4.0.60 → 4.0.62
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 +20 -0
- package/dist/index.d.ts +45 -10
- package/dist/index.js +204 -137
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +38 -9
- package/dist/internal/index.js +113 -49
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +96 -10
- package/package.json +3 -3
- package/src/image/openai-image-model-options.ts +3 -2
- package/src/index.ts +1 -0
- package/src/{openai-responses-batch.ts → openai-batch.ts} +71 -87
- package/src/openai-language-model-capabilities.ts +2 -0
- package/src/openai-provider.ts +27 -6
- package/src/openai-tools.ts +2 -1
- package/src/responses/convert-to-openai-responses-input.ts +13 -0
- package/src/responses/openai-responses-api.ts +11 -1
- package/src/responses/openai-responses-language-model.ts +65 -29
- package/src/responses/openai-responses-prepare-tools.ts +47 -0
- package/src/responses/openai-responses-provider-metadata.ts +16 -0
- package/src/tool/custom.ts +7 -0
- package/src/tool/image-generation.ts +6 -3
package/docs/03-openai.mdx
CHANGED
|
@@ -528,6 +528,55 @@ metadata on tool-call parts. The SDK uses `providerMetadata.openai.namespace` or
|
|
|
528
528
|
`providerOptions.openai.namespace` to round-trip the namespace back to OpenAI on
|
|
529
529
|
subsequent requests.
|
|
530
530
|
|
|
531
|
+
#### Async Tool Calling
|
|
532
|
+
|
|
533
|
+
GPT-6 Astra and later Responses models support
|
|
534
|
+
[async tool calling](https://developers.openai.com/api/docs/guides/async-tool-calling).
|
|
535
|
+
An async tool lets the model continue generating independent output after issuing
|
|
536
|
+
the call instead of waiting for its result. Your application still executes the
|
|
537
|
+
tool and sends its result in a later request using the original tool call ID.
|
|
538
|
+
|
|
539
|
+
Enable async calling on a function tool with `providerOptions.openai.async`:
|
|
540
|
+
|
|
541
|
+
```ts
|
|
542
|
+
import { openai, type OpenAIToolOptions } from '@ai-sdk/openai';
|
|
543
|
+
import { generateText, tool } from 'ai';
|
|
544
|
+
import { z } from 'zod';
|
|
545
|
+
|
|
546
|
+
const result = await generateText({
|
|
547
|
+
model: openai.responses('gpt-6-astra'),
|
|
548
|
+
tools: {
|
|
549
|
+
getWeather: tool({
|
|
550
|
+
description: 'Get the weather for a city.',
|
|
551
|
+
inputSchema: z.object({ city: z.string() }),
|
|
552
|
+
outputSchema: z.object({
|
|
553
|
+
city: z.string(),
|
|
554
|
+
temperatureC: z.number(),
|
|
555
|
+
}),
|
|
556
|
+
providerOptions: {
|
|
557
|
+
openai: { async: true } satisfies OpenAIToolOptions,
|
|
558
|
+
},
|
|
559
|
+
}),
|
|
560
|
+
},
|
|
561
|
+
prompt:
|
|
562
|
+
'Start the weather lookup for Paris, then list three general packing essentials without waiting.',
|
|
563
|
+
});
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
The generated tool call exposes the provider marker as
|
|
567
|
+
`providerMetadata.openai.async`. Use `providerMetadata.openai.responseId` as the
|
|
568
|
+
next request's `previousResponseId`, and submit the result in a tool message with
|
|
569
|
+
the original `toolCallId`.
|
|
570
|
+
|
|
571
|
+
With `streamText`, OpenAI can continue streaming text after the completed
|
|
572
|
+
`tool-call` part. Use the tool's `onInputAvailable` callback to start work as soon
|
|
573
|
+
as that part arrives. If `execute` returns the same already-running promise, tool
|
|
574
|
+
execution overlaps the rest of the model stream. Omit `execute` when the job
|
|
575
|
+
should outlive the current generation and submit its result in a later request.
|
|
576
|
+
|
|
577
|
+
Async calling applies to directly called function and custom tools. It does not
|
|
578
|
+
apply to hosted tools and should not be combined with programmatic tool calling.
|
|
579
|
+
|
|
531
580
|
#### Programmatic Tool Calling
|
|
532
581
|
|
|
533
582
|
OpenAI Programmatic Tool Calling lets supported Responses models generate and run
|
|
@@ -778,6 +827,16 @@ for await (const part of result.stream) {
|
|
|
778
827
|
setting `store: false`.
|
|
779
828
|
</Note>
|
|
780
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
|
+
|
|
781
840
|
For complete details on model availability, image quality controls, supported sizes, and tool-specific parameters,
|
|
782
841
|
refer to the OpenAI documentation:
|
|
783
842
|
|
|
@@ -2035,27 +2094,37 @@ import { openai } from '@ai-sdk/openai';
|
|
|
2035
2094
|
import {
|
|
2036
2095
|
experimental_getBatchResults as getBatchResults,
|
|
2037
2096
|
experimental_getBatchStatus as getBatchStatus,
|
|
2038
|
-
|
|
2097
|
+
experimental_startBatch as startBatch,
|
|
2039
2098
|
} from 'ai';
|
|
2040
2099
|
import { setTimeout } from 'node:timers/promises';
|
|
2041
2100
|
|
|
2042
|
-
const model =
|
|
2101
|
+
const model = 'gpt-4.1-nano';
|
|
2043
2102
|
|
|
2044
|
-
const batch = await
|
|
2045
|
-
|
|
2103
|
+
const batch = await startBatch({
|
|
2104
|
+
provider: openai,
|
|
2046
2105
|
requests: [
|
|
2047
|
-
{
|
|
2048
|
-
|
|
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
|
+
},
|
|
2049
2118
|
],
|
|
2050
2119
|
});
|
|
2051
2120
|
|
|
2052
2121
|
let status = batch.status;
|
|
2053
2122
|
while (status === 'pending') {
|
|
2054
2123
|
await setTimeout(60_000);
|
|
2055
|
-
({ status } = await getBatchStatus({
|
|
2124
|
+
({ status } = await getBatchStatus({ provider: openai, batch }));
|
|
2056
2125
|
}
|
|
2057
2126
|
|
|
2058
|
-
for await (const item of getBatchResults({
|
|
2127
|
+
for await (const item of getBatchResults({ provider: openai, batch })) {
|
|
2059
2128
|
if (item.status === 'succeeded') {
|
|
2060
2129
|
console.log(item.id, item.text);
|
|
2061
2130
|
} else {
|
|
@@ -2064,11 +2133,15 @@ for await (const item of getBatchResults({ model, batch })) {
|
|
|
2064
2133
|
}
|
|
2065
2134
|
```
|
|
2066
2135
|
|
|
2067
|
-
`
|
|
2136
|
+
`startBatch` returns a serializable batch reference. Persist this reference
|
|
2068
2137
|
to check the batch status or retrieve its results from another process. Results
|
|
2069
2138
|
can arrive in a different order from the input requests, so match each result by
|
|
2070
2139
|
its `id`.
|
|
2071
2140
|
|
|
2141
|
+
Each request specifies its `type` and `model`. OpenAI requires every text
|
|
2142
|
+
request in a batch to use the same model and throws before submission when the
|
|
2143
|
+
models differ.
|
|
2144
|
+
|
|
2072
2145
|
#### Webhooks
|
|
2073
2146
|
|
|
2074
2147
|
<Note>
|
|
@@ -3023,6 +3096,19 @@ const { image, providerMetadata } = await generateImage({
|
|
|
3023
3096
|
});
|
|
3024
3097
|
```
|
|
3025
3098
|
|
|
3099
|
+
The `gpt-image-2.5-flare` and `gpt-image-2.5-sunburst` models also support
|
|
3100
|
+
`quality: 'xhigh'` and `quality: 'max'` for both image generation and editing:
|
|
3101
|
+
|
|
3102
|
+
```ts
|
|
3103
|
+
const { image } = await generateImage({
|
|
3104
|
+
model: openai.image('gpt-image-2.5-sunburst'),
|
|
3105
|
+
prompt: 'A salamander at sunrise in a forest pond in the Seychelles.',
|
|
3106
|
+
providerOptions: {
|
|
3107
|
+
openai: { quality: 'max' } satisfies OpenAIImageModelGenerationOptions,
|
|
3108
|
+
},
|
|
3109
|
+
});
|
|
3110
|
+
```
|
|
3111
|
+
|
|
3026
3112
|
For more on `generateImage()` see [Image Generation](/docs/ai-sdk-core/image-generation).
|
|
3027
3113
|
|
|
3028
3114
|
OpenAI's image models return additional metadata in the response that can be
|
|
@@ -3035,7 +3121,7 @@ is available:
|
|
|
3035
3121
|
- `revisedPrompt` _string_ - The revised prompt that was actually used to generate the image (OpenAI may modify your prompt for safety or clarity)
|
|
3036
3122
|
- `created` _number_ - The Unix timestamp (in seconds) of when the image was created
|
|
3037
3123
|
- `size` _string_ - The size of the generated image. One of `1024x1024`, `1024x1536`, or `1536x1024`
|
|
3038
|
-
- `quality` _string_ - The quality of the generated image. One of `low`, `medium`, or `
|
|
3124
|
+
- `quality` _string_ - The quality of the generated image. One of `low`, `medium`, `high`, `xhigh`, or `max`
|
|
3039
3125
|
- `background` _string_ - The background parameter used for the image generation. Either `transparent` or `opaque`
|
|
3040
3126
|
- `outputFormat` _string_ - The output format of the generated image. One of `png`, `webp`, or `jpeg`
|
|
3041
3127
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/openai",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.62",
|
|
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.11",
|
|
39
|
+
"@ai-sdk/provider-utils": "5.0.37"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@ai-sdk/test-server": "2.0.1",
|
|
@@ -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
|
@@ -44,6 +44,7 @@ export type {
|
|
|
44
44
|
OpenaiResponsesCompactionProviderMetadata,
|
|
45
45
|
OpenaiResponsesProviderMetadata,
|
|
46
46
|
OpenaiResponsesReasoningProviderMetadata,
|
|
47
|
+
OpenaiResponsesToolCallProviderMetadata,
|
|
47
48
|
OpenaiResponsesTextProviderMetadata,
|
|
48
49
|
OpenaiResponsesSourceDocumentProviderMetadata,
|
|
49
50
|
} from './responses/openai-responses-provider-metadata';
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
InvalidArgumentError,
|
|
3
3
|
InvalidResponseDataError,
|
|
4
|
-
type
|
|
5
|
-
type Experimental_BatchV4StartOptions as BatchV4StartOptions,
|
|
4
|
+
type Experimental_BatchV4 as BatchV4,
|
|
6
5
|
type Experimental_BatchV4StartResult as BatchV4StartResult,
|
|
7
6
|
type Experimental_BatchV4Error as BatchV4Error,
|
|
8
7
|
type Experimental_BatchV4ItemResult as BatchV4ItemResult,
|
|
9
8
|
type Experimental_BatchV4OperationOptions as BatchV4OperationOptions,
|
|
10
9
|
type Experimental_BatchV4Status as BatchV4Status,
|
|
10
|
+
type Experimental_TextBatchV4ItemResult as TextBatchV4ItemResult,
|
|
11
|
+
type Experimental_BatchV4StartOptions as BatchV4StartOptions,
|
|
12
|
+
type Experimental_TextBatchV4Request as TextBatchV4Request,
|
|
11
13
|
type LanguageModelV4GenerateResult,
|
|
12
14
|
type SharedV4ProviderMetadata,
|
|
13
15
|
type SharedV4Warning,
|
|
@@ -24,8 +26,6 @@ import {
|
|
|
24
26
|
postJsonToApi,
|
|
25
27
|
postToApi,
|
|
26
28
|
safeValidateTypes,
|
|
27
|
-
WORKFLOW_DESERIALIZE,
|
|
28
|
-
WORKFLOW_SERIALIZE,
|
|
29
29
|
zodSchema,
|
|
30
30
|
type InferSchema,
|
|
31
31
|
} from '@ai-sdk/provider-utils';
|
|
@@ -34,10 +34,7 @@ import {
|
|
|
34
34
|
openaiErrorDataSchema,
|
|
35
35
|
openaiFailedResponseHandler,
|
|
36
36
|
} from './openai-error';
|
|
37
|
-
import {
|
|
38
|
-
prepareOpenAIConfigForWorkflowDeserialize,
|
|
39
|
-
type OpenAIConfig,
|
|
40
|
-
} from './openai-config';
|
|
37
|
+
import type { OpenAIConfig } from './openai-config';
|
|
41
38
|
import { openaiFilesResponseSchema } from './files/openai-files-api';
|
|
42
39
|
import { convertOpenAIResponsesUsage } from './responses/convert-openai-responses-usage';
|
|
43
40
|
import { mapOpenAIResponseFinishReason } from './responses/map-openai-responses-finish-reason';
|
|
@@ -48,6 +45,7 @@ import {
|
|
|
48
45
|
import {
|
|
49
46
|
mapWebSearchOutput,
|
|
50
47
|
OpenAIResponsesLanguageModel,
|
|
48
|
+
openaiResponsesSupportedUrls,
|
|
51
49
|
} from './responses/openai-responses-language-model';
|
|
52
50
|
import type { OpenAIResponsesModelId } from './responses/openai-responses-language-model-options';
|
|
53
51
|
import type { ResponsesReasoningProviderMetadata } from './responses/openai-responses-provider-metadata';
|
|
@@ -73,16 +71,18 @@ const openaiBatchProviderOptionsSchema = lazySchema(() =>
|
|
|
73
71
|
),
|
|
74
72
|
);
|
|
75
73
|
|
|
76
|
-
type
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
type OpenAIBatchModelIds = {
|
|
75
|
+
readonly text: OpenAIResponsesModelId;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
type OpenAIBatchRequest = TextBatchV4Request<OpenAIResponsesModelId>;
|
|
79
79
|
|
|
80
80
|
type OpenAIBatchPreparedRequest = {
|
|
81
81
|
body: unknown;
|
|
82
82
|
warnings: SharedV4Warning[];
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
-
type
|
|
85
|
+
type OpenAIBatchResultConversion =
|
|
86
86
|
| { success: true; result: LanguageModelV4GenerateResult }
|
|
87
87
|
| { success: false; error: BatchV4Error };
|
|
88
88
|
|
|
@@ -143,20 +143,25 @@ const openaiBatchResultLineSchema = lazySchema(() =>
|
|
|
143
143
|
|
|
144
144
|
type OpenAIBatchResultLine = InferSchema<typeof openaiBatchResultLineSchema>;
|
|
145
145
|
|
|
146
|
-
class
|
|
146
|
+
export class OpenAIBatch implements BatchV4<OpenAIBatchModelIds> {
|
|
147
|
+
readonly specificationVersion = 'v4' as const;
|
|
148
|
+
readonly provider: string;
|
|
149
|
+
readonly supportedUrls = openaiResponsesSupportedUrls;
|
|
150
|
+
|
|
147
151
|
constructor(
|
|
148
152
|
private readonly options: {
|
|
149
|
-
|
|
153
|
+
provider: string;
|
|
150
154
|
config: OpenAIConfig;
|
|
151
|
-
prepareRequest: (
|
|
152
|
-
request: OpenAIBatchRequest,
|
|
153
|
-
) => PromiseLike<OpenAIBatchPreparedRequest>;
|
|
154
155
|
},
|
|
155
|
-
) {
|
|
156
|
+
) {
|
|
157
|
+
this.provider = options.provider;
|
|
158
|
+
}
|
|
156
159
|
|
|
157
|
-
async
|
|
158
|
-
options: BatchV4StartOptions<
|
|
160
|
+
async doStartBatch(
|
|
161
|
+
options: BatchV4StartOptions<OpenAIBatchModelIds>,
|
|
159
162
|
): Promise<BatchV4StartResult> {
|
|
163
|
+
validateSingleModel(options.requests);
|
|
164
|
+
|
|
160
165
|
const fileParts: string[] = [];
|
|
161
166
|
const warnings: BatchV4StartResult['warnings'] =
|
|
162
167
|
options.webhookUrl == null
|
|
@@ -180,7 +185,7 @@ class OpenAIResponsesBatch {
|
|
|
180
185
|
openaiBatchInputFileDefaultExpiresAfterSeconds;
|
|
181
186
|
|
|
182
187
|
for (const request of options.requests) {
|
|
183
|
-
const preparedRequest = await this.
|
|
188
|
+
const preparedRequest = await this.prepareRequest(request);
|
|
184
189
|
|
|
185
190
|
fileParts.push(
|
|
186
191
|
JSON.stringify({
|
|
@@ -285,7 +290,7 @@ class OpenAIResponsesBatch {
|
|
|
285
290
|
}
|
|
286
291
|
|
|
287
292
|
private async parseBatchProviderOptions(
|
|
288
|
-
providerOptions: BatchV4StartOptions<
|
|
293
|
+
providerOptions: BatchV4StartOptions<OpenAIBatchModelIds>['providerOptions'],
|
|
289
294
|
) {
|
|
290
295
|
const providerOptionsName = this.options.config.provider.includes('azure')
|
|
291
296
|
? 'azure'
|
|
@@ -307,16 +312,16 @@ class OpenAIResponsesBatch {
|
|
|
307
312
|
return batchOptions;
|
|
308
313
|
}
|
|
309
314
|
|
|
310
|
-
async
|
|
315
|
+
async doGetBatchStatus(
|
|
311
316
|
options: BatchV4OperationOptions,
|
|
312
317
|
): Promise<BatchV4Status> {
|
|
313
318
|
const batch = await this.retrieveBatch(options);
|
|
314
319
|
return convertOpenAIBatchStatus(batch);
|
|
315
320
|
}
|
|
316
321
|
|
|
317
|
-
async
|
|
322
|
+
async doGetBatchResults(
|
|
318
323
|
options: BatchV4OperationOptions,
|
|
319
|
-
): Promise<ReadableStream<BatchV4ItemResult
|
|
324
|
+
): Promise<ReadableStream<BatchV4ItemResult>> {
|
|
320
325
|
const batch = await this.retrieveBatch(options);
|
|
321
326
|
|
|
322
327
|
const batchStatus = convertOpenAIBatchStatus(batch);
|
|
@@ -368,7 +373,7 @@ class OpenAIResponsesBatch {
|
|
|
368
373
|
}: {
|
|
369
374
|
fileIds: string[];
|
|
370
375
|
options: BatchV4OperationOptions;
|
|
371
|
-
}): AsyncGenerator<BatchV4ItemResult
|
|
376
|
+
}): AsyncGenerator<BatchV4ItemResult> {
|
|
372
377
|
for (const fileId of fileIds) {
|
|
373
378
|
const { value: lines } = await getFromApi({
|
|
374
379
|
url: this.getUrl(`/files/${encodeURIComponent(fileId)}/content`),
|
|
@@ -393,7 +398,7 @@ class OpenAIResponsesBatch {
|
|
|
393
398
|
|
|
394
399
|
private async convertResultLine(
|
|
395
400
|
line: OpenAIBatchResultLine,
|
|
396
|
-
): Promise<
|
|
401
|
+
): Promise<TextBatchV4ItemResult> {
|
|
397
402
|
if (line.error != null) {
|
|
398
403
|
const error = {
|
|
399
404
|
message: line.error.message,
|
|
@@ -401,18 +406,19 @@ class OpenAIResponsesBatch {
|
|
|
401
406
|
};
|
|
402
407
|
|
|
403
408
|
if (line.error.code === 'batch_cancelled') {
|
|
404
|
-
return { id: line.custom_id, status: 'cancelled', error };
|
|
409
|
+
return { type: 'text', id: line.custom_id, status: 'cancelled', error };
|
|
405
410
|
}
|
|
406
411
|
|
|
407
412
|
if (line.error.code === 'batch_expired') {
|
|
408
|
-
return { id: line.custom_id, status: 'expired', error };
|
|
413
|
+
return { type: 'text', id: line.custom_id, status: 'expired', error };
|
|
409
414
|
}
|
|
410
415
|
|
|
411
|
-
return { id: line.custom_id, status: 'failed', error };
|
|
416
|
+
return { type: 'text', id: line.custom_id, status: 'failed', error };
|
|
412
417
|
}
|
|
413
418
|
|
|
414
419
|
if (line.response == null) {
|
|
415
420
|
return {
|
|
421
|
+
type: 'text',
|
|
416
422
|
id: line.custom_id,
|
|
417
423
|
status: 'failed',
|
|
418
424
|
error: {
|
|
@@ -425,6 +431,7 @@ class OpenAIResponsesBatch {
|
|
|
425
431
|
|
|
426
432
|
if (line.response.status_code < 200 || line.response.status_code >= 300) {
|
|
427
433
|
return {
|
|
434
|
+
type: 'text',
|
|
428
435
|
id: line.custom_id,
|
|
429
436
|
status: 'failed',
|
|
430
437
|
error: await convertOpenAIErrorResponse({
|
|
@@ -434,11 +441,10 @@ class OpenAIResponsesBatch {
|
|
|
434
441
|
};
|
|
435
442
|
}
|
|
436
443
|
|
|
437
|
-
const conversion = await
|
|
438
|
-
line.response.body,
|
|
439
|
-
);
|
|
444
|
+
const conversion = await convertOpenAIBatchResult(line.response.body);
|
|
440
445
|
if (!conversion.success) {
|
|
441
446
|
return {
|
|
447
|
+
type: 'text',
|
|
442
448
|
id: line.custom_id,
|
|
443
449
|
status: 'failed',
|
|
444
450
|
error: conversion.error,
|
|
@@ -446,17 +452,43 @@ class OpenAIResponsesBatch {
|
|
|
446
452
|
}
|
|
447
453
|
|
|
448
454
|
return {
|
|
455
|
+
type: 'text',
|
|
449
456
|
id: line.custom_id,
|
|
450
457
|
status: 'succeeded',
|
|
451
458
|
result: conversion.result,
|
|
452
459
|
};
|
|
453
460
|
}
|
|
454
461
|
|
|
462
|
+
private async prepareRequest(
|
|
463
|
+
request: OpenAIBatchRequest,
|
|
464
|
+
): Promise<OpenAIBatchPreparedRequest> {
|
|
465
|
+
const { args: body, warnings } =
|
|
466
|
+
await OpenAIResponsesLanguageModel.prepareRequest({
|
|
467
|
+
modelId: request.modelId,
|
|
468
|
+
config: this.options.config,
|
|
469
|
+
options: request.options,
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
return { body, warnings };
|
|
473
|
+
}
|
|
474
|
+
|
|
455
475
|
private getUrl(path: string) {
|
|
456
|
-
return this.options.config.url({
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
476
|
+
return this.options.config.url({ path, modelId: '' });
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function validateSingleModel(requests: ReadonlyArray<OpenAIBatchRequest>) {
|
|
481
|
+
const modelId = requests[0]?.modelId;
|
|
482
|
+
|
|
483
|
+
for (const request of requests) {
|
|
484
|
+
if (request.modelId !== modelId) {
|
|
485
|
+
throw new InvalidArgumentError({
|
|
486
|
+
argument: 'requests',
|
|
487
|
+
message:
|
|
488
|
+
'The OpenAI Batch API requires all requests in a batch to use the ' +
|
|
489
|
+
`same model. Found "${modelId}" and "${request.modelId}".`,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
460
492
|
}
|
|
461
493
|
}
|
|
462
494
|
|
|
@@ -468,54 +500,6 @@ const openAIBatchConvertibleProviderToolIds = new Set([
|
|
|
468
500
|
'openai.web_search_preview',
|
|
469
501
|
]);
|
|
470
502
|
|
|
471
|
-
export class OpenAIResponsesBatchLanguageModel
|
|
472
|
-
extends OpenAIResponsesLanguageModel
|
|
473
|
-
implements BatchLanguageModelV4
|
|
474
|
-
{
|
|
475
|
-
private readonly batch: OpenAIResponsesBatch;
|
|
476
|
-
|
|
477
|
-
static [WORKFLOW_SERIALIZE](model: OpenAIResponsesLanguageModel) {
|
|
478
|
-
return OpenAIResponsesLanguageModel[WORKFLOW_SERIALIZE](model);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
static [WORKFLOW_DESERIALIZE](options: {
|
|
482
|
-
modelId: string;
|
|
483
|
-
config: Parameters<typeof prepareOpenAIConfigForWorkflowDeserialize>[0];
|
|
484
|
-
}) {
|
|
485
|
-
return new OpenAIResponsesBatchLanguageModel(
|
|
486
|
-
options.modelId as OpenAIResponsesModelId,
|
|
487
|
-
prepareOpenAIConfigForWorkflowDeserialize(options.config),
|
|
488
|
-
);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
constructor(modelId: OpenAIResponsesModelId, config: OpenAIConfig) {
|
|
492
|
-
super(modelId, config);
|
|
493
|
-
this.batch = new OpenAIResponsesBatch({
|
|
494
|
-
modelId,
|
|
495
|
-
config,
|
|
496
|
-
prepareRequest: async request => {
|
|
497
|
-
const { args: body, warnings } = await this.getArgs(request.options);
|
|
498
|
-
|
|
499
|
-
return { body, warnings };
|
|
500
|
-
},
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
experimental_doStartBatch(
|
|
505
|
-
options: Parameters<BatchLanguageModelV4['experimental_doStartBatch']>[0],
|
|
506
|
-
) {
|
|
507
|
-
return this.batch.startBatch(options);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
experimental_doGetBatchStatus(options: BatchV4OperationOptions) {
|
|
511
|
-
return this.batch.getBatchStatus(options);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
experimental_doGetBatchResults(options: BatchV4OperationOptions) {
|
|
515
|
-
return this.batch.getBatchResults(options);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
503
|
function convertOpenAIBatchStatus(batch: OpenAIBatchResponse): BatchV4Status {
|
|
520
504
|
const status = mapOpenAIBatchStatus(batch.status);
|
|
521
505
|
const firstError = batch.errors?.data?.[0];
|
|
@@ -616,9 +600,9 @@ async function convertOpenAIErrorResponse({
|
|
|
616
600
|
};
|
|
617
601
|
}
|
|
618
602
|
|
|
619
|
-
async function
|
|
603
|
+
async function convertOpenAIBatchResult(
|
|
620
604
|
body: unknown,
|
|
621
|
-
): Promise<
|
|
605
|
+
): Promise<OpenAIBatchResultConversion> {
|
|
622
606
|
const validation = await safeValidateTypes({
|
|
623
607
|
value: body,
|
|
624
608
|
schema: openaiResponsesResponseSchema,
|
|
@@ -4,6 +4,7 @@ export type OpenAILanguageModelCapabilities = {
|
|
|
4
4
|
supportsFlexProcessing: boolean;
|
|
5
5
|
supportsPriorityProcessing: boolean;
|
|
6
6
|
supportsConfigurationUpdate: boolean;
|
|
7
|
+
supportsAsyncToolCalling: boolean;
|
|
7
8
|
supportedReasoningEfforts: readonly string[] | undefined;
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -55,6 +56,7 @@ export function getOpenAILanguageModelCapabilities(
|
|
|
55
56
|
supportsFlexProcessing,
|
|
56
57
|
supportsPriorityProcessing,
|
|
57
58
|
supportsConfigurationUpdate: isGpt6OrLaterModel,
|
|
59
|
+
supportsAsyncToolCalling: isGpt6OrLaterModel,
|
|
58
60
|
supportedReasoningEfforts: isGpt6OrLaterModel
|
|
59
61
|
? ['low', 'medium', 'high', 'xhigh', 'max']
|
|
60
62
|
: undefined,
|
package/src/openai-provider.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
Experimental_BatchV4 as BatchV4,
|
|
2
3
|
EmbeddingModelV4,
|
|
3
|
-
Experimental_BatchLanguageModelV4 as BatchLanguageModelV4,
|
|
4
4
|
FilesV4,
|
|
5
5
|
ImageModelV4,
|
|
6
6
|
LanguageModelV4,
|
|
@@ -31,7 +31,8 @@ import type { OpenAIEmbeddingModelId } from './embedding/openai-embedding-model-
|
|
|
31
31
|
import { OpenAIImageModel } from './image/openai-image-model';
|
|
32
32
|
import type { OpenAIImageModelId } from './image/openai-image-model-options';
|
|
33
33
|
import { openaiTools } from './openai-tools';
|
|
34
|
-
import {
|
|
34
|
+
import { OpenAIBatch } from './openai-batch';
|
|
35
|
+
import { OpenAIResponsesLanguageModel } from './responses/openai-responses-language-model';
|
|
35
36
|
import { OpenAIRealtimeModel } from './realtime/openai-realtime-model';
|
|
36
37
|
import type { OpenAIResponsesModelId } from './responses/openai-responses-language-model-options';
|
|
37
38
|
import { OpenAISpeechModel } from './speech/openai-speech-model';
|
|
@@ -44,12 +45,12 @@ import { OpenAISkills } from './skills/openai-skills';
|
|
|
44
45
|
import { VERSION } from './version';
|
|
45
46
|
|
|
46
47
|
export interface OpenAIProvider extends ProviderV4 {
|
|
47
|
-
(modelId: OpenAIResponsesModelId):
|
|
48
|
+
(modelId: OpenAIResponsesModelId): LanguageModelV4;
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
51
|
* Creates an OpenAI model for text generation.
|
|
51
52
|
*/
|
|
52
|
-
languageModel(modelId: OpenAIResponsesModelId):
|
|
53
|
+
languageModel(modelId: OpenAIResponsesModelId): LanguageModelV4;
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
56
|
* Creates an OpenAI chat model for text generation.
|
|
@@ -59,7 +60,7 @@ export interface OpenAIProvider extends ProviderV4 {
|
|
|
59
60
|
/**
|
|
60
61
|
* Creates an OpenAI responses API model for text generation.
|
|
61
62
|
*/
|
|
62
|
-
responses(modelId: OpenAIResponsesModelId):
|
|
63
|
+
responses(modelId: OpenAIResponsesModelId): LanguageModelV4;
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
66
|
* Creates an OpenAI completion model for text generation.
|
|
@@ -136,6 +137,11 @@ export interface OpenAIProvider extends ProviderV4 {
|
|
|
136
137
|
*/
|
|
137
138
|
skills(): SkillsV4;
|
|
138
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Returns a BatchV4 interface for processing batches with OpenAI.
|
|
142
|
+
*/
|
|
143
|
+
experimental_batch(): BatchV4<{ text: OpenAIResponsesModelId }>;
|
|
144
|
+
|
|
139
145
|
/**
|
|
140
146
|
* OpenAI-specific tools.
|
|
141
147
|
*/
|
|
@@ -306,7 +312,7 @@ export function createOpenAI(
|
|
|
306
312
|
};
|
|
307
313
|
|
|
308
314
|
const createResponsesModel = (modelId: OpenAIResponsesModelId) => {
|
|
309
|
-
return new
|
|
315
|
+
return new OpenAIResponsesLanguageModel(modelId, {
|
|
310
316
|
provider: `${providerName}.responses`,
|
|
311
317
|
baseURL,
|
|
312
318
|
url: ({ path }) => `${baseURL}${path}`,
|
|
@@ -317,6 +323,20 @@ export function createOpenAI(
|
|
|
317
323
|
});
|
|
318
324
|
};
|
|
319
325
|
|
|
326
|
+
const createBatch = () =>
|
|
327
|
+
new OpenAIBatch({
|
|
328
|
+
provider: `${providerName}.batch`,
|
|
329
|
+
config: {
|
|
330
|
+
provider: `${providerName}.responses`,
|
|
331
|
+
baseURL,
|
|
332
|
+
url: ({ path }) => `${baseURL}${path}`,
|
|
333
|
+
headers: getHeaders,
|
|
334
|
+
fetch: options.fetch,
|
|
335
|
+
// Soft-deprecated. TODO: remove in v8
|
|
336
|
+
fileIdPrefixes: ['file-'],
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
|
|
320
340
|
const createRealtimeModel = (modelId: string) =>
|
|
321
341
|
new OpenAIRealtimeModel(modelId, {
|
|
322
342
|
provider: `${providerName}.realtime`,
|
|
@@ -371,6 +391,7 @@ export function createOpenAI(
|
|
|
371
391
|
provider.speechModel = createSpeechModel;
|
|
372
392
|
provider.files = createFiles;
|
|
373
393
|
provider.skills = createSkills;
|
|
394
|
+
provider.experimental_batch = createBatch;
|
|
374
395
|
|
|
375
396
|
provider.experimental_realtime = experimentalRealtimeFactory;
|
|
376
397
|
|
package/src/openai-tools.ts
CHANGED
|
@@ -28,6 +28,7 @@ export const openaiTools = {
|
|
|
28
28
|
* `input` field is a string matching the specified grammar.
|
|
29
29
|
*
|
|
30
30
|
* @param description - An optional description of the tool.
|
|
31
|
+
* @param async - Whether the model can continue without waiting for the tool result.
|
|
31
32
|
* @param format - The output format constraint (grammar type, syntax, and definition).
|
|
32
33
|
*/
|
|
33
34
|
customTool,
|
|
@@ -76,7 +77,7 @@ export const openaiTools = {
|
|
|
76
77
|
* @param outputCompression - Compression level for the output image (0-100).
|
|
77
78
|
* @param outputFormat - The output format of the generated image. One of 'png', 'jpeg', or 'webp'.
|
|
78
79
|
* @param partialImages - Number of partial images to generate in streaming mode (0-3).
|
|
79
|
-
* @param quality - The quality of the generated image. One of 'auto', 'low', 'medium', or '
|
|
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.
|
|
80
81
|
* @param size - The size of the generated image. One of 'auto', '1024x1024', '1024x1536', or '1536x1024'.
|
|
81
82
|
*/
|
|
82
83
|
imageGeneration,
|
|
@@ -677,6 +677,17 @@ export async function convertToOpenAIResponsesInput({
|
|
|
677
677
|
).providerMetadata?.[providerOptionsName]?.namespace) as
|
|
678
678
|
| string
|
|
679
679
|
| undefined;
|
|
680
|
+
const isAsync = (part.providerOptions?.[providerOptionsName]
|
|
681
|
+
?.async ??
|
|
682
|
+
(
|
|
683
|
+
part as {
|
|
684
|
+
providerMetadata?: {
|
|
685
|
+
[providerOptionsName]?: { async?: boolean };
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
).providerMetadata?.[providerOptionsName]?.async) as
|
|
689
|
+
| boolean
|
|
690
|
+
| undefined;
|
|
680
691
|
const caller = part.providerOptions?.[providerOptionsName]
|
|
681
692
|
?.caller as
|
|
682
693
|
| { type: 'direct' }
|
|
@@ -904,6 +915,7 @@ export async function convertToOpenAIResponsesInput({
|
|
|
904
915
|
typeof part.input === 'string'
|
|
905
916
|
? part.input
|
|
906
917
|
: JSON.stringify(part.input),
|
|
918
|
+
...(isAsync != null && { async: isAsync }),
|
|
907
919
|
id,
|
|
908
920
|
});
|
|
909
921
|
break;
|
|
@@ -914,6 +926,7 @@ export async function convertToOpenAIResponsesInput({
|
|
|
914
926
|
call_id: part.toolCallId,
|
|
915
927
|
name: resolvedToolName,
|
|
916
928
|
arguments: serializeToolCallArguments(part.input),
|
|
929
|
+
...(isAsync != null && { async: isAsync }),
|
|
917
930
|
...(namespace != null && { namespace }),
|
|
918
931
|
...(caller != null && {
|
|
919
932
|
caller: mapToolCaller(caller),
|