@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.
@@ -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
- experimental_startTextBatch as startTextBatch,
2097
+ experimental_startBatch as startBatch,
2039
2098
  } from 'ai';
2040
2099
  import { setTimeout } from 'node:timers/promises';
2041
2100
 
2042
- const model = openai('gpt-4.1-nano');
2101
+ const model = 'gpt-4.1-nano';
2043
2102
 
2044
- const batch = await startTextBatch({
2045
- model,
2103
+ const batch = await startBatch({
2104
+ provider: openai,
2046
2105
  requests: [
2047
- { id: 'capital-france', prompt: 'What is the capital of France?' },
2048
- { id: 'capital-germany', prompt: 'What is the capital of Germany?' },
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({ model, batch }));
2124
+ ({ status } = await getBatchStatus({ provider: openai, batch }));
2056
2125
  }
2057
2126
 
2058
- for await (const item of getBatchResults({ model, batch })) {
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
- `startTextBatch` returns a serializable batch reference. Persist this reference
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 `high`
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.60",
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.10",
39
- "@ai-sdk/provider-utils": "5.0.36"
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 Experimental_BatchLanguageModelV4 as BatchLanguageModelV4,
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 OpenAIBatchRequest = Parameters<
77
- BatchLanguageModelV4['experimental_doStartBatch']
78
- >[0]['requests'][number];
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 OpenAIBatchResponseConversion =
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 OpenAIResponsesBatch {
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
- modelId: string;
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 startBatch(
158
- options: BatchV4StartOptions<OpenAIBatchRequest>,
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.options.prepareRequest(request);
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<OpenAIBatchRequest>['providerOptions'],
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 getBatchStatus(
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 getBatchResults(
322
+ async doGetBatchResults(
318
323
  options: BatchV4OperationOptions,
319
- ): Promise<ReadableStream<BatchV4ItemResult<LanguageModelV4GenerateResult>>> {
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<LanguageModelV4GenerateResult>> {
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<BatchV4ItemResult<LanguageModelV4GenerateResult>> {
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 convertOpenAIResponsesBatchResponse(
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
- modelId: this.options.modelId,
458
- path,
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 convertOpenAIResponsesBatchResponse(
603
+ async function convertOpenAIBatchResult(
620
604
  body: unknown,
621
- ): Promise<OpenAIBatchResponseConversion> {
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,
@@ -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 { OpenAIResponsesBatchLanguageModel } from './openai-responses-batch';
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): BatchLanguageModelV4;
48
+ (modelId: OpenAIResponsesModelId): LanguageModelV4;
48
49
 
49
50
  /**
50
51
  * Creates an OpenAI model for text generation.
51
52
  */
52
- languageModel(modelId: OpenAIResponsesModelId): BatchLanguageModelV4;
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): BatchLanguageModelV4;
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 OpenAIResponsesBatchLanguageModel(modelId, {
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
 
@@ -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 'high'.
80
+ * @param quality - The quality of the generated image. One of 'auto', 'low', 'medium', 'high', 'xhigh', or 'max'. 'xhigh' and 'max' require a GPT Image 2.5 model.
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),