@ai-sdk/openai 4.0.0-beta.1 → 4.0.0-beta.11

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +145 -0
  2. package/dist/index.d.mts +69 -22
  3. package/dist/index.d.ts +69 -22
  4. package/dist/index.js +1169 -873
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +1123 -822
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/internal/index.d.mts +56 -28
  9. package/dist/internal/index.d.ts +56 -28
  10. package/dist/internal/index.js +1198 -912
  11. package/dist/internal/index.js.map +1 -1
  12. package/dist/internal/index.mjs +1180 -889
  13. package/dist/internal/index.mjs.map +1 -1
  14. package/docs/03-openai.mdx +142 -3
  15. package/package.json +3 -3
  16. package/src/chat/convert-openai-chat-usage.ts +2 -2
  17. package/src/chat/convert-to-openai-chat-messages.ts +5 -5
  18. package/src/chat/map-openai-finish-reason.ts +2 -2
  19. package/src/chat/openai-chat-language-model.ts +22 -22
  20. package/src/chat/openai-chat-options.ts +1 -0
  21. package/src/chat/openai-chat-prepare-tools.ts +6 -6
  22. package/src/completion/convert-openai-completion-usage.ts +2 -2
  23. package/src/completion/convert-to-openai-completion-prompt.ts +2 -2
  24. package/src/completion/map-openai-finish-reason.ts +2 -2
  25. package/src/completion/openai-completion-language-model.ts +20 -20
  26. package/src/embedding/openai-embedding-model.ts +5 -5
  27. package/src/image/openai-image-model.ts +9 -9
  28. package/src/openai-language-model-capabilities.ts +1 -0
  29. package/src/openai-provider.ts +21 -21
  30. package/src/openai-tools.ts +12 -1
  31. package/src/responses/convert-openai-responses-usage.ts +2 -2
  32. package/src/responses/convert-to-openai-responses-input.ts +116 -12
  33. package/src/responses/map-openai-responses-finish-reason.ts +2 -2
  34. package/src/responses/openai-responses-api.ts +87 -1
  35. package/src/responses/openai-responses-language-model.ts +168 -33
  36. package/src/responses/openai-responses-options.ts +4 -2
  37. package/src/responses/openai-responses-prepare-tools.ts +34 -9
  38. package/src/speech/openai-speech-model.ts +7 -7
  39. package/src/tool/custom.ts +0 -6
  40. package/src/tool/tool-search.ts +98 -0
  41. package/src/transcription/openai-transcription-model.ts +8 -8
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  APICallError,
3
3
  JSONValue,
4
- LanguageModelV3,
5
- LanguageModelV3Prompt,
6
- LanguageModelV3CallOptions,
7
- LanguageModelV3Content,
8
- LanguageModelV3FinishReason,
9
- LanguageModelV3GenerateResult,
10
- LanguageModelV3ProviderTool,
11
- LanguageModelV3StreamPart,
12
- LanguageModelV3StreamResult,
13
- LanguageModelV3ToolApprovalRequest,
14
- SharedV3ProviderMetadata,
15
- SharedV3Warning,
4
+ LanguageModelV4,
5
+ LanguageModelV4Prompt,
6
+ LanguageModelV4CallOptions,
7
+ LanguageModelV4Content,
8
+ LanguageModelV4FinishReason,
9
+ LanguageModelV4GenerateResult,
10
+ LanguageModelV4ProviderTool,
11
+ LanguageModelV4StreamPart,
12
+ LanguageModelV4StreamResult,
13
+ LanguageModelV4ToolApprovalRequest,
14
+ SharedV4ProviderMetadata,
15
+ SharedV4Warning,
16
16
  } from '@ai-sdk/provider';
17
17
  import {
18
18
  combineHeaders,
@@ -38,6 +38,10 @@ import { imageGenerationOutputSchema } from '../tool/image-generation';
38
38
  import { localShellInputSchema } from '../tool/local-shell';
39
39
  import { mcpOutputSchema } from '../tool/mcp';
40
40
  import { shellInputSchema, shellOutputSchema } from '../tool/shell';
41
+ import {
42
+ toolSearchInputSchema,
43
+ toolSearchOutputSchema,
44
+ } from '../tool/tool-search';
41
45
  import { webSearchOutputSchema } from '../tool/web-search';
42
46
  import {
43
47
  convertOpenAIResponsesUsage,
@@ -77,7 +81,7 @@ import {
77
81
  * so that tool results reference the correct tool call.
78
82
  */
79
83
  function extractApprovalRequestIdToToolCallIdMapping(
80
- prompt: LanguageModelV3Prompt,
84
+ prompt: LanguageModelV4Prompt,
81
85
  ): Record<string, string> {
82
86
  const mapping: Record<string, string> = {};
83
87
  for (const message of prompt) {
@@ -94,8 +98,8 @@ function extractApprovalRequestIdToToolCallIdMapping(
94
98
  return mapping;
95
99
  }
96
100
 
97
- export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
98
- readonly specificationVersion = 'v3';
101
+ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
102
+ readonly specificationVersion = 'v4';
99
103
 
100
104
  readonly modelId: OpenAIResponsesModelId;
101
105
 
@@ -129,8 +133,8 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
129
133
  tools,
130
134
  toolChoice,
131
135
  responseFormat,
132
- }: LanguageModelV3CallOptions) {
133
- const warnings: SharedV3Warning[] = [];
136
+ }: LanguageModelV4CallOptions) {
137
+ const warnings: SharedV4Warning[] = [];
134
138
  const modelCapabilities = getOpenAILanguageModelCapabilities(this.modelId);
135
139
 
136
140
  if (topK != null) {
@@ -193,11 +197,8 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
193
197
  'openai.web_search_preview': 'web_search_preview',
194
198
  'openai.mcp': 'mcp',
195
199
  'openai.apply_patch': 'apply_patch',
200
+ 'openai.tool_search': 'tool_search',
196
201
  },
197
- resolveProviderToolName: tool =>
198
- tool.id === 'openai.custom'
199
- ? (tool.args as { name?: string }).name
200
- : undefined,
201
202
  });
202
203
 
203
204
  const customProviderToolNames = new Set<string>();
@@ -273,7 +274,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
273
274
  tool.type === 'provider' &&
274
275
  (tool.id === 'openai.web_search' ||
275
276
  tool.id === 'openai.web_search_preview'),
276
- ) as LanguageModelV3ProviderTool | undefined
277
+ ) as LanguageModelV4ProviderTool | undefined
277
278
  )?.name;
278
279
 
279
280
  if (webSearchToolName) {
@@ -454,8 +455,8 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
454
455
  }
455
456
 
456
457
  async doGenerate(
457
- options: LanguageModelV3CallOptions,
458
- ): Promise<LanguageModelV3GenerateResult> {
458
+ options: LanguageModelV4CallOptions,
459
+ ): Promise<LanguageModelV4GenerateResult> {
459
460
  const {
460
461
  args: body,
461
462
  warnings,
@@ -500,11 +501,12 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
500
501
  });
501
502
  }
502
503
 
503
- const content: Array<LanguageModelV3Content> = [];
504
+ const content: Array<LanguageModelV4Content> = [];
504
505
  const logprobs: Array<OpenAIResponsesLogprobs> = [];
505
506
 
506
507
  // flag that checks if there have been client-side tool calls (not executed by openai)
507
508
  let hasFunctionCall = false;
509
+ const hostedToolSearchCallIds: string[] = [];
508
510
 
509
511
  // map response content to content array (defined when there is no error)
510
512
  for (const part of response.output!) {
@@ -551,6 +553,54 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
551
553
  break;
552
554
  }
553
555
 
556
+ case 'tool_search_call': {
557
+ const toolCallId = part.call_id ?? part.id;
558
+ const isHosted = part.execution === 'server';
559
+
560
+ if (isHosted) {
561
+ hostedToolSearchCallIds.push(toolCallId);
562
+ }
563
+
564
+ content.push({
565
+ type: 'tool-call',
566
+ toolCallId,
567
+ toolName: toolNameMapping.toCustomToolName('tool_search'),
568
+ input: JSON.stringify({
569
+ arguments: part.arguments,
570
+ call_id: part.call_id,
571
+ } satisfies InferSchema<typeof toolSearchInputSchema>),
572
+ ...(isHosted ? { providerExecuted: true } : {}),
573
+ providerMetadata: {
574
+ [providerOptionsName]: {
575
+ itemId: part.id,
576
+ },
577
+ },
578
+ });
579
+
580
+ break;
581
+ }
582
+
583
+ case 'tool_search_output': {
584
+ const toolCallId =
585
+ part.call_id ?? hostedToolSearchCallIds.shift() ?? part.id;
586
+
587
+ content.push({
588
+ type: 'tool-result',
589
+ toolCallId,
590
+ toolName: toolNameMapping.toCustomToolName('tool_search'),
591
+ result: {
592
+ tools: part.tools,
593
+ } satisfies InferSchema<typeof toolSearchOutputSchema>,
594
+ providerMetadata: {
595
+ [providerOptionsName]: {
596
+ itemId: part.id,
597
+ },
598
+ },
599
+ });
600
+
601
+ break;
602
+ }
603
+
554
604
  case 'local_shell_call': {
555
605
  content.push({
556
606
  type: 'tool-call',
@@ -621,7 +671,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
621
671
  logprobs.push(contentPart.logprobs);
622
672
  }
623
673
 
624
- const providerMetadata: SharedV3ProviderMetadata[string] = {
674
+ const providerMetadata: SharedV4ProviderMetadata[string] = {
625
675
  itemId: part.id,
626
676
  ...(part.phase != null && { phase: part.phase }),
627
677
  ...(contentPart.annotations.length > 0 && {
@@ -833,7 +883,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
833
883
  type: 'tool-approval-request',
834
884
  approvalId: approvalRequestId,
835
885
  toolCallId: dummyToolCallId,
836
- } satisfies LanguageModelV3ToolApprovalRequest);
886
+ } satisfies LanguageModelV4ToolApprovalRequest);
837
887
  break;
838
888
  }
839
889
 
@@ -930,7 +980,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
930
980
  }
931
981
  }
932
982
 
933
- const providerMetadata: SharedV3ProviderMetadata = {
983
+ const providerMetadata: SharedV4ProviderMetadata = {
934
984
  [providerOptionsName]: {
935
985
  responseId: response.id,
936
986
  ...(logprobs.length > 0 ? { logprobs } : {}),
@@ -966,8 +1016,8 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
966
1016
  }
967
1017
 
968
1018
  async doStream(
969
- options: LanguageModelV3CallOptions,
970
- ): Promise<LanguageModelV3StreamResult> {
1019
+ options: LanguageModelV4CallOptions,
1020
+ ): Promise<LanguageModelV4StreamResult> {
971
1021
  const {
972
1022
  args: body,
973
1023
  warnings,
@@ -1006,7 +1056,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1006
1056
  string
1007
1057
  >();
1008
1058
 
1009
- let finishReason: LanguageModelV3FinishReason = {
1059
+ let finishReason: LanguageModelV4FinishReason = {
1010
1060
  unified: 'other',
1011
1061
  raw: undefined,
1012
1062
  };
@@ -1026,6 +1076,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1026
1076
  hasDiff: boolean;
1027
1077
  endEmitted: boolean;
1028
1078
  };
1079
+ toolSearchExecution?: 'server' | 'client';
1029
1080
  }
1030
1081
  | undefined
1031
1082
  > = {};
@@ -1054,12 +1105,13 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1054
1105
  > = {};
1055
1106
 
1056
1107
  let serviceTier: string | undefined;
1108
+ const hostedToolSearchCallIds: string[] = [];
1057
1109
 
1058
1110
  return {
1059
1111
  stream: response.pipeThrough(
1060
1112
  new TransformStream<
1061
1113
  ParseResult<OpenAIResponsesChunk>,
1062
- LanguageModelV3StreamPart
1114
+ LanguageModelV4StreamPart
1063
1115
  >({
1064
1116
  start(controller) {
1065
1117
  controller.enqueue({ type: 'stream-start', warnings });
@@ -1188,6 +1240,28 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1188
1240
  input: '{}',
1189
1241
  providerExecuted: true,
1190
1242
  });
1243
+ } else if (value.item.type === 'tool_search_call') {
1244
+ const toolCallId = value.item.id;
1245
+ const toolName =
1246
+ toolNameMapping.toCustomToolName('tool_search');
1247
+ const isHosted = value.item.execution === 'server';
1248
+
1249
+ ongoingToolCalls[value.output_index] = {
1250
+ toolName,
1251
+ toolCallId,
1252
+ toolSearchExecution: value.item.execution ?? 'server',
1253
+ };
1254
+
1255
+ if (isHosted) {
1256
+ controller.enqueue({
1257
+ type: 'tool-input-start',
1258
+ id: toolCallId,
1259
+ toolName,
1260
+ providerExecuted: true,
1261
+ });
1262
+ }
1263
+ } else if (value.item.type === 'tool_search_output') {
1264
+ // handled on output_item.done so we can pair it with the call
1191
1265
  } else if (
1192
1266
  value.item.type === 'mcp_call' ||
1193
1267
  value.item.type === 'mcp_list_tools' ||
@@ -1418,6 +1492,67 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1418
1492
  result: value.item.result,
1419
1493
  } satisfies InferSchema<typeof imageGenerationOutputSchema>,
1420
1494
  });
1495
+ } else if (value.item.type === 'tool_search_call') {
1496
+ const toolCall = ongoingToolCalls[value.output_index];
1497
+ const isHosted = value.item.execution === 'server';
1498
+
1499
+ if (toolCall != null) {
1500
+ const toolCallId = isHosted
1501
+ ? toolCall.toolCallId
1502
+ : (value.item.call_id ?? value.item.id);
1503
+
1504
+ if (isHosted) {
1505
+ hostedToolSearchCallIds.push(toolCallId);
1506
+ } else {
1507
+ controller.enqueue({
1508
+ type: 'tool-input-start',
1509
+ id: toolCallId,
1510
+ toolName: toolCall.toolName,
1511
+ });
1512
+ }
1513
+
1514
+ controller.enqueue({
1515
+ type: 'tool-input-end',
1516
+ id: toolCallId,
1517
+ });
1518
+
1519
+ controller.enqueue({
1520
+ type: 'tool-call',
1521
+ toolCallId,
1522
+ toolName: toolCall.toolName,
1523
+ input: JSON.stringify({
1524
+ arguments: value.item.arguments,
1525
+ call_id: isHosted ? null : toolCallId,
1526
+ } satisfies InferSchema<typeof toolSearchInputSchema>),
1527
+ ...(isHosted ? { providerExecuted: true } : {}),
1528
+ providerMetadata: {
1529
+ [providerOptionsName]: {
1530
+ itemId: value.item.id,
1531
+ },
1532
+ },
1533
+ });
1534
+ }
1535
+
1536
+ ongoingToolCalls[value.output_index] = undefined;
1537
+ } else if (value.item.type === 'tool_search_output') {
1538
+ const toolCallId =
1539
+ value.item.call_id ??
1540
+ hostedToolSearchCallIds.shift() ??
1541
+ value.item.id;
1542
+
1543
+ controller.enqueue({
1544
+ type: 'tool-result',
1545
+ toolCallId,
1546
+ toolName: toolNameMapping.toCustomToolName('tool_search'),
1547
+ result: {
1548
+ tools: value.item.tools,
1549
+ } satisfies InferSchema<typeof toolSearchOutputSchema>,
1550
+ providerMetadata: {
1551
+ [providerOptionsName]: {
1552
+ itemId: value.item.id,
1553
+ },
1554
+ },
1555
+ });
1421
1556
  } else if (value.item.type === 'mcp_call') {
1422
1557
  ongoingToolCalls[value.output_index] = undefined;
1423
1558
 
@@ -1941,7 +2076,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1941
2076
  },
1942
2077
 
1943
2078
  flush(controller) {
1944
- const providerMetadata: SharedV3ProviderMetadata = {
2079
+ const providerMetadata: SharedV4ProviderMetadata = {
1945
2080
  [providerOptionsName]: {
1946
2081
  responseId: responseId,
1947
2082
  ...(logprobs.length > 0 ? { logprobs } : {}),
@@ -37,11 +37,12 @@ export const openaiResponsesReasoningModelIds = [
37
37
  'gpt-5.2-chat-latest',
38
38
  'gpt-5.2-pro',
39
39
  'gpt-5.2-codex',
40
+ 'gpt-5.3-chat-latest',
41
+ 'gpt-5.3-codex',
40
42
  'gpt-5.4',
41
43
  'gpt-5.4-2026-03-05',
42
44
  'gpt-5.4-pro',
43
45
  'gpt-5.4-pro-2026-03-05',
44
- 'gpt-5.3-codex',
45
46
  ] as const;
46
47
 
47
48
  export const openaiResponsesModelIds = [
@@ -98,11 +99,12 @@ export type OpenAIResponsesModelId =
98
99
  | 'gpt-5.2-pro'
99
100
  | 'gpt-5.2-pro-2025-12-11'
100
101
  | 'gpt-5.2-codex'
102
+ | 'gpt-5.3-chat-latest'
103
+ | 'gpt-5.3-codex'
101
104
  | 'gpt-5.4'
102
105
  | 'gpt-5.4-2026-03-05'
103
106
  | 'gpt-5.4-pro'
104
107
  | 'gpt-5.4-pro-2026-03-05'
105
- | 'gpt-5.3-codex'
106
108
  | 'gpt-5-2025-08-07'
107
109
  | 'gpt-5-chat-latest'
108
110
  | 'gpt-5-codex'
@@ -1,6 +1,6 @@
1
1
  import {
2
- LanguageModelV3CallOptions,
3
- SharedV3Warning,
2
+ LanguageModelV4CallOptions,
3
+ SharedV4Warning,
4
4
  UnsupportedFunctionalityError,
5
5
  } from '@ai-sdk/provider';
6
6
  import { ToolNameMapping, validateTypes } from '@ai-sdk/provider-utils';
@@ -10,6 +10,7 @@ import { imageGenerationArgsSchema } from '../tool/image-generation';
10
10
  import { customArgsSchema } from '../tool/custom';
11
11
  import { mcpArgsSchema } from '../tool/mcp';
12
12
  import { shellArgsSchema } from '../tool/shell';
13
+ import { toolSearchArgsSchema } from '../tool/tool-search';
13
14
  import { webSearchArgsSchema } from '../tool/web-search';
14
15
  import { webSearchPreviewArgsSchema } from '../tool/web-search-preview';
15
16
  import { OpenAIResponsesTool } from './openai-responses-api';
@@ -20,8 +21,8 @@ export async function prepareResponsesTools({
20
21
  toolNameMapping,
21
22
  customProviderToolNames,
22
23
  }: {
23
- tools: LanguageModelV3CallOptions['tools'];
24
- toolChoice: LanguageModelV3CallOptions['toolChoice'] | undefined;
24
+ tools: LanguageModelV4CallOptions['tools'];
25
+ toolChoice: LanguageModelV4CallOptions['toolChoice'] | undefined;
25
26
  toolNameMapping?: ToolNameMapping;
26
27
  customProviderToolNames?: Set<string>;
27
28
  }): Promise<{
@@ -39,12 +40,12 @@ export async function prepareResponsesTools({
39
40
  | { type: 'mcp' }
40
41
  | { type: 'image_generation' }
41
42
  | { type: 'apply_patch' };
42
- toolWarnings: SharedV3Warning[];
43
+ toolWarnings: SharedV4Warning[];
43
44
  }> {
44
45
  // when the tools array is empty, change it to undefined to prevent errors:
45
46
  tools = tools?.length ? tools : undefined;
46
47
 
47
- const toolWarnings: SharedV3Warning[] = [];
48
+ const toolWarnings: SharedV4Warning[] = [];
48
49
 
49
50
  if (tools == null) {
50
51
  return { tools: undefined, toolChoice: undefined, toolWarnings };
@@ -56,15 +57,22 @@ export async function prepareResponsesTools({
56
57
 
57
58
  for (const tool of tools) {
58
59
  switch (tool.type) {
59
- case 'function':
60
+ case 'function': {
61
+ const openaiOptions = tool.providerOptions?.openai as
62
+ | { deferLoading?: boolean }
63
+ | undefined;
64
+ const deferLoading = openaiOptions?.deferLoading;
65
+
60
66
  openaiTools.push({
61
67
  type: 'function',
62
68
  name: tool.name,
63
69
  description: tool.description,
64
70
  parameters: tool.inputSchema,
65
71
  ...(tool.strict != null ? { strict: tool.strict } : {}),
72
+ ...(deferLoading != null ? { defer_loading: deferLoading } : {}),
66
73
  });
67
74
  break;
75
+ }
68
76
  case 'provider': {
69
77
  switch (tool.id) {
70
78
  case 'openai.file_search': {
@@ -241,11 +249,28 @@ export async function prepareResponsesTools({
241
249
 
242
250
  openaiTools.push({
243
251
  type: 'custom',
244
- name: args.name,
252
+ name: tool.name,
245
253
  description: args.description,
246
254
  format: args.format,
247
255
  });
248
- resolvedCustomProviderToolNames.add(args.name);
256
+ resolvedCustomProviderToolNames.add(tool.name);
257
+ break;
258
+ }
259
+ case 'openai.tool_search': {
260
+ const args = await validateTypes({
261
+ value: tool.args,
262
+ schema: toolSearchArgsSchema,
263
+ });
264
+ openaiTools.push({
265
+ type: 'tool_search',
266
+ ...(args.execution != null ? { execution: args.execution } : {}),
267
+ ...(args.description != null
268
+ ? { description: args.description }
269
+ : {}),
270
+ ...(args.parameters != null
271
+ ? { parameters: args.parameters }
272
+ : {}),
273
+ });
249
274
  break;
250
275
  }
251
276
  }
@@ -1,4 +1,4 @@
1
- import { SpeechModelV3, SharedV3Warning } from '@ai-sdk/provider';
1
+ import { SpeechModelV4, SharedV4Warning } from '@ai-sdk/provider';
2
2
  import {
3
3
  combineHeaders,
4
4
  createBinaryResponseHandler,
@@ -19,8 +19,8 @@ interface OpenAISpeechModelConfig extends OpenAIConfig {
19
19
  };
20
20
  }
21
21
 
22
- export class OpenAISpeechModel implements SpeechModelV3 {
23
- readonly specificationVersion = 'v3';
22
+ export class OpenAISpeechModel implements SpeechModelV4 {
23
+ readonly specificationVersion = 'v4';
24
24
 
25
25
  get provider(): string {
26
26
  return this.config.provider;
@@ -39,8 +39,8 @@ export class OpenAISpeechModel implements SpeechModelV3 {
39
39
  instructions,
40
40
  language,
41
41
  providerOptions,
42
- }: Parameters<SpeechModelV3['doGenerate']>[0]) {
43
- const warnings: SharedV3Warning[] = [];
42
+ }: Parameters<SpeechModelV4['doGenerate']>[0]) {
43
+ const warnings: SharedV4Warning[] = [];
44
44
 
45
45
  // Parse provider options
46
46
  const openAIOptions = await parseProviderOptions({
@@ -98,8 +98,8 @@ export class OpenAISpeechModel implements SpeechModelV3 {
98
98
  }
99
99
 
100
100
  async doGenerate(
101
- options: Parameters<SpeechModelV3['doGenerate']>[0],
102
- ): Promise<Awaited<ReturnType<SpeechModelV3['doGenerate']>>> {
101
+ options: Parameters<SpeechModelV4['doGenerate']>[0],
102
+ ): Promise<Awaited<ReturnType<SpeechModelV4['doGenerate']>>> {
103
103
  const currentDate = this.config._internal?.currentDate?.() ?? new Date();
104
104
  const { requestBody, warnings } = await this.getArgs(options);
105
105
 
@@ -8,7 +8,6 @@ import { z } from 'zod/v4';
8
8
  export const customArgsSchema = lazySchema(() =>
9
9
  zodSchema(
10
10
  z.object({
11
- name: z.string(),
12
11
  description: z.string().optional(),
13
12
  format: z
14
13
  .union([
@@ -31,11 +30,6 @@ const customInputSchema = lazySchema(() => zodSchema(z.string()));
31
30
  export const customToolFactory = createProviderToolFactory<
32
31
  string,
33
32
  {
34
- /**
35
- * The name of the custom tool, used to identify it in the API.
36
- */
37
- name: string;
38
-
39
33
  /**
40
34
  * An optional description of what the tool does.
41
35
  */
@@ -0,0 +1,98 @@
1
+ import { JSONObject } from '@ai-sdk/provider';
2
+ import {
3
+ createProviderToolFactoryWithOutputSchema,
4
+ FlexibleSchema,
5
+ lazySchema,
6
+ zodSchema,
7
+ } from '@ai-sdk/provider-utils';
8
+ import { z } from 'zod/v4';
9
+
10
+ export const toolSearchArgsSchema = lazySchema(() =>
11
+ zodSchema(
12
+ z.object({
13
+ execution: z.enum(['server', 'client']).optional(),
14
+ description: z.string().optional(),
15
+ parameters: z.record(z.string(), z.unknown()).optional(),
16
+ }),
17
+ ),
18
+ );
19
+
20
+ export const toolSearchInputSchema = lazySchema(() =>
21
+ zodSchema(
22
+ z.object({
23
+ arguments: z.unknown().optional(),
24
+ call_id: z.string().nullish(),
25
+ }),
26
+ ),
27
+ );
28
+
29
+ export const toolSearchOutputSchema: FlexibleSchema<{
30
+ tools: Array<JSONObject>;
31
+ }> = lazySchema(() =>
32
+ zodSchema(
33
+ z.object({
34
+ tools: z.array(z.record(z.string(), z.unknown())),
35
+ }),
36
+ ),
37
+ ) as FlexibleSchema<{ tools: Array<JSONObject> }>;
38
+
39
+ const toolSearchToolFactory = createProviderToolFactoryWithOutputSchema<
40
+ {
41
+ /**
42
+ * The arguments from the tool_search_call.
43
+ * This is preserved for multi-turn conversation reconstruction.
44
+ */
45
+ arguments?: unknown;
46
+
47
+ /**
48
+ * The call ID from the tool_search_call.
49
+ * Present for client-executed tool search; null for hosted.
50
+ */
51
+ call_id?: string | null;
52
+ },
53
+ {
54
+ /**
55
+ * The tools that were loaded by the tool search.
56
+ * These are the deferred tools that the model requested to load.
57
+ * Each tool is represented as a JSON object with properties depending on its type.
58
+ *
59
+ * Common properties include:
60
+ * - `type`: The type of the tool (e.g., 'function', 'web_search', etc.)
61
+ * - `name`: The name of the tool (for function tools)
62
+ * - `description`: A description of the tool
63
+ * - `deferLoading`: Whether this tool was deferred (had defer_loading: true)
64
+ * - `parameters`: The JSON Schema for the function parameters (for function tools)
65
+ * - `strict`: Whether to enable strict schema adherence (for function tools)
66
+ */
67
+ tools: Array<JSONObject>;
68
+ },
69
+ {
70
+ /**
71
+ * Whether the tool search is executed by the server (hosted) or client.
72
+ * - `'server'` (default): OpenAI performs the search across deferred tools.
73
+ * - `'client'`: The model emits a `tool_search_call` and your `execute`
74
+ * function performs the lookup, returning the tools to load.
75
+ */
76
+ execution?: 'server' | 'client';
77
+
78
+ /**
79
+ * A description of the tool search capability.
80
+ * Only used for client-executed tool search.
81
+ */
82
+ description?: string;
83
+
84
+ /**
85
+ * JSON Schema for the search arguments your application expects.
86
+ * Only used for client-executed tool search.
87
+ */
88
+ parameters?: Record<string, unknown>;
89
+ }
90
+ >({
91
+ id: 'openai.tool_search',
92
+ inputSchema: toolSearchInputSchema,
93
+ outputSchema: toolSearchOutputSchema,
94
+ });
95
+
96
+ export const toolSearch = (
97
+ args: Parameters<typeof toolSearchToolFactory>[0] = {},
98
+ ) => toolSearchToolFactory(args);
@@ -1,7 +1,7 @@
1
1
  import {
2
- TranscriptionModelV3,
3
- TranscriptionModelV3CallOptions,
4
- SharedV3Warning,
2
+ TranscriptionModelV4,
3
+ TranscriptionModelV4CallOptions,
4
+ SharedV4Warning,
5
5
  } from '@ai-sdk/provider';
6
6
  import {
7
7
  combineHeaders,
@@ -21,7 +21,7 @@ import {
21
21
  } from './openai-transcription-options';
22
22
 
23
23
  export type OpenAITranscriptionCallOptions = Omit<
24
- TranscriptionModelV3CallOptions,
24
+ TranscriptionModelV4CallOptions,
25
25
  'providerOptions'
26
26
  > & {
27
27
  providerOptions?: {
@@ -96,8 +96,8 @@ const languageMap = {
96
96
  welsh: 'cy',
97
97
  };
98
98
 
99
- export class OpenAITranscriptionModel implements TranscriptionModelV3 {
100
- readonly specificationVersion = 'v3';
99
+ export class OpenAITranscriptionModel implements TranscriptionModelV4 {
100
+ readonly specificationVersion = 'v4';
101
101
 
102
102
  get provider(): string {
103
103
  return this.config.provider;
@@ -113,7 +113,7 @@ export class OpenAITranscriptionModel implements TranscriptionModelV3 {
113
113
  mediaType,
114
114
  providerOptions,
115
115
  }: OpenAITranscriptionCallOptions) {
116
- const warnings: SharedV3Warning[] = [];
116
+ const warnings: SharedV4Warning[] = [];
117
117
 
118
118
  // Parse provider options
119
119
  const openAIOptions = await parseProviderOptions({
@@ -176,7 +176,7 @@ export class OpenAITranscriptionModel implements TranscriptionModelV3 {
176
176
 
177
177
  async doGenerate(
178
178
  options: OpenAITranscriptionCallOptions,
179
- ): Promise<Awaited<ReturnType<TranscriptionModelV3['doGenerate']>>> {
179
+ ): Promise<Awaited<ReturnType<TranscriptionModelV4['doGenerate']>>> {
180
180
  const currentDate = this.config._internal?.currentDate?.() ?? new Date();
181
181
  const { formData, warnings } = await this.getArgs(options);
182
182