@ai-sdk/openai 4.0.47 → 4.0.50

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.
@@ -1,5 +1,6 @@
1
1
  import type { JSONObject, JSONSchema7, JSONValue } from '@ai-sdk/provider';
2
2
  import {
3
+ isRecord,
3
4
  lazySchema,
4
5
  zodSchema,
5
6
  type InferSchema,
@@ -108,6 +109,20 @@ const openaiResponsesProgramOutputSchema = z.object({
108
109
  status: z.enum(['completed', 'incomplete']),
109
110
  });
110
111
 
112
+ const openaiResponsesLocalShellCallSchema = z.object({
113
+ type: z.literal('local_shell_call'),
114
+ id: z.string(),
115
+ call_id: z.string(),
116
+ action: z.object({
117
+ type: z.literal('exec'),
118
+ command: z.array(z.string()),
119
+ timeout_ms: z.number().optional(),
120
+ user: z.string().optional(),
121
+ working_directory: z.string().optional(),
122
+ env: z.record(z.string(), z.string()).optional(),
123
+ }),
124
+ });
125
+
111
126
  export type OpenAIResponsesInput = Array<OpenAIResponsesInputItem>;
112
127
 
113
128
  export type OpenAIResponsesInputItem =
@@ -326,19 +341,9 @@ export type OpenAIResponsesComputerCallOutput = {
326
341
  }>;
327
342
  };
328
343
 
329
- export type OpenAIResponsesLocalShellCall = {
330
- type: 'local_shell_call';
331
- id: string;
332
- call_id: string;
333
- action: {
334
- type: 'exec';
335
- command: string[];
336
- timeout_ms?: number;
337
- user?: string;
338
- working_directory?: string;
339
- env?: Record<string, string>;
340
- };
341
- };
344
+ export type OpenAIResponsesLocalShellCall = InferSchema<
345
+ typeof openaiResponsesLocalShellCallSchema
346
+ >;
342
347
 
343
348
  export type OpenAIResponsesLocalShellCallOutput = {
344
349
  type: 'local_shell_call_output';
@@ -709,7 +714,8 @@ const openaiResponsesNestedErrorChunkSchema = z.object({
709
714
  });
710
715
 
711
716
  // Current OpenAI OpenAPI docs define ResponseErrorEvent with top-level
712
- // code/message/param fields.
717
+ // code/message/param fields:
718
+ // https://developers.openai.com/api/reference/resources/responses/streaming-events
713
719
  const openaiResponsesErrorChunkSchema = z.object({
714
720
  type: z.literal('error'),
715
721
  sequence_number: z.number(),
@@ -718,6 +724,86 @@ const openaiResponsesErrorChunkSchema = z.object({
718
724
  param: z.string().nullish(),
719
725
  });
720
726
 
727
+ /**
728
+ * Chunk types explicitly modeled by openaiResponsesChunkSchema. Keep this set
729
+ * in sync with the union below. OpenAI's complete event catalog:
730
+ * https://developers.openai.com/api/reference/resources/responses/streaming-events
731
+ */
732
+ const openaiResponsesModeledChunkTypes = new Set([
733
+ 'error',
734
+ 'response.apply_patch_call_operation_diff.delta',
735
+ 'response.apply_patch_call_operation_diff.done',
736
+ 'response.code_interpreter_call_code.delta',
737
+ 'response.code_interpreter_call_code.done',
738
+ 'response.completed',
739
+ 'response.created',
740
+ 'response.custom_tool_call_input.delta',
741
+ 'response.failed',
742
+ 'response.function_call_arguments.delta',
743
+ 'response.function_call_arguments.done',
744
+ 'response.image_generation_call.partial_image',
745
+ 'response.in_progress',
746
+ 'response.incomplete',
747
+ 'response.output_item.added',
748
+ 'response.output_item.done',
749
+ 'response.output_text.annotation.added',
750
+ 'response.output_text.delta',
751
+ 'response.reasoning_summary_part.added',
752
+ 'response.reasoning_summary_part.done',
753
+ 'response.reasoning_summary_text.delta',
754
+ ]);
755
+
756
+ /**
757
+ * Output item types explicitly modeled by both output item event schemas below.
758
+ * OpenAI's complete ResponseOutputItem schema:
759
+ * https://developers.openai.com/api/reference/resources/responses#(resource)%20responses%20%3E%20(model)%20response_output_item%20%3E%20(schema)
760
+ */
761
+ const openaiResponsesModeledOutputItemTypes = new Set([
762
+ 'apply_patch_call',
763
+ 'code_interpreter_call',
764
+ 'compaction',
765
+ 'computer_call',
766
+ 'custom_tool_call',
767
+ 'file_search_call',
768
+ 'function_call',
769
+ 'image_generation_call',
770
+ 'local_shell_call',
771
+ 'mcp_approval_request',
772
+ 'mcp_call',
773
+ 'mcp_list_tools',
774
+ 'message',
775
+ 'program',
776
+ 'program_output',
777
+ 'reasoning',
778
+ 'shell_call',
779
+ 'shell_call_output',
780
+ 'tool_search_call',
781
+ 'tool_search_output',
782
+ 'web_search_call',
783
+ ]);
784
+
785
+ function isModeledOpenAIResponsesChunk(value: Record<string, unknown>) {
786
+ if (
787
+ typeof value.type !== 'string' ||
788
+ !openaiResponsesModeledChunkTypes.has(value.type)
789
+ ) {
790
+ return false;
791
+ }
792
+
793
+ if (
794
+ value.type !== 'response.output_item.added' &&
795
+ value.type !== 'response.output_item.done'
796
+ ) {
797
+ return true;
798
+ }
799
+
800
+ if (!isRecord(value.item) || typeof value.item.type !== 'string') {
801
+ return true;
802
+ }
803
+
804
+ return openaiResponsesModeledOutputItemTypes.has(value.item.type);
805
+ }
806
+
721
807
  export const openaiResponsesChunkSchema = lazySchema(() =>
722
808
  zodSchema(
723
809
  z.union([
@@ -863,6 +949,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
863
949
  type: z.literal('file_search_call'),
864
950
  id: z.string(),
865
951
  }),
952
+ openaiResponsesLocalShellCallSchema,
866
953
  z.object({
867
954
  type: z.literal('image_generation_call'),
868
955
  id: z.string(),
@@ -1079,19 +1166,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
1079
1166
  )
1080
1167
  .nullish(),
1081
1168
  }),
1082
- z.object({
1083
- type: z.literal('local_shell_call'),
1084
- id: z.string(),
1085
- call_id: z.string(),
1086
- action: z.object({
1087
- type: z.literal('exec'),
1088
- command: z.array(z.string()),
1089
- timeout_ms: z.number().optional(),
1090
- user: z.string().optional(),
1091
- working_directory: z.string().optional(),
1092
- env: z.record(z.string(), z.string()).optional(),
1093
- }),
1094
- }),
1169
+ openaiResponsesLocalShellCallSchema,
1095
1170
  openaiResponsesComputerCallSchema,
1096
1171
  z.object({
1097
1172
  type: z.literal('mcp_call'),
@@ -1227,6 +1302,14 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
1227
1302
  output_index: z.number(),
1228
1303
  delta: z.string(),
1229
1304
  }),
1305
+ z.object({
1306
+ // `name` is documented as required but omitted from live API events:
1307
+ // https://github.com/openai/openai-openapi/issues/545
1308
+ type: z.literal('response.function_call_arguments.done'),
1309
+ item_id: z.string(),
1310
+ output_index: z.number(),
1311
+ arguments: z.string(),
1312
+ }),
1230
1313
  z.object({
1231
1314
  type: z.literal('response.custom_tool_call_input.delta'),
1232
1315
  item_id: z.string(),
@@ -1319,6 +1402,9 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
1319
1402
  z
1320
1403
  .object({ type: z.string() })
1321
1404
  .loose()
1405
+ .refine(value => !isModeledOpenAIResponsesChunk(value), {
1406
+ message: 'Known response chunk failed schema validation',
1407
+ })
1322
1408
  .transform(value => ({
1323
1409
  type: 'unknown_chunk' as const,
1324
1410
  message: value.type,
@@ -1490,19 +1576,7 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1490
1576
  id: z.string(),
1491
1577
  result: z.string(),
1492
1578
  }),
1493
- z.object({
1494
- type: z.literal('local_shell_call'),
1495
- id: z.string(),
1496
- call_id: z.string(),
1497
- action: z.object({
1498
- type: z.literal('exec'),
1499
- command: z.array(z.string()),
1500
- timeout_ms: z.number().optional(),
1501
- user: z.string().optional(),
1502
- working_directory: z.string().optional(),
1503
- env: z.record(z.string(), z.string()).optional(),
1504
- }),
1505
- }),
1579
+ openaiResponsesLocalShellCallSchema,
1506
1580
  z.object({
1507
1581
  type: z.literal('function_call'),
1508
1582
  call_id: z.string(),
@@ -33,7 +33,10 @@ import {
33
33
  import type { OpenAIConfig } from '../openai-config';
34
34
  import { openaiFailedResponseHandler } from '../openai-error';
35
35
  import { getOpenAILanguageModelCapabilities } from '../openai-language-model-capabilities';
36
- import { throwIfOpenAIStreamErrorBeforeOutput } from '../openai-stream-error';
36
+ import {
37
+ createOpenAIProviderStreamError,
38
+ throwIfOpenAIStreamErrorBeforeOutput,
39
+ } from '../openai-stream-error';
37
40
  import type { applyPatchInputSchema } from '../tool/apply-patch';
38
41
  import type {
39
42
  codeInterpreterInputSchema,
@@ -364,6 +367,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
364
367
  hasShellTool: hasOpenAITool('openai.shell'),
365
368
  hasApplyPatchTool: hasOpenAITool('openai.apply_patch'),
366
369
  hasComputerTool: hasOpenAITool('openai.computer'),
370
+ toolSearchToolName: getOpenAIToolName('openai.tool_search'),
367
371
  customProviderToolNames:
368
372
  customProviderToolNames.size > 0
369
373
  ? customProviderToolNames
@@ -393,10 +397,13 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
393
397
  }
394
398
  }
395
399
 
400
+ function getOpenAIToolName(id: string) {
401
+ return tools?.find(tool => tool.type === 'provider' && tool.id === id)
402
+ ?.name;
403
+ }
404
+
396
405
  function hasOpenAITool(id: string) {
397
- return (
398
- tools?.find(tool => tool.type === 'provider' && tool.id === id) != null
399
- );
406
+ return getOpenAIToolName(id) != null;
400
407
  }
401
408
 
402
409
  // when logprobs are requested, automatically include them:
@@ -1473,6 +1480,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1473
1480
  })
1474
1481
  : chunk.error;
1475
1482
 
1483
+ encounteredStreamError = true;
1476
1484
  finishReason = { unified: 'error', raw: undefined };
1477
1485
  controller.enqueue({ type: 'error', error });
1478
1486
  return;
@@ -2561,13 +2569,15 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2561
2569
  }
2562
2570
  }
2563
2571
  } else if (isResponseFinishedChunk(value)) {
2564
- finishReason = {
2565
- unified: mapOpenAIResponseFinishReason({
2566
- finishReason: value.response.incomplete_details?.reason,
2567
- hasFunctionCall,
2568
- }),
2569
- raw: value.response.incomplete_details?.reason ?? undefined,
2570
- };
2572
+ if (!encounteredStreamError) {
2573
+ finishReason = {
2574
+ unified: mapOpenAIResponseFinishReason({
2575
+ finishReason: value.response.incomplete_details?.reason,
2576
+ hasFunctionCall,
2577
+ }),
2578
+ raw: value.response.incomplete_details?.reason ?? undefined,
2579
+ };
2580
+ }
2571
2581
  usage = value.response.usage;
2572
2582
  if (typeof value.response.service_tier === 'string') {
2573
2583
  serviceTier = value.response.service_tier;
@@ -2594,17 +2604,18 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2594
2604
 
2595
2605
  if (!encounteredStreamError && value.response.error != null) {
2596
2606
  encounteredStreamError = true;
2607
+ const error = {
2608
+ type: 'response.failed',
2609
+ sequence_number: value.sequence_number,
2610
+ response: {
2611
+ error: value.response.error,
2612
+ incomplete_details: value.response.incomplete_details,
2613
+ service_tier: value.response.service_tier,
2614
+ },
2615
+ };
2597
2616
  controller.enqueue({
2598
2617
  type: 'error',
2599
- error: {
2600
- type: 'response.failed',
2601
- sequence_number: value.sequence_number,
2602
- response: {
2603
- error: value.response.error,
2604
- incomplete_details: value.response.incomplete_details,
2605
- service_tier: value.response.service_tier,
2606
- },
2607
- },
2618
+ error: createOpenAIProviderStreamError(error) ?? error,
2608
2619
  });
2609
2620
  }
2610
2621
  } else if (isResponseAnnotationAddedChunk(value)) {
@@ -2678,7 +2689,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2678
2689
  } else if (isErrorChunk(value)) {
2679
2690
  encounteredStreamError = true;
2680
2691
  finishReason = { unified: 'error', raw: 'error' };
2681
- controller.enqueue({ type: 'error', error: value });
2692
+ controller.enqueue({
2693
+ type: 'error',
2694
+ error: createOpenAIProviderStreamError(value) ?? value,
2695
+ });
2682
2696
  }
2683
2697
  },
2684
2698