@ai-sdk/openai 3.0.100 → 3.0.102

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.100",
3
+ "version": "3.0.102",
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.47"
40
+ "@ai-sdk/provider-utils": "4.0.48"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "20.17.24",
@@ -52,7 +52,7 @@ export function convertOpenAIChatUsage(
52
52
  },
53
53
  outputTokens: {
54
54
  total: completionTokens,
55
- text: completionTokens - reasoningTokens,
55
+ text: Math.max(0, completionTokens - reasoningTokens),
56
56
  reasoning: reasoningTokens,
57
57
  },
58
58
  raw: usage,
@@ -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(),
@@ -1222,6 +1222,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1222
1222
  })
1223
1223
  : chunk.error;
1224
1224
 
1225
+ encounteredStreamError = true;
1225
1226
  finishReason = { unified: 'error', raw: undefined };
1226
1227
  controller.enqueue({ type: 'error', error });
1227
1228
  return;
@@ -2189,13 +2190,15 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
2189
2190
  ] = 'can-conclude';
2190
2191
  }
2191
2192
  } 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
- };
2193
+ if (!encounteredStreamError) {
2194
+ finishReason = {
2195
+ unified: mapOpenAIResponseFinishReason({
2196
+ finishReason: value.response.incomplete_details?.reason,
2197
+ hasFunctionCall,
2198
+ }),
2199
+ raw: value.response.incomplete_details?.reason ?? undefined,
2200
+ };
2201
+ }
2199
2202
  usage = value.response.usage;
2200
2203
  if (typeof value.response.service_tier === 'string') {
2201
2204
  serviceTier = value.response.service_tier;