@ai-sdk/openai 4.0.47 → 4.0.49

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,
@@ -1473,6 +1476,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1473
1476
  })
1474
1477
  : chunk.error;
1475
1478
 
1479
+ encounteredStreamError = true;
1476
1480
  finishReason = { unified: 'error', raw: undefined };
1477
1481
  controller.enqueue({ type: 'error', error });
1478
1482
  return;
@@ -2561,13 +2565,15 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2561
2565
  }
2562
2566
  }
2563
2567
  } 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
- };
2568
+ if (!encounteredStreamError) {
2569
+ finishReason = {
2570
+ unified: mapOpenAIResponseFinishReason({
2571
+ finishReason: value.response.incomplete_details?.reason,
2572
+ hasFunctionCall,
2573
+ }),
2574
+ raw: value.response.incomplete_details?.reason ?? undefined,
2575
+ };
2576
+ }
2571
2577
  usage = value.response.usage;
2572
2578
  if (typeof value.response.service_tier === 'string') {
2573
2579
  serviceTier = value.response.service_tier;
@@ -2594,17 +2600,18 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2594
2600
 
2595
2601
  if (!encounteredStreamError && value.response.error != null) {
2596
2602
  encounteredStreamError = true;
2603
+ const error = {
2604
+ type: 'response.failed',
2605
+ sequence_number: value.sequence_number,
2606
+ response: {
2607
+ error: value.response.error,
2608
+ incomplete_details: value.response.incomplete_details,
2609
+ service_tier: value.response.service_tier,
2610
+ },
2611
+ };
2597
2612
  controller.enqueue({
2598
2613
  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
- },
2614
+ error: createOpenAIProviderStreamError(error) ?? error,
2608
2615
  });
2609
2616
  }
2610
2617
  } else if (isResponseAnnotationAddedChunk(value)) {
@@ -2678,7 +2685,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2678
2685
  } else if (isErrorChunk(value)) {
2679
2686
  encounteredStreamError = true;
2680
2687
  finishReason = { unified: 'error', raw: 'error' };
2681
- controller.enqueue({ type: 'error', error: value });
2688
+ controller.enqueue({
2689
+ type: 'error',
2690
+ error: createOpenAIProviderStreamError(value) ?? value,
2691
+ });
2682
2692
  }
2683
2693
  },
2684
2694