@ai-sdk/openai 4.0.0-canary.70 → 4.0.0-canary.71

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @ai-sdk/openai
2
2
 
3
+ ## 4.0.0-canary.71
4
+
5
+ ### Patch Changes
6
+
7
+ - ae7f932: fix(openai): throw retryable errors for OpenAI stream failures before output starts
8
+
3
9
  ## 4.0.0-canary.70
4
10
 
5
11
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -209,6 +209,21 @@ type OpenAIResponsesFileSearchToolCompoundFilter = {
209
209
  declare const openaiResponsesChunkSchema: _ai_sdk_provider_utils.LazySchema<{
210
210
  type: "unknown_chunk";
211
211
  message: string;
212
+ } | {
213
+ type: "error";
214
+ sequence_number: number;
215
+ error: {
216
+ type: string;
217
+ code: string;
218
+ message: string;
219
+ param?: string | null | undefined;
220
+ };
221
+ } | {
222
+ type: "error";
223
+ sequence_number: number;
224
+ message: string;
225
+ code?: string | null | undefined;
226
+ param?: string | null | undefined;
212
227
  } | {
213
228
  type: "response.output_text.delta";
214
229
  item_id: string;
@@ -241,6 +256,7 @@ declare const openaiResponsesChunkSchema: _ai_sdk_provider_utils.LazySchema<{
241
256
  };
242
257
  } | {
243
258
  type: "response.failed";
259
+ sequence_number: number;
244
260
  response: {
245
261
  error?: {
246
262
  message: string;
@@ -653,15 +669,6 @@ declare const openaiResponsesChunkSchema: _ai_sdk_provider_utils.LazySchema<{
653
669
  item_id: string;
654
670
  output_index: number;
655
671
  diff: string;
656
- } | {
657
- type: "error";
658
- sequence_number: number;
659
- error: {
660
- type: string;
661
- code: string;
662
- message: string;
663
- param?: string | null | undefined;
664
- };
665
672
  }>;
666
673
  type OpenAIResponsesChunk = InferSchema<typeof openaiResponsesChunkSchema>;
667
674
  type OpenAIResponsesLogprobs = NonNullable<(OpenAIResponsesChunk & {
package/dist/index.js CHANGED
@@ -56,6 +56,129 @@ function getOpenAILanguageModelCapabilities(modelId) {
56
56
  };
57
57
  }
58
58
 
59
+ // src/openai-stream-error.ts
60
+ import { APICallError } from "@ai-sdk/provider";
61
+ async function throwIfOpenAIStreamErrorBeforeOutput({
62
+ stream,
63
+ getError,
64
+ isOutputChunk,
65
+ url,
66
+ requestBodyValues,
67
+ responseHeaders
68
+ }) {
69
+ const [streamForEarlyError, streamForConsumer] = stream.tee();
70
+ const reader = streamForEarlyError.getReader();
71
+ try {
72
+ while (true) {
73
+ const result = await reader.read();
74
+ if (result.done) {
75
+ return streamForConsumer;
76
+ }
77
+ const chunk = result.value;
78
+ if (!chunk.success) {
79
+ return streamForConsumer;
80
+ }
81
+ const errorFrame = getError(chunk.value);
82
+ if (errorFrame != null) {
83
+ streamForConsumer.cancel().catch(() => {
84
+ });
85
+ throw createOpenAIStreamError({
86
+ frame: errorFrame,
87
+ url,
88
+ requestBodyValues,
89
+ responseHeaders
90
+ });
91
+ }
92
+ if (isOutputChunk(chunk.value)) {
93
+ return streamForConsumer;
94
+ }
95
+ }
96
+ } finally {
97
+ reader.cancel().catch(() => {
98
+ });
99
+ reader.releaseLock();
100
+ }
101
+ }
102
+ function createOpenAIStreamError({
103
+ frame,
104
+ url,
105
+ requestBodyValues,
106
+ responseHeaders
107
+ }) {
108
+ var _a;
109
+ const streamError = parseStreamError(frame);
110
+ return new APICallError({
111
+ message: (_a = streamError == null ? void 0 : streamError.message) != null ? _a : "OpenAI stream failed before any output was generated",
112
+ url,
113
+ requestBodyValues,
114
+ statusCode: streamError == null ? 500 : getStatusCode(streamError),
115
+ responseHeaders,
116
+ responseBody: JSON.stringify(frame),
117
+ data: frame
118
+ });
119
+ }
120
+ function parseStreamError(frame) {
121
+ var _a;
122
+ const value = asRecord(frame);
123
+ if (value == null) {
124
+ return void 0;
125
+ }
126
+ if (value.type === "response.failed") {
127
+ const response = asRecord(value.response);
128
+ const responseError = asRecord(response == null ? void 0 : response.error);
129
+ return typeof (responseError == null ? void 0 : responseError.message) === "string" ? {
130
+ message: responseError.message,
131
+ code: getStringOrNumber(responseError.code),
132
+ type: "response.failed",
133
+ frame
134
+ } : void 0;
135
+ }
136
+ const error = (_a = asRecord(value.error)) != null ? _a : value;
137
+ return typeof error.message === "string" && (asRecord(value.error) != null || typeof error.type === "string" || "code" in error || "param" in error) ? {
138
+ message: error.message,
139
+ code: getStringOrNumber(error.code),
140
+ type: typeof error.type === "string" ? error.type : void 0,
141
+ frame
142
+ } : void 0;
143
+ }
144
+ function getStatusCode(error) {
145
+ if (typeof error.code === "number" && isHttpErrorStatusCode(error.code)) {
146
+ return error.code;
147
+ }
148
+ if (typeof error.code === "string" && /^\d{3}$/.test(error.code)) {
149
+ const numericCode = Number(error.code);
150
+ if (isHttpErrorStatusCode(numericCode)) {
151
+ return numericCode;
152
+ }
153
+ }
154
+ const discriminator = [error.code, error.type].filter((value) => typeof value === "string" || typeof value === "number").join(" ").toLowerCase();
155
+ if (["insufficient_quota", "rate_limit"].some(
156
+ (term) => discriminator.includes(term)
157
+ )) {
158
+ return 429;
159
+ }
160
+ if (discriminator.includes("authentication")) return 401;
161
+ if (discriminator.includes("permission")) return 403;
162
+ if (discriminator.includes("not_found")) return 404;
163
+ if (["invalid", "bad_request", "context_length"].some(
164
+ (term) => discriminator.includes(term)
165
+ )) {
166
+ return 400;
167
+ }
168
+ if (discriminator.includes("overload")) return 503;
169
+ if (discriminator.includes("timeout")) return 504;
170
+ return 500;
171
+ }
172
+ function asRecord(value) {
173
+ return typeof value === "object" && value != null ? value : void 0;
174
+ }
175
+ function getStringOrNumber(value) {
176
+ return typeof value === "string" || typeof value === "number" ? value : void 0;
177
+ }
178
+ function isHttpErrorStatusCode(value) {
179
+ return Number.isInteger(value) && value >= 400 && value <= 599;
180
+ }
181
+
59
182
  // src/chat/convert-openai-chat-usage.ts
60
183
  function convertOpenAIChatUsage(usage) {
61
184
  var _a, _b, _c, _d, _e, _f;
@@ -962,11 +1085,12 @@ var OpenAIChatLanguageModel = class _OpenAIChatLanguageModel {
962
1085
  include_usage: true
963
1086
  }
964
1087
  };
1088
+ const url = this.config.url({
1089
+ path: "/chat/completions",
1090
+ modelId: this.modelId
1091
+ });
965
1092
  const { responseHeaders, value: response } = await postJsonToApi({
966
- url: this.config.url({
967
- path: "/chat/completions",
968
- modelId: this.modelId
969
- }),
1093
+ url,
970
1094
  headers: combineHeaders((_b = (_a = this.config).headers) == null ? void 0 : _b.call(_a), options.headers),
971
1095
  body,
972
1096
  failedResponseHandler: openaiFailedResponseHandler,
@@ -976,6 +1100,14 @@ var OpenAIChatLanguageModel = class _OpenAIChatLanguageModel {
976
1100
  abortSignal: options.abortSignal,
977
1101
  fetch: this.config.fetch
978
1102
  });
1103
+ const checkedResponse = await throwIfOpenAIStreamErrorBeforeOutput({
1104
+ stream: response,
1105
+ getError: (chunk) => "error" in chunk ? chunk.error : void 0,
1106
+ isOutputChunk: isOpenAIChatOutputChunk,
1107
+ url,
1108
+ requestBodyValues: body,
1109
+ responseHeaders
1110
+ });
979
1111
  let toolCallTracker;
980
1112
  let finishReason = {
981
1113
  unified: "other",
@@ -985,8 +1117,8 @@ var OpenAIChatLanguageModel = class _OpenAIChatLanguageModel {
985
1117
  let metadataExtracted = false;
986
1118
  let isActiveText = false;
987
1119
  const providerMetadata = { openai: {} };
988
- return {
989
- stream: response.pipeThrough(
1120
+ const result = {
1121
+ stream: checkedResponse.pipeThrough(
990
1122
  new TransformStream({
991
1123
  start(controller) {
992
1124
  toolCallTracker = new StreamingToolCallTracker(controller, {
@@ -1089,8 +1221,18 @@ var OpenAIChatLanguageModel = class _OpenAIChatLanguageModel {
1089
1221
  request: { body },
1090
1222
  response: { headers: responseHeaders }
1091
1223
  };
1224
+ return result;
1092
1225
  }
1093
1226
  };
1227
+ function isOpenAIChatOutputChunk(chunk) {
1228
+ if ("error" in chunk) {
1229
+ return false;
1230
+ }
1231
+ return chunk.choices.some((choice) => {
1232
+ const delta = choice.delta;
1233
+ return (delta == null ? void 0 : delta.content) != null && delta.content.length > 0 || (delta == null ? void 0 : delta.tool_calls) != null && delta.tool_calls.length > 0 || (delta == null ? void 0 : delta.annotations) != null && delta.annotations.length > 0;
1234
+ });
1235
+ }
1094
1236
 
1095
1237
  // src/completion/openai-completion-language-model.ts
1096
1238
  import {
@@ -1510,11 +1652,12 @@ var OpenAICompletionLanguageModel = class _OpenAICompletionLanguageModel {
1510
1652
  include_usage: true
1511
1653
  }
1512
1654
  };
1655
+ const url = this.config.url({
1656
+ path: "/completions",
1657
+ modelId: this.modelId
1658
+ });
1513
1659
  const { responseHeaders, value: response } = await postJsonToApi2({
1514
- url: this.config.url({
1515
- path: "/completions",
1516
- modelId: this.modelId
1517
- }),
1660
+ url,
1518
1661
  headers: combineHeaders2((_b = (_a = this.config).headers) == null ? void 0 : _b.call(_a), options.headers),
1519
1662
  body,
1520
1663
  failedResponseHandler: openaiFailedResponseHandler,
@@ -1524,6 +1667,14 @@ var OpenAICompletionLanguageModel = class _OpenAICompletionLanguageModel {
1524
1667
  abortSignal: options.abortSignal,
1525
1668
  fetch: this.config.fetch
1526
1669
  });
1670
+ const checkedResponse = await throwIfOpenAIStreamErrorBeforeOutput({
1671
+ stream: response,
1672
+ getError: (chunk) => "error" in chunk ? chunk.error : void 0,
1673
+ isOutputChunk: isOpenAICompletionOutputChunk,
1674
+ url,
1675
+ requestBodyValues: body,
1676
+ responseHeaders
1677
+ });
1527
1678
  let finishReason = {
1528
1679
  unified: "other",
1529
1680
  raw: void 0
@@ -1531,8 +1682,8 @@ var OpenAICompletionLanguageModel = class _OpenAICompletionLanguageModel {
1531
1682
  const providerMetadata = { openai: {} };
1532
1683
  let usage = void 0;
1533
1684
  let isFirstChunk = true;
1534
- return {
1535
- stream: response.pipeThrough(
1685
+ const result = {
1686
+ stream: checkedResponse.pipeThrough(
1536
1687
  new TransformStream({
1537
1688
  start(controller) {
1538
1689
  controller.enqueue({ type: "stream-start", warnings });
@@ -1597,8 +1748,12 @@ var OpenAICompletionLanguageModel = class _OpenAICompletionLanguageModel {
1597
1748
  request: { body },
1598
1749
  response: { headers: responseHeaders }
1599
1750
  };
1751
+ return result;
1600
1752
  }
1601
1753
  };
1754
+ function isOpenAICompletionOutputChunk(chunk) {
1755
+ return !("error" in chunk) && chunk.choices.some((choice) => choice.text.length > 0);
1756
+ }
1602
1757
 
1603
1758
  // src/embedding/openai-embedding-model.ts
1604
1759
  import {
@@ -3260,7 +3415,7 @@ var OpenAIRealtimeModel = class {
3260
3415
 
3261
3416
  // src/responses/openai-responses-language-model.ts
3262
3417
  import {
3263
- APICallError
3418
+ APICallError as APICallError2
3264
3419
  } from "@ai-sdk/provider";
3265
3420
  import {
3266
3421
  combineHeaders as combineHeaders6,
@@ -4062,6 +4217,23 @@ var jsonValueSchema2 = z24.lazy(
4062
4217
  z24.record(z24.string(), jsonValueSchema2.optional())
4063
4218
  ])
4064
4219
  );
4220
+ var openaiResponsesNestedErrorChunkSchema = z24.object({
4221
+ type: z24.literal("error"),
4222
+ sequence_number: z24.number(),
4223
+ error: z24.object({
4224
+ type: z24.string(),
4225
+ code: z24.string(),
4226
+ message: z24.string(),
4227
+ param: z24.string().nullish()
4228
+ })
4229
+ });
4230
+ var openaiResponsesErrorChunkSchema = z24.object({
4231
+ type: z24.literal("error"),
4232
+ sequence_number: z24.number(),
4233
+ code: z24.string().nullish(),
4234
+ message: z24.string(),
4235
+ param: z24.string().nullish()
4236
+ });
4065
4237
  var openaiResponsesChunkSchema = lazySchema22(
4066
4238
  () => zodSchema22(
4067
4239
  z24.union([
@@ -4097,6 +4269,7 @@ var openaiResponsesChunkSchema = lazySchema22(
4097
4269
  }),
4098
4270
  z24.object({
4099
4271
  type: z24.literal("response.failed"),
4272
+ sequence_number: z24.number(),
4100
4273
  response: z24.object({
4101
4274
  error: z24.object({
4102
4275
  code: z24.string().nullish(),
@@ -4588,16 +4761,8 @@ var openaiResponsesChunkSchema = lazySchema22(
4588
4761
  output_index: z24.number(),
4589
4762
  diff: z24.string()
4590
4763
  }),
4591
- z24.object({
4592
- type: z24.literal("error"),
4593
- sequence_number: z24.number(),
4594
- error: z24.object({
4595
- type: z24.string(),
4596
- code: z24.string(),
4597
- message: z24.string(),
4598
- param: z24.string().nullish()
4599
- })
4600
- }),
4764
+ openaiResponsesNestedErrorChunkSchema,
4765
+ openaiResponsesErrorChunkSchema,
4601
4766
  z24.object({ type: z24.string() }).loose().transform((value) => ({
4602
4767
  type: "unknown_chunk",
4603
4768
  message: value.type
@@ -5827,7 +5992,7 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
5827
5992
  fetch: this.config.fetch
5828
5993
  });
5829
5994
  if (response.error) {
5830
- throw new APICallError({
5995
+ throw new APICallError2({
5831
5996
  message: response.error.message,
5832
5997
  url,
5833
5998
  requestBodyValues: body,
@@ -6297,11 +6462,12 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
6297
6462
  providerOptionsName,
6298
6463
  isShellProviderExecuted
6299
6464
  } = await this.getArgs(options);
6465
+ const url = this.config.url({
6466
+ path: "/responses",
6467
+ modelId: this.modelId
6468
+ });
6300
6469
  const { responseHeaders, value: response } = await postJsonToApi5({
6301
- url: this.config.url({
6302
- path: "/responses",
6303
- modelId: this.modelId
6304
- }),
6470
+ url,
6305
6471
  headers: combineHeaders6((_b = (_a = this.config).headers) == null ? void 0 : _b.call(_a), options.headers),
6306
6472
  body: {
6307
6473
  ...body,
@@ -6314,6 +6480,14 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
6314
6480
  abortSignal: options.abortSignal,
6315
6481
  fetch: this.config.fetch
6316
6482
  });
6483
+ const checkedResponse = await throwIfOpenAIStreamErrorBeforeOutput({
6484
+ stream: response,
6485
+ getError: (chunk) => isErrorChunk(chunk) || isResponseFailedChunk(chunk) && chunk.response.error != null ? chunk : void 0,
6486
+ isOutputChunk: isResponseOutputChunk,
6487
+ url,
6488
+ requestBodyValues: body,
6489
+ responseHeaders
6490
+ });
6317
6491
  const self = this;
6318
6492
  const approvalRequestIdToDummyToolCallIdFromPrompt = extractApprovalRequestIdToToolCallIdMapping(options.prompt);
6319
6493
  const approvalRequestIdToDummyToolCallIdFromStream = /* @__PURE__ */ new Map();
@@ -6331,8 +6505,9 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
6331
6505
  const activeReasoning = {};
6332
6506
  let serviceTier;
6333
6507
  const hostedToolSearchCallIds = [];
6334
- return {
6335
- stream: response.pipeThrough(
6508
+ let encounteredStreamError = false;
6509
+ const result = {
6510
+ stream: checkedResponse.pipeThrough(
6336
6511
  new TransformStream({
6337
6512
  start(controller) {
6338
6513
  controller.enqueue({ type: "stream-start", warnings });
@@ -6645,12 +6820,12 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
6645
6820
  toolName: toolNameMapping.toCustomToolName("file_search"),
6646
6821
  result: {
6647
6822
  queries: value.item.queries,
6648
- results: (_f = (_e = value.item.results) == null ? void 0 : _e.map((result) => ({
6649
- attributes: result.attributes,
6650
- fileId: result.file_id,
6651
- filename: result.filename,
6652
- score: result.score,
6653
- text: result.text
6823
+ results: (_f = (_e = value.item.results) == null ? void 0 : _e.map((result2) => ({
6824
+ attributes: result2.attributes,
6825
+ fileId: result2.file_id,
6826
+ filename: result2.filename,
6827
+ score: result2.score,
6828
+ text: result2.text
6654
6829
  }))) != null ? _f : null
6655
6830
  }
6656
6831
  });
@@ -7095,6 +7270,21 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
7095
7270
  raw: incompleteReason != null ? incompleteReason : "error"
7096
7271
  };
7097
7272
  usage = (_z = value.response.usage) != null ? _z : void 0;
7273
+ if (!encounteredStreamError && value.response.error != null) {
7274
+ encounteredStreamError = true;
7275
+ controller.enqueue({
7276
+ type: "error",
7277
+ error: {
7278
+ type: "response.failed",
7279
+ sequence_number: value.sequence_number,
7280
+ response: {
7281
+ error: value.response.error,
7282
+ incomplete_details: value.response.incomplete_details,
7283
+ service_tier: value.response.service_tier
7284
+ }
7285
+ }
7286
+ });
7287
+ }
7098
7288
  } else if (isResponseAnnotationAddedChunk(value)) {
7099
7289
  ongoingAnnotations.push(value.annotation);
7100
7290
  if (value.annotation.type === "url_citation") {
@@ -7155,6 +7345,8 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
7155
7345
  });
7156
7346
  }
7157
7347
  } else if (isErrorChunk(value)) {
7348
+ encounteredStreamError = true;
7349
+ finishReason = { unified: "error", raw: "error" };
7158
7350
  controller.enqueue({ type: "error", error: value });
7159
7351
  }
7160
7352
  },
@@ -7178,6 +7370,7 @@ var OpenAIResponsesLanguageModel = class _OpenAIResponsesLanguageModel {
7178
7370
  request: { body },
7179
7371
  response: { headers: responseHeaders }
7180
7372
  };
7373
+ return result;
7181
7374
  }
7182
7375
  };
7183
7376
  function isTextDeltaChunk(chunk) {
@@ -7225,6 +7418,9 @@ function isResponseAnnotationAddedChunk(chunk) {
7225
7418
  function isErrorChunk(chunk) {
7226
7419
  return chunk.type === "error";
7227
7420
  }
7421
+ function isResponseOutputChunk(chunk) {
7422
+ return !(chunk.type === "response.created" || chunk.type === "response.failed" || chunk.type === "error" || chunk.type === "unknown_chunk");
7423
+ }
7228
7424
  function mapWebSearchOutput(action) {
7229
7425
  var _a;
7230
7426
  if (action == null) {
@@ -7742,7 +7938,7 @@ var OpenAISkills = class {
7742
7938
  };
7743
7939
 
7744
7940
  // src/version.ts
7745
- var VERSION = true ? "4.0.0-canary.70" : "0.0.0-test";
7941
+ var VERSION = true ? "4.0.0-canary.71" : "0.0.0-test";
7746
7942
 
7747
7943
  // src/openai-provider.ts
7748
7944
  function createOpenAI(options = {}) {