@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.
@@ -94,6 +94,7 @@ import type {
94
94
  ResponsesReasoningProviderMetadata,
95
95
  ResponsesSourceDocumentProviderMetadata,
96
96
  ResponsesTextProviderMetadata,
97
+ ResponsesToolCallProviderMetadata,
97
98
  } from './openai-responses-provider-metadata';
98
99
 
99
100
  /**
@@ -202,6 +203,11 @@ function mapComputerCallInput({
202
203
  };
203
204
  }
204
205
 
206
+ export const openaiResponsesSupportedUrls: Record<string, RegExp[]> = {
207
+ 'image/*': [/^https?:\/\/.*$/],
208
+ 'application/pdf': [/^https?:\/\/.*$/],
209
+ };
210
+
205
211
  export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
206
212
  readonly specificationVersion = 'v4';
207
213
 
@@ -231,33 +237,38 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
231
237
  this.config = config;
232
238
  }
233
239
 
234
- readonly supportedUrls: Record<string, RegExp[]> = {
235
- 'image/*': [/^https?:\/\/.*$/],
236
- 'application/pdf': [/^https?:\/\/.*$/],
237
- };
240
+ readonly supportedUrls = openaiResponsesSupportedUrls;
238
241
 
239
242
  get provider(): string {
240
243
  return this.config.provider;
241
244
  }
242
245
 
243
- protected async getArgs({
244
- maxOutputTokens,
245
- temperature,
246
- stopSequences,
247
- topP,
248
- topK,
249
- presencePenalty,
250
- frequencyPenalty,
251
- seed,
252
- prompt,
253
- reasoning,
254
- providerOptions,
255
- tools,
256
- toolChoice,
257
- responseFormat,
258
- }: LanguageModelV4CallOptions) {
246
+ static async prepareRequest({
247
+ modelId,
248
+ config,
249
+ options: {
250
+ maxOutputTokens,
251
+ temperature,
252
+ stopSequences,
253
+ topP,
254
+ topK,
255
+ presencePenalty,
256
+ frequencyPenalty,
257
+ seed,
258
+ prompt,
259
+ reasoning,
260
+ providerOptions,
261
+ tools,
262
+ toolChoice,
263
+ responseFormat,
264
+ },
265
+ }: {
266
+ modelId: OpenAIResponsesModelId;
267
+ config: OpenAIConfig;
268
+ options: LanguageModelV4CallOptions;
269
+ }) {
259
270
  const warnings: SharedV4Warning[] = [];
260
- const modelCapabilities = getOpenAILanguageModelCapabilities(this.modelId);
271
+ const modelCapabilities = getOpenAILanguageModelCapabilities(modelId);
261
272
 
262
273
  if (topK != null) {
263
274
  warnings.push({ type: 'unsupported', feature: 'topK' });
@@ -279,7 +290,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
279
290
  warnings.push({ type: 'unsupported', feature: 'stopSequences' });
280
291
  }
281
292
 
282
- const providerOptionsName = this.config.provider.includes('azure')
293
+ const providerOptionsName = config.provider.includes('azure')
283
294
  ? 'azure'
284
295
  : 'openai';
285
296
  let openaiOptions = await parseProviderOptions({
@@ -310,7 +321,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
310
321
  warnings.push({
311
322
  type: 'unsupported',
312
323
  feature: 'reasoningEffort',
313
- details: `${this.modelId} only supports the following reasoning efforts: ${modelCapabilities.supportedReasoningEfforts.join(', ')}`,
324
+ details: `${modelId} only supports the following reasoning efforts: ${modelCapabilities.supportedReasoningEfforts.join(', ')}`,
314
325
  });
315
326
  resolvedReasoningEffort = undefined;
316
327
  }
@@ -367,6 +378,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
367
378
  toolNameMapping,
368
379
  customProviderToolNames,
369
380
  outputSchemaToolNames,
381
+ supportsAsyncToolCalling: modelCapabilities.supportsAsyncToolCalling,
370
382
  });
371
383
 
372
384
  const { input, warnings: inputWarnings } =
@@ -379,7 +391,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
379
391
  ? 'developer'
380
392
  : modelCapabilities.systemMessageMode),
381
393
  providerOptionsName,
382
- fileIdPrefixes: this.config.fileIdPrefixes,
394
+ fileIdPrefixes: config.fileIdPrefixes,
383
395
  passThroughUnsupportedFiles:
384
396
  openaiOptions?.passThroughUnsupportedFiles ?? false,
385
397
  store: openaiOptions?.store ?? true,
@@ -490,7 +502,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
490
502
  }
491
503
 
492
504
  const baseArgs = {
493
- model: this.modelId,
505
+ model: modelId,
494
506
  input,
495
507
  temperature,
496
508
  top_p: topP,
@@ -717,6 +729,14 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
717
729
  };
718
730
  }
719
731
 
732
+ private getArgs(options: LanguageModelV4CallOptions) {
733
+ return OpenAIResponsesLanguageModel.prepareRequest({
734
+ modelId: this.modelId,
735
+ config: this.config,
736
+ options,
737
+ });
738
+ }
739
+
720
740
  async doGenerate(
721
741
  options: LanguageModelV4CallOptions,
722
742
  ): Promise<LanguageModelV4GenerateResult> {
@@ -1069,6 +1089,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1069
1089
  providerMetadata: {
1070
1090
  [providerOptionsName]: {
1071
1091
  itemId: part.id,
1092
+ ...(part.async != null && { async: part.async }),
1072
1093
  ...(part.namespace != null && { namespace: part.namespace }),
1073
1094
  ...(part.caller != null && {
1074
1095
  caller:
@@ -1079,7 +1100,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1079
1100
  }
1080
1101
  : part.caller,
1081
1102
  }),
1082
- },
1103
+ } satisfies ResponsesToolCallProviderMetadata,
1083
1104
  },
1084
1105
  });
1085
1106
  break;
@@ -1138,7 +1159,8 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1138
1159
  providerMetadata: {
1139
1160
  [providerOptionsName]: {
1140
1161
  itemId: part.id,
1141
- },
1162
+ ...(part.async != null && { async: part.async }),
1163
+ } satisfies ResponsesToolCallProviderMetadata,
1142
1164
  },
1143
1165
  });
1144
1166
  break;
@@ -1485,6 +1507,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1485
1507
  toolSearchExecution?: 'server' | 'client';
1486
1508
  suppressInputStreaming?: boolean;
1487
1509
  bufferedInputDeltas?: string[];
1510
+ async?: boolean | null;
1488
1511
  }
1489
1512
  | undefined
1490
1513
  > = {};
@@ -1578,6 +1601,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1578
1601
  toolCallId: value.item.call_id,
1579
1602
  suppressInputStreaming,
1580
1603
  bufferedInputDeltas: suppressInputStreaming ? [] : undefined,
1604
+ async: value.item.async,
1581
1605
  };
1582
1606
 
1583
1607
  if (!suppressInputStreaming) {
@@ -1594,6 +1618,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1594
1618
  ongoingToolCalls[value.output_index] = {
1595
1619
  toolName,
1596
1620
  toolCallId: value.item.call_id,
1621
+ async: value.item.async,
1597
1622
  };
1598
1623
 
1599
1624
  controller.enqueue({
@@ -1884,6 +1909,11 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1884
1909
  providerMetadata: {
1885
1910
  [providerOptionsName]: {
1886
1911
  itemId: item.id,
1912
+ ...(item.async != null
1913
+ ? { async: item.async }
1914
+ : ongoingToolCall?.async != null
1915
+ ? { async: ongoingToolCall.async }
1916
+ : {}),
1887
1917
  ...(item.namespace != null && {
1888
1918
  namespace: item.namespace,
1889
1919
  }),
@@ -1896,7 +1926,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1896
1926
  }
1897
1927
  : item.caller,
1898
1928
  }),
1899
- },
1929
+ } satisfies ResponsesToolCallProviderMetadata,
1900
1930
  },
1901
1931
  });
1902
1932
  };
@@ -1979,6 +2009,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1979
2009
  },
1980
2010
  });
1981
2011
  } else if (value.item.type === 'custom_tool_call') {
2012
+ const ongoingToolCall = ongoingToolCalls[value.output_index];
1982
2013
  ongoingToolCalls[value.output_index] = undefined;
1983
2014
  hasFunctionCall = true;
1984
2015
  const toolName = toolNameMapping.toCustomToolName(
@@ -1998,7 +2029,12 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1998
2029
  providerMetadata: {
1999
2030
  [providerOptionsName]: {
2000
2031
  itemId: value.item.id,
2001
- },
2032
+ ...(value.item.async != null
2033
+ ? { async: value.item.async }
2034
+ : ongoingToolCall?.async != null
2035
+ ? { async: ongoingToolCall.async }
2036
+ : {}),
2037
+ } satisfies ResponsesToolCallProviderMetadata,
2002
2038
  },
2003
2039
  });
2004
2040
  } else if (value.item.type === 'web_search_call') {
@@ -33,6 +33,11 @@ type AllowedToolResolution =
33
33
 
34
34
  export type OpenAIToolOptions = {
35
35
  allowedCallers?: Array<'direct' | 'programmatic'>;
36
+ /**
37
+ * Whether the model can continue generating after calling this tool without
38
+ * waiting for its result.
39
+ */
40
+ async?: boolean;
36
41
  deferLoading?: boolean;
37
42
  outputSchema?: JSONObject;
38
43
  namespace?: {
@@ -48,6 +53,7 @@ export async function prepareResponsesTools({
48
53
  toolNameMapping,
49
54
  customProviderToolNames,
50
55
  outputSchemaToolNames,
56
+ supportsAsyncToolCalling = true,
51
57
  }: {
52
58
  tools: LanguageModelV4CallOptions['tools'];
53
59
  toolChoice: LanguageModelV4CallOptions['toolChoice'] | undefined;
@@ -58,6 +64,7 @@ export async function prepareResponsesTools({
58
64
  toolNameMapping?: ToolNameMapping;
59
65
  customProviderToolNames?: Set<string>;
60
66
  outputSchemaToolNames?: Set<string>;
67
+ supportsAsyncToolCalling?: boolean;
61
68
  }): Promise<{
62
69
  tools?: Array<OpenAIResponsesTool>;
63
70
  toolChoice?:
@@ -140,6 +147,12 @@ export async function prepareResponsesTools({
140
147
  const openaiFunctionTool = prepareFunctionTool({
141
148
  tool,
142
149
  options: openaiOptions,
150
+ async: resolveAsyncToolOption({
151
+ value: openaiOptions?.async,
152
+ supportsAsyncToolCalling,
153
+ toolName: tool.name,
154
+ toolWarnings,
155
+ }),
143
156
  });
144
157
  const namespace = openaiOptions?.namespace;
145
158
 
@@ -378,6 +391,14 @@ export async function prepareResponsesTools({
378
391
  type: 'custom',
379
392
  name: tool.name,
380
393
  description: args.description,
394
+ ...(resolveAsyncToolOption({
395
+ value: args.async,
396
+ supportsAsyncToolCalling,
397
+ toolName: tool.name,
398
+ toolWarnings,
399
+ }) != null
400
+ ? { async: args.async }
401
+ : {}),
381
402
  format: args.format,
382
403
  });
383
404
  resolvedCustomProviderToolNames.add(tool.name);
@@ -606,9 +627,11 @@ function toAllowedToolResolution(
606
627
  function prepareFunctionTool({
607
628
  tool,
608
629
  options,
630
+ async,
609
631
  }: {
610
632
  tool: LanguageModelV4FunctionTool;
611
633
  options: OpenAIToolOptions | undefined;
634
+ async: boolean | undefined;
612
635
  }): OpenAIResponsesFunctionTool {
613
636
  const deferLoading = options?.deferLoading;
614
637
 
@@ -617,6 +640,7 @@ function prepareFunctionTool({
617
640
  name: tool.name,
618
641
  description: tool.description,
619
642
  parameters: tool.inputSchema,
643
+ ...(async != null ? { async } : {}),
620
644
  ...(tool.strict != null ? { strict: tool.strict } : {}),
621
645
  ...(deferLoading != null ? { defer_loading: deferLoading } : {}),
622
646
  ...(options?.allowedCallers != null
@@ -628,6 +652,29 @@ function prepareFunctionTool({
628
652
  };
629
653
  }
630
654
 
655
+ function resolveAsyncToolOption({
656
+ value,
657
+ supportsAsyncToolCalling,
658
+ toolName,
659
+ toolWarnings,
660
+ }: {
661
+ value: boolean | undefined;
662
+ supportsAsyncToolCalling: boolean;
663
+ toolName: string;
664
+ toolWarnings: SharedV4Warning[];
665
+ }): boolean | undefined {
666
+ if (value !== true || supportsAsyncToolCalling) {
667
+ return value;
668
+ }
669
+
670
+ toolWarnings.push({
671
+ type: 'unsupported',
672
+ feature: `async tool calling for "${toolName}"`,
673
+ details: 'Async tool calling is only supported by GPT-6 and later models.',
674
+ });
675
+ return undefined;
676
+ }
677
+
631
678
  function mapShellEnvironment(environment: {
632
679
  type?: string;
633
680
  [key: string]: unknown;
@@ -31,6 +31,22 @@ export type OpenaiResponsesProviderMetadata = {
31
31
  openai: ResponsesProviderMetadata;
32
32
  };
33
33
 
34
+ export type ResponsesToolCallProviderMetadata = {
35
+ itemId: string;
36
+ async?: boolean;
37
+ namespace?: string;
38
+ caller?:
39
+ | { type: 'direct' }
40
+ | {
41
+ type: 'program';
42
+ callerId: string;
43
+ };
44
+ };
45
+
46
+ export type OpenaiResponsesToolCallProviderMetadata = {
47
+ openai: ResponsesToolCallProviderMetadata;
48
+ };
49
+
34
50
  export type ResponsesCompactionProviderMetadata = {
35
51
  type: 'compaction';
36
52
  itemId: string;
@@ -9,6 +9,7 @@ export const customArgsSchema = lazySchema(() =>
9
9
  zodSchema(
10
10
  z.object({
11
11
  description: z.string().optional(),
12
+ async: z.boolean().optional(),
12
13
  format: z
13
14
  .union([
14
15
  z.object({
@@ -35,6 +36,12 @@ export const customToolFactory = createProviderDefinedToolFactory<
35
36
  */
36
37
  description?: string;
37
38
 
39
+ /**
40
+ * Whether the model can continue generating after calling this tool
41
+ * without waiting for its result.
42
+ */
43
+ async?: boolean;
44
+
38
45
  /**
39
46
  * The output format specification for the tool.
40
47
  * Omit for unconstrained text output.