@ai-sdk/openai 3.0.101 → 3.0.104

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai",
3
- "version": "3.0.101",
3
+ "version": "3.0.104",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@ai-sdk/provider": "3.0.15",
40
- "@ai-sdk/provider-utils": "4.0.48"
40
+ "@ai-sdk/provider-utils": "4.0.49"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "20.17.24",
@@ -8,7 +8,11 @@ import type { OpenAIChatPrompt } from './openai-chat-prompt';
8
8
  import { convertToBase64 } from '@ai-sdk/provider-utils';
9
9
 
10
10
  function serializeToolCallArguments(input: unknown): string {
11
- return JSON.stringify(input === undefined ? {} : input);
11
+ return JSON.stringify(
12
+ typeof input === 'object' && input !== null && !Array.isArray(input)
13
+ ? input
14
+ : {},
15
+ );
12
16
  }
13
17
 
14
18
  type OpenAIPromptCacheBreakpoint = { mode: 'explicit' };
@@ -5,6 +5,7 @@ import {
5
5
  import {
6
6
  combineHeaders,
7
7
  createJsonResponseHandler,
8
+ EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL,
8
9
  parseProviderOptions,
9
10
  postJsonToApi,
10
11
  } from '@ai-sdk/provider-utils';
@@ -20,6 +21,7 @@ export class OpenAIEmbeddingModel implements EmbeddingModelV3 {
20
21
  readonly specificationVersion = 'v3';
21
22
  readonly modelId: OpenAIEmbeddingModelId;
22
23
  readonly maxEmbeddingsPerCall = 2048;
24
+ readonly [EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL] = 300_000;
23
25
  readonly supportsParallelCalls = true;
24
26
 
25
27
  private readonly config: OpenAIConfig;
@@ -268,6 +268,7 @@ export async function convertToOpenAIResponsesInput({
268
268
  hasLocalShellTool = false,
269
269
  hasShellTool = false,
270
270
  hasApplyPatchTool = false,
271
+ toolSearchToolName,
271
272
  customProviderToolNames,
272
273
  }: {
273
274
  prompt: LanguageModelV3Prompt;
@@ -282,6 +283,7 @@ export async function convertToOpenAIResponsesInput({
282
283
  hasLocalShellTool?: boolean;
283
284
  hasShellTool?: boolean;
284
285
  hasApplyPatchTool?: boolean;
286
+ toolSearchToolName?: string;
285
287
  customProviderToolNames?: Set<string>;
286
288
  }): Promise<{
287
289
  input: OpenAIResponsesInput;
@@ -559,7 +561,7 @@ export async function convertToOpenAIResponsesInput({
559
561
  part.toolName,
560
562
  );
561
563
 
562
- if (resolvedToolName === 'tool_search') {
564
+ if (part.toolName === toolSearchToolName) {
563
565
  if (store && id != null) {
564
566
  input.push({ type: 'item_reference', id });
565
567
  break;
@@ -740,7 +742,7 @@ export async function convertToOpenAIResponsesInput({
740
742
  part.toolName,
741
743
  );
742
744
 
743
- if (resolvedResultToolName === 'tool_search') {
745
+ if (part.toolName === toolSearchToolName) {
744
746
  const itemId = (part.providerOptions?.[providerOptionsName]
745
747
  ?.itemId ??
746
748
  (
@@ -1030,7 +1032,7 @@ export async function convertToOpenAIResponsesInput({
1030
1032
  part.toolName,
1031
1033
  );
1032
1034
 
1033
- if (resolvedToolName === 'tool_search' && output.type === 'json') {
1035
+ if (part.toolName === toolSearchToolName && output.type === 'json') {
1034
1036
  const parsedOutput = await validateTypes({
1035
1037
  value: output.value,
1036
1038
  schema: toolSearchOutputSchema,
@@ -17,6 +17,24 @@ const jsonValueSchema: z.ZodType<JSONValue> = z.lazy(() =>
17
17
  ]),
18
18
  );
19
19
 
20
+ function isRecord(value: unknown): value is Record<string, unknown> {
21
+ return value != null && typeof value === 'object' && !Array.isArray(value);
22
+ }
23
+
24
+ const openaiResponsesLocalShellCallSchema = z.object({
25
+ type: z.literal('local_shell_call'),
26
+ id: z.string(),
27
+ call_id: z.string(),
28
+ action: z.object({
29
+ type: z.literal('exec'),
30
+ command: z.array(z.string()),
31
+ timeout_ms: z.number().optional(),
32
+ user: z.string().optional(),
33
+ working_directory: z.string().optional(),
34
+ env: z.record(z.string(), z.string()).optional(),
35
+ }),
36
+ });
37
+
20
38
  export type OpenAIResponsesInput = Array<OpenAIResponsesInputItem>;
21
39
 
22
40
  export type OpenAIResponsesInputItem =
@@ -190,19 +208,9 @@ export type OpenAIResponsesComputerCall = {
190
208
  status?: string;
191
209
  };
192
210
 
193
- export type OpenAIResponsesLocalShellCall = {
194
- type: 'local_shell_call';
195
- id: string;
196
- call_id: string;
197
- action: {
198
- type: 'exec';
199
- command: string[];
200
- timeout_ms?: number;
201
- user?: string;
202
- working_directory?: string;
203
- env?: Record<string, string>;
204
- };
205
- };
211
+ export type OpenAIResponsesLocalShellCall = InferSchema<
212
+ typeof openaiResponsesLocalShellCallSchema
213
+ >;
206
214
 
207
215
  export type OpenAIResponsesLocalShellCallOutput = {
208
216
  type: 'local_shell_call_output';
@@ -555,7 +563,8 @@ const openaiResponsesNestedErrorChunkSchema = z.object({
555
563
  });
556
564
 
557
565
  // Current OpenAI OpenAPI docs define ResponseErrorEvent with top-level
558
- // code/message/param fields.
566
+ // code/message/param fields:
567
+ // https://developers.openai.com/api/reference/resources/responses/streaming-events
559
568
  const openaiResponsesErrorChunkSchema = z.object({
560
569
  type: z.literal('error'),
561
570
  sequence_number: z.number(),
@@ -564,6 +573,82 @@ const openaiResponsesErrorChunkSchema = z.object({
564
573
  param: z.string().nullish(),
565
574
  });
566
575
 
576
+ /**
577
+ * Chunk types explicitly modeled by openaiResponsesChunkSchema. Keep this set
578
+ * in sync with the union below. OpenAI's complete event catalog:
579
+ * https://developers.openai.com/api/reference/resources/responses/streaming-events
580
+ */
581
+ const openaiResponsesModeledChunkTypes = new Set([
582
+ 'error',
583
+ 'response.apply_patch_call_operation_diff.delta',
584
+ 'response.apply_patch_call_operation_diff.done',
585
+ 'response.code_interpreter_call_code.delta',
586
+ 'response.code_interpreter_call_code.done',
587
+ 'response.completed',
588
+ 'response.created',
589
+ 'response.custom_tool_call_input.delta',
590
+ 'response.failed',
591
+ 'response.function_call_arguments.delta',
592
+ 'response.function_call_arguments.done',
593
+ 'response.image_generation_call.partial_image',
594
+ 'response.incomplete',
595
+ 'response.output_item.added',
596
+ 'response.output_item.done',
597
+ 'response.output_text.annotation.added',
598
+ 'response.output_text.delta',
599
+ 'response.reasoning_summary_part.added',
600
+ 'response.reasoning_summary_part.done',
601
+ 'response.reasoning_summary_text.delta',
602
+ ]);
603
+
604
+ /**
605
+ * Output item types explicitly modeled by both output item event schemas below.
606
+ * OpenAI's complete ResponseOutputItem schema:
607
+ * https://developers.openai.com/api/reference/resources/responses#(resource)%20responses%20%3E%20(model)%20response_output_item%20%3E%20(schema)
608
+ */
609
+ const openaiResponsesModeledOutputItemTypes = new Set([
610
+ 'apply_patch_call',
611
+ 'code_interpreter_call',
612
+ 'computer_call',
613
+ 'custom_tool_call',
614
+ 'file_search_call',
615
+ 'function_call',
616
+ 'image_generation_call',
617
+ 'local_shell_call',
618
+ 'mcp_approval_request',
619
+ 'mcp_call',
620
+ 'mcp_list_tools',
621
+ 'message',
622
+ 'reasoning',
623
+ 'shell_call',
624
+ 'shell_call_output',
625
+ 'tool_search_call',
626
+ 'tool_search_output',
627
+ 'web_search_call',
628
+ ]);
629
+
630
+ function isModeledOpenAIResponsesChunk(value: Record<string, unknown>) {
631
+ if (
632
+ typeof value.type !== 'string' ||
633
+ !openaiResponsesModeledChunkTypes.has(value.type)
634
+ ) {
635
+ return false;
636
+ }
637
+
638
+ if (
639
+ value.type !== 'response.output_item.added' &&
640
+ value.type !== 'response.output_item.done'
641
+ ) {
642
+ return true;
643
+ }
644
+
645
+ if (!isRecord(value.item) || typeof value.item.type !== 'string') {
646
+ return true;
647
+ }
648
+
649
+ return openaiResponsesModeledOutputItemTypes.has(value.item.type);
650
+ }
651
+
567
652
  export const openaiResponsesChunkSchema = lazySchema(() =>
568
653
  zodSchema(
569
654
  z.union([
@@ -700,6 +785,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
700
785
  type: z.literal('file_search_call'),
701
786
  id: z.string(),
702
787
  }),
788
+ openaiResponsesLocalShellCallSchema,
703
789
  z.object({
704
790
  type: z.literal('image_generation_call'),
705
791
  id: z.string(),
@@ -908,19 +994,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
908
994
  )
909
995
  .nullish(),
910
996
  }),
911
- z.object({
912
- type: z.literal('local_shell_call'),
913
- id: z.string(),
914
- call_id: z.string(),
915
- action: z.object({
916
- type: z.literal('exec'),
917
- command: z.array(z.string()),
918
- timeout_ms: z.number().optional(),
919
- user: z.string().optional(),
920
- working_directory: z.string().optional(),
921
- env: z.record(z.string(), z.string()).optional(),
922
- }),
923
- }),
997
+ openaiResponsesLocalShellCallSchema,
924
998
  z.object({
925
999
  type: z.literal('computer_call'),
926
1000
  id: z.string(),
@@ -1055,6 +1129,14 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
1055
1129
  output_index: z.number(),
1056
1130
  delta: z.string(),
1057
1131
  }),
1132
+ z.object({
1133
+ // `name` is documented as required but omitted from live API events:
1134
+ // https://github.com/openai/openai-openapi/issues/545
1135
+ type: z.literal('response.function_call_arguments.done'),
1136
+ item_id: z.string(),
1137
+ output_index: z.number(),
1138
+ arguments: z.string(),
1139
+ }),
1058
1140
  z.object({
1059
1141
  type: z.literal('response.custom_tool_call_input.delta'),
1060
1142
  item_id: z.string(),
@@ -1144,6 +1226,9 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
1144
1226
  z
1145
1227
  .object({ type: z.string() })
1146
1228
  .loose()
1229
+ .refine(value => !isModeledOpenAIResponsesChunk(value), {
1230
+ message: 'Known response chunk failed schema validation',
1231
+ })
1147
1232
  .transform(value => ({
1148
1233
  type: 'unknown_chunk' as const,
1149
1234
  message: value.type,
@@ -1315,19 +1400,7 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1315
1400
  id: z.string(),
1316
1401
  result: z.string(),
1317
1402
  }),
1318
- z.object({
1319
- type: z.literal('local_shell_call'),
1320
- id: z.string(),
1321
- call_id: z.string(),
1322
- action: z.object({
1323
- type: z.literal('exec'),
1324
- command: z.array(z.string()),
1325
- timeout_ms: z.number().optional(),
1326
- user: z.string().optional(),
1327
- working_directory: z.string().optional(),
1328
- env: z.record(z.string(), z.string()).optional(),
1329
- }),
1330
- }),
1403
+ openaiResponsesLocalShellCallSchema,
1331
1404
  z.object({
1332
1405
  type: z.literal('function_call'),
1333
1406
  call_id: z.string(),
@@ -243,6 +243,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
243
243
  hasLocalShellTool: hasOpenAITool('openai.local_shell'),
244
244
  hasShellTool: hasOpenAITool('openai.shell'),
245
245
  hasApplyPatchTool: hasOpenAITool('openai.apply_patch'),
246
+ toolSearchToolName: getOpenAIToolName('openai.tool_search'),
246
247
  customProviderToolNames:
247
248
  customProviderToolNames.size > 0
248
249
  ? customProviderToolNames
@@ -263,10 +264,13 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
263
264
  }
264
265
  }
265
266
 
267
+ function getOpenAIToolName(id: string) {
268
+ return tools?.find(tool => tool.type === 'provider' && tool.id === id)
269
+ ?.name;
270
+ }
271
+
266
272
  function hasOpenAITool(id: string) {
267
- return (
268
- tools?.find(tool => tool.type === 'provider' && tool.id === id) != null
269
- );
273
+ return getOpenAIToolName(id) != null;
270
274
  }
271
275
 
272
276
  // when logprobs are requested, automatically include them:
@@ -1222,6 +1226,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1222
1226
  })
1223
1227
  : chunk.error;
1224
1228
 
1229
+ encounteredStreamError = true;
1225
1230
  finishReason = { unified: 'error', raw: undefined };
1226
1231
  controller.enqueue({ type: 'error', error });
1227
1232
  return;
@@ -2189,13 +2194,15 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
2189
2194
  ] = 'can-conclude';
2190
2195
  }
2191
2196
  } else if (isResponseFinishedChunk(value)) {
2192
- finishReason = {
2193
- unified: mapOpenAIResponseFinishReason({
2194
- finishReason: value.response.incomplete_details?.reason,
2195
- hasFunctionCall,
2196
- }),
2197
- raw: value.response.incomplete_details?.reason ?? undefined,
2198
- };
2197
+ if (!encounteredStreamError) {
2198
+ finishReason = {
2199
+ unified: mapOpenAIResponseFinishReason({
2200
+ finishReason: value.response.incomplete_details?.reason,
2201
+ hasFunctionCall,
2202
+ }),
2203
+ raw: value.response.incomplete_details?.reason ?? undefined,
2204
+ };
2205
+ }
2199
2206
  usage = value.response.usage;
2200
2207
  if (typeof value.response.service_tier === 'string') {
2201
2208
  serviceTier = value.response.service_tier;