@ai-sdk/openai 3.0.106 → 3.0.108

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.
@@ -2383,6 +2383,7 @@ The following optional provider options are available for OpenAI completion mode
2383
2383
 
2384
2384
  | Model | Image Input | Audio Input | Object Generation | Tool Usage |
2385
2385
  | --------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
2386
+ | `gpt-6-astra` | <Check size={18} /> | <Cross size={18} /> | <Check size={18} /> | <Check size={18} /> |
2386
2387
  | `gpt-5.6` | <Check size={18} /> | <Cross size={18} /> | <Check size={18} /> | <Check size={18} /> |
2387
2388
  | `gpt-5.6-luna` | <Check size={18} /> | <Cross size={18} /> | <Check size={18} /> | <Check size={18} /> |
2388
2389
  | `gpt-5.6-sol` | <Check size={18} /> | <Cross size={18} /> | <Check size={18} /> | <Check size={18} /> |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai",
3
- "version": "3.0.106",
3
+ "version": "3.0.108",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -70,6 +70,7 @@ export type OpenAIChatModelId =
70
70
  | 'gpt-5.6-luna'
71
71
  | 'gpt-5.6-sol'
72
72
  | 'gpt-5.6-terra'
73
+ | 'gpt-6-astra'
73
74
  | (string & {});
74
75
 
75
76
  export const openaiLanguageModelChatOptions = lazySchema(() =>
@@ -5,9 +5,43 @@ type StreamError = {
5
5
  message: string;
6
6
  code?: string | number | null;
7
7
  type?: string | null;
8
- frame: unknown;
9
8
  };
10
9
 
10
+ export type OpenAIProviderStreamError = {
11
+ readonly message: string;
12
+ readonly type?: string;
13
+ readonly code?: string | number;
14
+ readonly statusCode: number;
15
+ readonly isRetryable: boolean;
16
+ readonly data: unknown;
17
+ };
18
+
19
+ /**
20
+ * Flattens an OpenAI stream error frame (`{ type: 'error', error: {...} }` or
21
+ * `response.failed`) so consumers can read `message` and `code` at the top
22
+ * level. The raw frame is kept in `data`.
23
+ */
24
+ export function createOpenAIProviderStreamError(
25
+ frame: unknown,
26
+ ): OpenAIProviderStreamError | undefined {
27
+ const streamError = parseStreamError(frame);
28
+
29
+ if (streamError == null) {
30
+ return undefined;
31
+ }
32
+
33
+ const statusCode = getStatusCode(streamError);
34
+
35
+ return {
36
+ message: streamError.message,
37
+ type: streamError.type ?? undefined,
38
+ code: streamError.code ?? undefined,
39
+ statusCode,
40
+ isRetryable: isRetryableStreamError(streamError, statusCode),
41
+ data: frame,
42
+ };
43
+ }
44
+
11
45
  export async function throwIfOpenAIStreamErrorBeforeOutput<T>({
12
46
  stream,
13
47
  getError,
@@ -103,7 +137,6 @@ function parseStreamError(frame: unknown): StreamError | undefined {
103
137
  message: responseError.message,
104
138
  code: getStringOrNumber(responseError.code),
105
139
  type: 'response.failed',
106
- frame,
107
140
  }
108
141
  : undefined;
109
142
  }
@@ -119,7 +152,6 @@ function parseStreamError(frame: unknown): StreamError | undefined {
119
152
  message: error.message,
120
153
  code: getStringOrNumber(error.code),
121
154
  type: typeof error.type === 'string' ? error.type : undefined,
122
- frame,
123
155
  }
124
156
  : undefined;
125
157
  }
@@ -179,3 +211,26 @@ function getStringOrNumber(value: unknown): string | number | undefined {
179
211
  function isHttpErrorStatusCode(value: number): boolean {
180
212
  return Number.isInteger(value) && value >= 400 && value <= 599;
181
213
  }
214
+
215
+ function isRetryableStatusCode(statusCode: number): boolean {
216
+ return (
217
+ statusCode === 408 ||
218
+ statusCode === 409 ||
219
+ statusCode === 429 ||
220
+ statusCode >= 500
221
+ );
222
+ }
223
+
224
+ function isRetryableStreamError(
225
+ error: StreamError,
226
+ statusCode: number,
227
+ ): boolean {
228
+ if (
229
+ error.code === 'insufficient_quota' ||
230
+ error.type === 'insufficient_quota'
231
+ ) {
232
+ return false;
233
+ }
234
+
235
+ return isRetryableStatusCode(statusCode);
236
+ }
@@ -1,18 +1,23 @@
1
- import type { LanguageModelV3Usage } from '@ai-sdk/provider';
1
+ import type { JSONObject, LanguageModelV3Usage } from '@ai-sdk/provider';
2
2
 
3
- export type OpenAIResponsesUsage = {
3
+ export type OpenAIResponsesUsage = JSONObject & {
4
4
  input_tokens: number;
5
5
  output_tokens: number;
6
- input_tokens_details?: {
7
- cached_tokens?: number | null;
8
- cache_write_tokens?: number | null;
9
- orchestration_input_tokens?: number | null;
10
- orchestration_input_cached_tokens?: number | null;
11
- } | null;
12
- output_tokens_details?: {
13
- reasoning_tokens?: number | null;
14
- orchestration_output_tokens?: number | null;
15
- } | null;
6
+ total_tokens?: number;
7
+ input_tokens_details?:
8
+ | (JSONObject & {
9
+ cached_tokens?: number | null;
10
+ cache_write_tokens?: number | null;
11
+ orchestration_input_tokens?: number | null;
12
+ orchestration_input_cached_tokens?: number | null;
13
+ })
14
+ | null;
15
+ output_tokens_details?:
16
+ | (JSONObject & {
17
+ reasoning_tokens?: number | null;
18
+ orchestration_output_tokens?: number | null;
19
+ })
20
+ | null;
16
21
  };
17
22
 
18
23
  export function convertOpenAIResponsesUsage(
@@ -17,6 +17,37 @@ const jsonValueSchema: z.ZodType<JSONValue> = z.lazy(() =>
17
17
  ]),
18
18
  );
19
19
 
20
+ const jsonObjectSchema = z.record(z.string(), jsonValueSchema.optional());
21
+
22
+ const openaiResponsesUsageSchema = z.intersection(
23
+ jsonObjectSchema,
24
+ z.object({
25
+ input_tokens: z.number(),
26
+ input_tokens_details: z
27
+ .intersection(
28
+ jsonObjectSchema,
29
+ z.object({
30
+ cached_tokens: z.number().nullish(),
31
+ cache_write_tokens: z.number().nullish(),
32
+ orchestration_input_tokens: z.number().nullish(),
33
+ orchestration_input_cached_tokens: z.number().nullish(),
34
+ }),
35
+ )
36
+ .nullish(),
37
+ output_tokens: z.number(),
38
+ output_tokens_details: z
39
+ .intersection(
40
+ jsonObjectSchema,
41
+ z.object({
42
+ reasoning_tokens: z.number().nullish(),
43
+ orchestration_output_tokens: z.number().nullish(),
44
+ }),
45
+ )
46
+ .nullish(),
47
+ total_tokens: z.number().optional(),
48
+ }),
49
+ );
50
+
20
51
  function isRecord(value: unknown): value is Record<string, unknown> {
21
52
  return value != null && typeof value === 'object' && !Array.isArray(value);
22
53
  }
@@ -675,24 +706,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
675
706
  type: z.enum(['response.completed', 'response.incomplete']),
676
707
  response: z.object({
677
708
  incomplete_details: z.object({ reason: z.string() }).nullish(),
678
- usage: z.object({
679
- input_tokens: z.number(),
680
- input_tokens_details: z
681
- .object({
682
- cached_tokens: z.number().nullish(),
683
- cache_write_tokens: z.number().nullish(),
684
- orchestration_input_tokens: z.number().nullish(),
685
- orchestration_input_cached_tokens: z.number().nullish(),
686
- })
687
- .nullish(),
688
- output_tokens: z.number(),
689
- output_tokens_details: z
690
- .object({
691
- reasoning_tokens: z.number().nullish(),
692
- orchestration_output_tokens: z.number().nullish(),
693
- })
694
- .nullish(),
695
- }),
709
+ usage: openaiResponsesUsageSchema.nullish(),
696
710
  reasoning: z
697
711
  .object({
698
712
  context: z.string().nullish(),
@@ -712,26 +726,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
712
726
  })
713
727
  .nullish(),
714
728
  incomplete_details: z.object({ reason: z.string() }).nullish(),
715
- usage: z
716
- .object({
717
- input_tokens: z.number(),
718
- input_tokens_details: z
719
- .object({
720
- cached_tokens: z.number().nullish(),
721
- cache_write_tokens: z.number().nullish(),
722
- orchestration_input_tokens: z.number().nullish(),
723
- orchestration_input_cached_tokens: z.number().nullish(),
724
- })
725
- .nullish(),
726
- output_tokens: z.number(),
727
- output_tokens_details: z
728
- .object({
729
- reasoning_tokens: z.number().nullish(),
730
- orchestration_output_tokens: z.number().nullish(),
731
- })
732
- .nullish(),
733
- })
734
- .nullish(),
729
+ usage: openaiResponsesUsageSchema.nullish(),
735
730
  reasoning: z
736
731
  .object({
737
732
  context: z.string().nullish(),
@@ -1563,26 +1558,7 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1563
1558
  })
1564
1559
  .nullish(),
1565
1560
  incomplete_details: z.object({ reason: z.string() }).nullish(),
1566
- usage: z
1567
- .object({
1568
- input_tokens: z.number(),
1569
- input_tokens_details: z
1570
- .object({
1571
- cached_tokens: z.number().nullish(),
1572
- cache_write_tokens: z.number().nullish(),
1573
- orchestration_input_tokens: z.number().nullish(),
1574
- orchestration_input_cached_tokens: z.number().nullish(),
1575
- })
1576
- .nullish(),
1577
- output_tokens: z.number(),
1578
- output_tokens_details: z
1579
- .object({
1580
- reasoning_tokens: z.number().nullish(),
1581
- orchestration_output_tokens: z.number().nullish(),
1582
- })
1583
- .nullish(),
1584
- })
1585
- .optional(),
1561
+ usage: openaiResponsesUsageSchema.nullish(),
1586
1562
  }),
1587
1563
  ),
1588
1564
  );
@@ -29,7 +29,10 @@ import {
29
29
  import type { OpenAIConfig } from '../openai-config';
30
30
  import { openaiFailedResponseHandler } from '../openai-error';
31
31
  import { getOpenAILanguageModelCapabilities } from '../openai-language-model-capabilities';
32
- import { throwIfOpenAIStreamErrorBeforeOutput } from '../openai-stream-error';
32
+ import {
33
+ createOpenAIProviderStreamError,
34
+ throwIfOpenAIStreamErrorBeforeOutput,
35
+ } from '../openai-stream-error';
33
36
  import type { applyPatchInputSchema } from '../tool/apply-patch';
34
37
  import type {
35
38
  codeInterpreterInputSchema,
@@ -2203,7 +2206,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
2203
2206
  raw: value.response.incomplete_details?.reason ?? undefined,
2204
2207
  };
2205
2208
  }
2206
- usage = value.response.usage;
2209
+ usage = value.response.usage ?? undefined;
2207
2210
  if (typeof value.response.service_tier === 'string') {
2208
2211
  serviceTier = value.response.service_tier;
2209
2212
  }
@@ -2229,17 +2232,18 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
2229
2232
 
2230
2233
  if (!encounteredStreamError && value.response.error != null) {
2231
2234
  encounteredStreamError = true;
2235
+ const error = {
2236
+ type: 'response.failed',
2237
+ sequence_number: value.sequence_number,
2238
+ response: {
2239
+ error: value.response.error,
2240
+ incomplete_details: value.response.incomplete_details,
2241
+ service_tier: value.response.service_tier,
2242
+ },
2243
+ };
2232
2244
  controller.enqueue({
2233
2245
  type: 'error',
2234
- error: {
2235
- type: 'response.failed',
2236
- sequence_number: value.sequence_number,
2237
- response: {
2238
- error: value.response.error,
2239
- incomplete_details: value.response.incomplete_details,
2240
- service_tier: value.response.service_tier,
2241
- },
2242
- },
2246
+ error: createOpenAIProviderStreamError(error) ?? error,
2243
2247
  });
2244
2248
  }
2245
2249
  } else if (isResponseAnnotationAddedChunk(value)) {
@@ -2313,7 +2317,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
2313
2317
  } else if (isErrorChunk(value)) {
2314
2318
  encounteredStreamError = true;
2315
2319
  finishReason = { unified: 'error', raw: 'error' };
2316
- controller.enqueue({ type: 'error', error: value });
2320
+ controller.enqueue({
2321
+ type: 'error',
2322
+ error: createOpenAIProviderStreamError(value) ?? value,
2323
+ });
2317
2324
  }
2318
2325
  },
2319
2326
 
@@ -57,6 +57,7 @@ export const openaiResponsesReasoningModelIds = [
57
57
  'gpt-5.6-luna',
58
58
  'gpt-5.6-sol',
59
59
  'gpt-5.6-terra',
60
+ 'gpt-6-astra',
60
61
  ] as const;
61
62
 
62
63
  export const openaiResponsesModelIds = [
@@ -129,6 +130,7 @@ export type OpenAIResponsesModelId =
129
130
  | 'gpt-5.6-luna'
130
131
  | 'gpt-5.6-sol'
131
132
  | 'gpt-5.6-terra'
133
+ | 'gpt-6-astra'
132
134
  | 'gpt-5-2025-08-07'
133
135
  | 'gpt-5-chat-latest'
134
136
  | 'gpt-5-codex'