@ai-sdk/openai 4.0.60 → 4.0.61

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
@@ -2035,27 +2084,37 @@ import { openai } from '@ai-sdk/openai';
2035
2084
  import {
2036
2085
  experimental_getBatchResults as getBatchResults,
2037
2086
  experimental_getBatchStatus as getBatchStatus,
2038
- experimental_startTextBatch as startTextBatch,
2087
+ experimental_startBatch as startBatch,
2039
2088
  } from 'ai';
2040
2089
  import { setTimeout } from 'node:timers/promises';
2041
2090
 
2042
- const model = openai('gpt-4.1-nano');
2091
+ const model = 'gpt-4.1-nano';
2043
2092
 
2044
- const batch = await startTextBatch({
2045
- model,
2093
+ const batch = await startBatch({
2094
+ provider: openai,
2046
2095
  requests: [
2047
- { id: 'capital-france', prompt: 'What is the capital of France?' },
2048
- { id: 'capital-germany', prompt: 'What is the capital of Germany?' },
2096
+ {
2097
+ id: 'capital-france',
2098
+ type: 'text',
2099
+ model,
2100
+ prompt: 'What is the capital of France?',
2101
+ },
2102
+ {
2103
+ id: 'capital-germany',
2104
+ type: 'text',
2105
+ model,
2106
+ prompt: 'What is the capital of Germany?',
2107
+ },
2049
2108
  ],
2050
2109
  });
2051
2110
 
2052
2111
  let status = batch.status;
2053
2112
  while (status === 'pending') {
2054
2113
  await setTimeout(60_000);
2055
- ({ status } = await getBatchStatus({ model, batch }));
2114
+ ({ status } = await getBatchStatus({ provider: openai, batch }));
2056
2115
  }
2057
2116
 
2058
- for await (const item of getBatchResults({ model, batch })) {
2117
+ for await (const item of getBatchResults({ provider: openai, batch })) {
2059
2118
  if (item.status === 'succeeded') {
2060
2119
  console.log(item.id, item.text);
2061
2120
  } else {
@@ -2064,11 +2123,15 @@ for await (const item of getBatchResults({ model, batch })) {
2064
2123
  }
2065
2124
  ```
2066
2125
 
2067
- `startTextBatch` returns a serializable batch reference. Persist this reference
2126
+ `startBatch` returns a serializable batch reference. Persist this reference
2068
2127
  to check the batch status or retrieve its results from another process. Results
2069
2128
  can arrive in a different order from the input requests, so match each result by
2070
2129
  its `id`.
2071
2130
 
2131
+ Each request specifies its `type` and `model`. OpenAI requires every text
2132
+ request in a batch to use the same model and throws before submission when the
2133
+ models differ.
2134
+
2072
2135
  #### Webhooks
2073
2136
 
2074
2137
  <Note>
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.61",
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",
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,
@@ -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),
@@ -273,6 +273,7 @@ export type OpenAIResponsesFunctionCall = {
273
273
  call_id: string;
274
274
  name: string;
275
275
  arguments: string;
276
+ async?: boolean;
276
277
  id?: string;
277
278
  namespace?: string;
278
279
  caller?: OpenAIResponsesToolCaller;
@@ -335,6 +336,7 @@ export type OpenAIResponsesCustomToolCall = {
335
336
  call_id: string;
336
337
  name: string;
337
338
  input: string;
339
+ async?: boolean;
338
340
  };
339
341
 
340
342
  export type OpenAIResponsesCustomToolCallOutput = {
@@ -523,6 +525,7 @@ export type OpenAIResponsesFunctionTool = {
523
525
  name: string;
524
526
  description: string | undefined;
525
527
  parameters: JSONSchema7;
528
+ async?: boolean;
526
529
  strict?: boolean;
527
530
  defer_loading?: boolean;
528
531
  allowed_callers?: Array<'direct' | 'programmatic'>;
@@ -663,6 +666,7 @@ export type OpenAIResponsesTool =
663
666
  type: 'custom';
664
667
  name: string;
665
668
  description?: string;
669
+ async?: boolean;
666
670
  format?:
667
671
  | {
668
672
  type: 'grammar';
@@ -944,6 +948,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
944
948
  call_id: z.string(),
945
949
  name: z.string(),
946
950
  arguments: z.string(),
951
+ async: z.boolean().nullish(),
947
952
  namespace: z.string().nullish(),
948
953
  caller: openaiResponsesToolCallerSchema.nullish(),
949
954
  }),
@@ -1021,6 +1026,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
1021
1026
  call_id: z.string(),
1022
1027
  name: z.string(),
1023
1028
  input: z.string(),
1029
+ async: z.boolean().nullish(),
1024
1030
  }),
1025
1031
  z.object({
1026
1032
  type: z.literal('shell_call'),
@@ -1093,6 +1099,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
1093
1099
  call_id: z.string(),
1094
1100
  name: z.string(),
1095
1101
  arguments: z.string(),
1102
+ async: z.boolean().nullish(),
1096
1103
  status: z.enum(['in_progress', 'completed', 'incomplete']),
1097
1104
  namespace: z.string().nullish(),
1098
1105
  caller: openaiResponsesToolCallerSchema.nullish(),
@@ -1105,6 +1112,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
1105
1112
  call_id: z.string(),
1106
1113
  name: z.string(),
1107
1114
  input: z.string(),
1115
+ async: z.boolean().nullish(),
1108
1116
  status: z.literal('completed'),
1109
1117
  }),
1110
1118
  z.object({
@@ -1593,6 +1601,7 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1593
1601
  name: z.string(),
1594
1602
  arguments: z.string(),
1595
1603
  id: z.string(),
1604
+ async: z.boolean().nullish(),
1596
1605
  namespace: z.string().nullish(),
1597
1606
  caller: openaiResponsesToolCallerSchema.nullish(),
1598
1607
  }),
@@ -1604,6 +1613,7 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1604
1613
  name: z.string(),
1605
1614
  input: z.string(),
1606
1615
  id: z.string(),
1616
+ async: z.boolean().nullish(),
1607
1617
  }),
1608
1618
  openaiResponsesComputerCallSchema,
1609
1619
  z.object({