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

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 +137 -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,10 +1,10 @@
1
1
  import {
2
- EmbeddingModelV3,
3
- ImageModelV3,
4
- LanguageModelV3,
5
- ProviderV3,
6
- SpeechModelV3,
7
- TranscriptionModelV3,
2
+ EmbeddingModelV4,
3
+ ImageModelV4,
4
+ LanguageModelV4,
5
+ ProviderV4,
6
+ SpeechModelV4,
7
+ TranscriptionModelV4,
8
8
  } from '@ai-sdk/provider';
9
9
  import {
10
10
  FetchFunction,
@@ -30,68 +30,68 @@ import { OpenAITranscriptionModel } from './transcription/openai-transcription-m
30
30
  import { OpenAITranscriptionModelId } from './transcription/openai-transcription-options';
31
31
  import { VERSION } from './version';
32
32
 
33
- export interface OpenAIProvider extends ProviderV3 {
34
- (modelId: OpenAIResponsesModelId): LanguageModelV3;
33
+ export interface OpenAIProvider extends ProviderV4 {
34
+ (modelId: OpenAIResponsesModelId): LanguageModelV4;
35
35
 
36
36
  /**
37
37
  * Creates an OpenAI model for text generation.
38
38
  */
39
- languageModel(modelId: OpenAIResponsesModelId): LanguageModelV3;
39
+ languageModel(modelId: OpenAIResponsesModelId): LanguageModelV4;
40
40
 
41
41
  /**
42
42
  * Creates an OpenAI chat model for text generation.
43
43
  */
44
- chat(modelId: OpenAIChatModelId): LanguageModelV3;
44
+ chat(modelId: OpenAIChatModelId): LanguageModelV4;
45
45
 
46
46
  /**
47
47
  * Creates an OpenAI responses API model for text generation.
48
48
  */
49
- responses(modelId: OpenAIResponsesModelId): LanguageModelV3;
49
+ responses(modelId: OpenAIResponsesModelId): LanguageModelV4;
50
50
 
51
51
  /**
52
52
  * Creates an OpenAI completion model for text generation.
53
53
  */
54
- completion(modelId: OpenAICompletionModelId): LanguageModelV3;
54
+ completion(modelId: OpenAICompletionModelId): LanguageModelV4;
55
55
 
56
56
  /**
57
57
  * Creates a model for text embeddings.
58
58
  */
59
- embedding(modelId: OpenAIEmbeddingModelId): EmbeddingModelV3;
59
+ embedding(modelId: OpenAIEmbeddingModelId): EmbeddingModelV4;
60
60
 
61
61
  /**
62
62
  * Creates a model for text embeddings.
63
63
  */
64
- embeddingModel(modelId: OpenAIEmbeddingModelId): EmbeddingModelV3;
64
+ embeddingModel(modelId: OpenAIEmbeddingModelId): EmbeddingModelV4;
65
65
 
66
66
  /**
67
67
  * @deprecated Use `embedding` instead.
68
68
  */
69
- textEmbedding(modelId: OpenAIEmbeddingModelId): EmbeddingModelV3;
69
+ textEmbedding(modelId: OpenAIEmbeddingModelId): EmbeddingModelV4;
70
70
 
71
71
  /**
72
72
  * @deprecated Use `embeddingModel` instead.
73
73
  */
74
- textEmbeddingModel(modelId: OpenAIEmbeddingModelId): EmbeddingModelV3;
74
+ textEmbeddingModel(modelId: OpenAIEmbeddingModelId): EmbeddingModelV4;
75
75
 
76
76
  /**
77
77
  * Creates a model for image generation.
78
78
  */
79
- image(modelId: OpenAIImageModelId): ImageModelV3;
79
+ image(modelId: OpenAIImageModelId): ImageModelV4;
80
80
 
81
81
  /**
82
82
  * Creates a model for image generation.
83
83
  */
84
- imageModel(modelId: OpenAIImageModelId): ImageModelV3;
84
+ imageModel(modelId: OpenAIImageModelId): ImageModelV4;
85
85
 
86
86
  /**
87
87
  * Creates a model for transcription.
88
88
  */
89
- transcription(modelId: OpenAITranscriptionModelId): TranscriptionModelV3;
89
+ transcription(modelId: OpenAITranscriptionModelId): TranscriptionModelV4;
90
90
 
91
91
  /**
92
92
  * Creates a model for speech generation.
93
93
  */
94
- speech(modelId: OpenAISpeechModelId): SpeechModelV3;
94
+ speech(modelId: OpenAISpeechModelId): SpeechModelV4;
95
95
 
96
96
  /**
97
97
  * OpenAI-specific tools.
@@ -240,7 +240,7 @@ export function createOpenAI(
240
240
  return createLanguageModel(modelId);
241
241
  };
242
242
 
243
- provider.specificationVersion = 'v3' as const;
243
+ provider.specificationVersion = 'v4' as const;
244
244
  provider.languageModel = createLanguageModel;
245
245
  provider.chat = createChatModel;
246
246
  provider.completion = createCompletionModel;
@@ -5,6 +5,7 @@ import { fileSearch } from './tool/file-search';
5
5
  import { imageGeneration } from './tool/image-generation';
6
6
  import { localShell } from './tool/local-shell';
7
7
  import { shell } from './tool/shell';
8
+ import { toolSearch } from './tool/tool-search';
8
9
  import { webSearch } from './tool/web-search';
9
10
  import { webSearchPreview } from './tool/web-search-preview';
10
11
  import { mcp } from './tool/mcp';
@@ -24,7 +25,6 @@ export const openaiTools = {
24
25
  * Lark syntax). The model returns a `custom_tool_call` output item whose
25
26
  * `input` field is a string matching the specified grammar.
26
27
  *
27
- * @param name - The name of the custom tool.
28
28
  * @param description - An optional description of the tool.
29
29
  * @param format - The output format constraint (grammar type, syntax, and definition).
30
30
  */
@@ -123,4 +123,15 @@ export const openaiTools = {
123
123
  * @param serverUrl - URL for the MCP server.
124
124
  */
125
125
  mcp,
126
+
127
+ /**
128
+ * Tool search allows the model to dynamically search for and load deferred
129
+ * tools into the model's context as needed. This helps reduce overall token
130
+ * usage, cost, and latency by only loading tools when the model needs them.
131
+ *
132
+ * To use tool search, mark functions or namespaces with `defer_loading: true`
133
+ * in the tools array. The model will use tool search to load these tools
134
+ * when it determines they are needed.
135
+ */
136
+ toolSearch,
126
137
  };
@@ -1,4 +1,4 @@
1
- import { LanguageModelV3Usage } from '@ai-sdk/provider';
1
+ import { LanguageModelV4Usage } from '@ai-sdk/provider';
2
2
 
3
3
  export type OpenAIResponsesUsage = {
4
4
  input_tokens: number;
@@ -13,7 +13,7 @@ export type OpenAIResponsesUsage = {
13
13
 
14
14
  export function convertOpenAIResponsesUsage(
15
15
  usage: OpenAIResponsesUsage | undefined | null,
16
- ): LanguageModelV3Usage {
16
+ ): LanguageModelV4Usage {
17
17
  if (usage == null) {
18
18
  return {
19
19
  inputTokens: {
@@ -1,12 +1,13 @@
1
1
  import {
2
- LanguageModelV3Prompt,
3
- LanguageModelV3ToolApprovalResponsePart,
4
- SharedV3Warning,
2
+ LanguageModelV4Prompt,
3
+ LanguageModelV4ToolApprovalResponsePart,
4
+ SharedV4Warning,
5
5
  UnsupportedFunctionalityError,
6
6
  } from '@ai-sdk/provider';
7
7
  import {
8
8
  convertToBase64,
9
9
  isNonNullable,
10
+ parseJSON,
10
11
  parseProviderOptions,
11
12
  ToolNameMapping,
12
13
  validateTypes,
@@ -21,6 +22,10 @@ import {
21
22
  localShellOutputSchema,
22
23
  } from '../tool/local-shell';
23
24
  import { shellInputSchema, shellOutputSchema } from '../tool/shell';
25
+ import {
26
+ toolSearchInputSchema,
27
+ toolSearchOutputSchema,
28
+ } from '../tool/tool-search';
24
29
  import {
25
30
  OpenAIResponsesCustomToolCallOutput,
26
31
  OpenAIResponsesFunctionCallOutput,
@@ -50,7 +55,7 @@ export async function convertToOpenAIResponsesInput({
50
55
  hasApplyPatchTool = false,
51
56
  customProviderToolNames,
52
57
  }: {
53
- prompt: LanguageModelV3Prompt;
58
+ prompt: LanguageModelV4Prompt;
54
59
  toolNameMapping: ToolNameMapping;
55
60
  systemMessageMode: 'system' | 'developer' | 'remove';
56
61
  providerOptionsName: string;
@@ -63,10 +68,10 @@ export async function convertToOpenAIResponsesInput({
63
68
  customProviderToolNames?: Set<string>;
64
69
  }): Promise<{
65
70
  input: OpenAIResponsesInput;
66
- warnings: Array<SharedV3Warning>;
71
+ warnings: Array<SharedV4Warning>;
67
72
  }> {
68
- const input: OpenAIResponsesInput = [];
69
- const warnings: Array<SharedV3Warning> = [];
73
+ let input: OpenAIResponsesInput = [];
74
+ const warnings: Array<SharedV4Warning> = [];
70
75
  const processedApprovalIds = new Set<string>();
71
76
 
72
77
  for (const { role, content } of prompt) {
@@ -206,6 +211,41 @@ export async function convertToOpenAIResponsesInput({
206
211
  break;
207
212
  }
208
213
 
214
+ const resolvedToolName = toolNameMapping.toProviderToolName(
215
+ part.toolName,
216
+ );
217
+
218
+ if (resolvedToolName === 'tool_search') {
219
+ if (store && id != null) {
220
+ input.push({ type: 'item_reference', id });
221
+ break;
222
+ }
223
+
224
+ const parsedInput =
225
+ typeof part.input === 'string'
226
+ ? await parseJSON({
227
+ text: part.input,
228
+ schema: toolSearchInputSchema,
229
+ })
230
+ : await validateTypes({
231
+ value: part.input,
232
+ schema: toolSearchInputSchema,
233
+ });
234
+
235
+ const execution =
236
+ parsedInput.call_id != null ? 'client' : 'server';
237
+
238
+ input.push({
239
+ type: 'tool_search_call',
240
+ id: id ?? part.toolCallId,
241
+ execution,
242
+ call_id: parsedInput.call_id ?? null,
243
+ status: 'completed',
244
+ arguments: parsedInput.arguments,
245
+ });
246
+ break;
247
+ }
248
+
209
249
  if (part.providerExecuted) {
210
250
  if (store && id != null) {
211
251
  input.push({ type: 'item_reference', id });
@@ -218,10 +258,6 @@ export async function convertToOpenAIResponsesInput({
218
258
  break;
219
259
  }
220
260
 
221
- const resolvedToolName = toolNameMapping.toProviderToolName(
222
- part.toolName,
223
- );
224
-
225
261
  if (hasLocalShellTool && resolvedToolName === 'local_shell') {
226
262
  const parsedInput = await validateTypes({
227
263
  value: part.input,
@@ -328,6 +364,35 @@ export async function convertToOpenAIResponsesInput({
328
364
  part.toolName,
329
365
  );
330
366
 
367
+ if (resolvedResultToolName === 'tool_search') {
368
+ const itemId =
369
+ (
370
+ part.providerOptions?.[providerOptionsName] as
371
+ | { itemId?: string }
372
+ | undefined
373
+ )?.itemId ?? part.toolCallId;
374
+
375
+ if (store) {
376
+ input.push({ type: 'item_reference', id: itemId });
377
+ } else if (part.output.type === 'json') {
378
+ const parsedOutput = await validateTypes({
379
+ value: part.output.value,
380
+ schema: toolSearchOutputSchema,
381
+ });
382
+
383
+ input.push({
384
+ type: 'tool_search_output',
385
+ id: itemId,
386
+ execution: 'server',
387
+ call_id: null,
388
+ status: 'completed',
389
+ tools: parsedOutput.tools,
390
+ });
391
+ }
392
+
393
+ break;
394
+ }
395
+
331
396
  /*
332
397
  * Shell tool results are separate output items (shell_call_output)
333
398
  * with their own item IDs distinct from the shell_call's item ID.
@@ -488,7 +553,7 @@ export async function convertToOpenAIResponsesInput({
488
553
  for (const part of content) {
489
554
  if (part.type === 'tool-approval-response') {
490
555
  const approvalResponse =
491
- part as LanguageModelV3ToolApprovalResponsePart;
556
+ part as LanguageModelV4ToolApprovalResponsePart;
492
557
 
493
558
  if (processedApprovalIds.has(approvalResponse.approvalId)) {
494
559
  continue;
@@ -527,6 +592,22 @@ export async function convertToOpenAIResponsesInput({
527
592
  part.toolName,
528
593
  );
529
594
 
595
+ if (resolvedToolName === 'tool_search' && output.type === 'json') {
596
+ const parsedOutput = await validateTypes({
597
+ value: output.value,
598
+ schema: toolSearchOutputSchema,
599
+ });
600
+
601
+ input.push({
602
+ type: 'tool_search_output',
603
+ execution: 'client',
604
+ call_id: part.toolCallId,
605
+ status: 'completed',
606
+ tools: parsedOutput.tools,
607
+ });
608
+ continue;
609
+ }
610
+
530
611
  if (
531
612
  hasLocalShellTool &&
532
613
  resolvedToolName === 'local_shell' &&
@@ -722,6 +803,29 @@ export async function convertToOpenAIResponsesInput({
722
803
  }
723
804
  }
724
805
 
806
+ // when store is false, remove reasoning parts without encrypted content
807
+ if (
808
+ !store &&
809
+ input.some(
810
+ item =>
811
+ 'type' in item &&
812
+ item.type === 'reasoning' &&
813
+ item.encrypted_content == null,
814
+ )
815
+ ) {
816
+ warnings.push({
817
+ type: 'other',
818
+ message:
819
+ 'Reasoning parts without encrypted content are not supported when store is false. Skipping reasoning parts.',
820
+ });
821
+ input = input.filter(
822
+ item =>
823
+ !('type' in item) ||
824
+ item.type !== 'reasoning' ||
825
+ item.encrypted_content != null,
826
+ );
827
+ }
828
+
725
829
  return { input, warnings };
726
830
  }
727
831
 
@@ -1,4 +1,4 @@
1
- import { LanguageModelV3FinishReason } from '@ai-sdk/provider';
1
+ import { LanguageModelV4FinishReason } from '@ai-sdk/provider';
2
2
 
3
3
  export function mapOpenAIResponseFinishReason({
4
4
  finishReason,
@@ -7,7 +7,7 @@ export function mapOpenAIResponseFinishReason({
7
7
  finishReason: string | null | undefined;
8
8
  // flag that checks if there have been client-side tool calls (not executed by openai)
9
9
  hasFunctionCall: boolean;
10
- }): LanguageModelV3FinishReason['unified'] {
10
+ }): LanguageModelV4FinishReason['unified'] {
11
11
  switch (finishReason) {
12
12
  case undefined:
13
13
  case null:
@@ -1,7 +1,18 @@
1
- import { JSONSchema7 } from '@ai-sdk/provider';
1
+ import { JSONObject, JSONSchema7, JSONValue } from '@ai-sdk/provider';
2
2
  import { InferSchema, lazySchema, zodSchema } from '@ai-sdk/provider-utils';
3
3
  import { z } from 'zod/v4';
4
4
 
5
+ const jsonValueSchema: z.ZodType<JSONValue> = z.lazy(() =>
6
+ z.union([
7
+ z.string(),
8
+ z.number(),
9
+ z.boolean(),
10
+ z.null(),
11
+ z.array(jsonValueSchema),
12
+ z.record(z.string(), jsonValueSchema.optional()),
13
+ ]),
14
+ );
15
+
5
16
  export type OpenAIResponsesInput = Array<OpenAIResponsesInputItem>;
6
17
 
7
18
  export type OpenAIResponsesInputItem =
@@ -20,6 +31,8 @@ export type OpenAIResponsesInputItem =
20
31
  | OpenAIResponsesShellCallOutput
21
32
  | OpenAIResponsesApplyPatchCall
22
33
  | OpenAIResponsesApplyPatchCallOutput
34
+ | OpenAIResponsesToolSearchCall
35
+ | OpenAIResponsesToolSearchOutput
23
36
  | OpenAIResponsesReasoning
24
37
  | OpenAIResponsesItemReference;
25
38
 
@@ -199,6 +212,24 @@ export type OpenAIResponsesApplyPatchCallOutput = {
199
212
  output?: string;
200
213
  };
201
214
 
215
+ export type OpenAIResponsesToolSearchCall = {
216
+ type: 'tool_search_call';
217
+ id: string;
218
+ execution: 'server' | 'client';
219
+ call_id: string | null;
220
+ status: 'in_progress' | 'completed' | 'incomplete';
221
+ arguments: unknown;
222
+ };
223
+
224
+ export type OpenAIResponsesToolSearchOutput = {
225
+ type: 'tool_search_output';
226
+ id?: string;
227
+ execution: 'server' | 'client';
228
+ call_id: string | null;
229
+ status: 'in_progress' | 'completed' | 'incomplete';
230
+ tools: Array<JSONObject>;
231
+ };
232
+
202
233
  export type OpenAIResponsesItemReference = {
203
234
  type: 'item_reference';
204
235
  id: string;
@@ -249,6 +280,7 @@ export type OpenAIResponsesTool =
249
280
  description: string | undefined;
250
281
  parameters: JSONSchema7;
251
282
  strict?: boolean;
283
+ defer_loading?: boolean;
252
284
  }
253
285
  | {
254
286
  type: 'apply_patch';
@@ -407,6 +439,12 @@ export type OpenAIResponsesTool =
407
439
  path: string;
408
440
  }>;
409
441
  };
442
+ }
443
+ | {
444
+ type: 'tool_search';
445
+ execution?: 'server' | 'client';
446
+ description?: string;
447
+ parameters?: Record<string, unknown>;
410
448
  };
411
449
 
412
450
  export type OpenAIResponsesReasoning = {
@@ -592,6 +630,22 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
592
630
  }),
593
631
  ),
594
632
  }),
633
+ z.object({
634
+ type: z.literal('tool_search_call'),
635
+ id: z.string(),
636
+ execution: z.enum(['server', 'client']),
637
+ call_id: z.string().nullable(),
638
+ status: z.enum(['in_progress', 'completed', 'incomplete']),
639
+ arguments: z.unknown(),
640
+ }),
641
+ z.object({
642
+ type: z.literal('tool_search_output'),
643
+ id: z.string(),
644
+ execution: z.enum(['server', 'client']),
645
+ call_id: z.string().nullable(),
646
+ status: z.enum(['in_progress', 'completed', 'incomplete']),
647
+ tools: z.array(z.record(z.string(), jsonValueSchema.optional())),
648
+ }),
595
649
  ]),
596
650
  }),
597
651
  z.object({
@@ -815,6 +869,22 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
815
869
  }),
816
870
  ),
817
871
  }),
872
+ z.object({
873
+ type: z.literal('tool_search_call'),
874
+ id: z.string(),
875
+ execution: z.enum(['server', 'client']),
876
+ call_id: z.string().nullable(),
877
+ status: z.enum(['in_progress', 'completed', 'incomplete']),
878
+ arguments: z.unknown(),
879
+ }),
880
+ z.object({
881
+ type: z.literal('tool_search_output'),
882
+ id: z.string(),
883
+ execution: z.enum(['server', 'client']),
884
+ call_id: z.string().nullable(),
885
+ status: z.enum(['in_progress', 'completed', 'incomplete']),
886
+ tools: z.array(z.record(z.string(), jsonValueSchema.optional())),
887
+ }),
818
888
  ]),
819
889
  }),
820
890
  z.object({
@@ -1238,6 +1308,22 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1238
1308
  }),
1239
1309
  ),
1240
1310
  }),
1311
+ z.object({
1312
+ type: z.literal('tool_search_call'),
1313
+ id: z.string(),
1314
+ execution: z.enum(['server', 'client']),
1315
+ call_id: z.string().nullable(),
1316
+ status: z.enum(['in_progress', 'completed', 'incomplete']),
1317
+ arguments: z.unknown(),
1318
+ }),
1319
+ z.object({
1320
+ type: z.literal('tool_search_output'),
1321
+ id: z.string(),
1322
+ execution: z.enum(['server', 'client']),
1323
+ call_id: z.string().nullable(),
1324
+ status: z.enum(['in_progress', 'completed', 'incomplete']),
1325
+ tools: z.array(z.record(z.string(), jsonValueSchema.optional())),
1326
+ }),
1241
1327
  ]),
1242
1328
  )
1243
1329
  .optional(),