@ai-sdk/provider-utils 3.0.35 → 3.0.36

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/provider-utils
2
2
 
3
+ ## 3.0.36
4
+
5
+ ### Patch Changes
6
+
7
+ - 26165ee: Mark transient network errors that occur while reading successful response bodies as retryable, including AI Gateway responses.
8
+
3
9
  ## 3.0.35
4
10
 
5
11
  ### Patch Changes
package/dist/index.js CHANGED
@@ -685,6 +685,32 @@ function isAbortError(error) {
685
685
 
686
686
  // src/handle-fetch-error.ts
687
687
  var FETCH_FAILED_ERROR_MESSAGES = ["fetch failed", "failed to fetch"];
688
+ var RETRYABLE_NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
689
+ "ConnectionRefused",
690
+ "ConnectionClosed",
691
+ "FailedToOpenSocket",
692
+ "ECONNRESET",
693
+ "ECONNREFUSED",
694
+ "ETIMEDOUT",
695
+ "EPIPE",
696
+ "UND_ERR_SOCKET",
697
+ "UND_ERR_HEADERS_TIMEOUT",
698
+ "UND_ERR_BODY_TIMEOUT",
699
+ "UND_ERR_CONNECT_TIMEOUT"
700
+ ]);
701
+ function findNetworkError(error) {
702
+ const visited = /* @__PURE__ */ new Set();
703
+ let current = error;
704
+ while (current instanceof Error && !visited.has(current)) {
705
+ visited.add(current);
706
+ const errorWithCode = current;
707
+ if (typeof errorWithCode.code === "string" && RETRYABLE_NETWORK_ERROR_CODES.has(errorWithCode.code)) {
708
+ return errorWithCode;
709
+ }
710
+ current = current.cause;
711
+ }
712
+ return void 0;
713
+ }
688
714
  function handleFetchError({
689
715
  error,
690
716
  url,
@@ -706,6 +732,29 @@ function handleFetchError({
706
732
  });
707
733
  }
708
734
  }
735
+ const networkError = findNetworkError(error);
736
+ if (networkError != null) {
737
+ if (import_provider3.APICallError.isInstance(error)) {
738
+ return new import_provider3.APICallError({
739
+ message: error.message,
740
+ cause: error.cause,
741
+ url: error.url,
742
+ requestBodyValues: error.requestBodyValues,
743
+ statusCode: error.statusCode,
744
+ responseHeaders: error.responseHeaders,
745
+ responseBody: error.responseBody,
746
+ data: error.data,
747
+ isRetryable: true
748
+ });
749
+ }
750
+ return new import_provider3.APICallError({
751
+ message: `Cannot connect to API: ${error instanceof Error ? error.message : networkError.message}`,
752
+ cause: error,
753
+ url,
754
+ requestBodyValues,
755
+ isRetryable: true
756
+ });
757
+ }
709
758
  return error;
710
759
  }
711
760
 
@@ -762,7 +811,7 @@ function withUserAgentSuffix(headers, ...userAgentSuffixParts) {
762
811
  }
763
812
 
764
813
  // src/version.ts
765
- var VERSION = true ? "3.0.35" : "0.0.0-test";
814
+ var VERSION = true ? "3.0.36" : "0.0.0-test";
766
815
 
767
816
  // src/get-from-api.ts
768
817
  var getOriginalFetch = () => globalThis.fetch;
@@ -1409,6 +1458,62 @@ async function resolve(value) {
1409
1458
  // src/response-handler.ts
1410
1459
  var import_provider12 = require("@ai-sdk/provider");
1411
1460
  var textDecoder = new TextDecoder();
1461
+ function wrapResponseBodyStream({
1462
+ stream,
1463
+ url,
1464
+ requestBodyValues,
1465
+ statusCode,
1466
+ responseHeaders
1467
+ }) {
1468
+ const reader = stream.getReader();
1469
+ let readerReleased = false;
1470
+ const releaseReader = () => {
1471
+ if (!readerReleased) {
1472
+ reader.releaseLock();
1473
+ readerReleased = true;
1474
+ }
1475
+ };
1476
+ return new ReadableStream({
1477
+ async pull(controller) {
1478
+ try {
1479
+ const { done, value } = await reader.read();
1480
+ if (done) {
1481
+ releaseReader();
1482
+ controller.close();
1483
+ } else {
1484
+ controller.enqueue(value);
1485
+ }
1486
+ } catch (error) {
1487
+ releaseReader();
1488
+ if (isAbortError(error)) {
1489
+ controller.error(error);
1490
+ return;
1491
+ }
1492
+ controller.error(
1493
+ handleFetchError({
1494
+ error: new import_provider12.APICallError({
1495
+ message: "Failed to process successful response",
1496
+ cause: error,
1497
+ statusCode,
1498
+ url,
1499
+ responseHeaders,
1500
+ requestBodyValues
1501
+ }),
1502
+ url,
1503
+ requestBodyValues
1504
+ })
1505
+ );
1506
+ }
1507
+ },
1508
+ async cancel(reason) {
1509
+ try {
1510
+ await reader.cancel(reason);
1511
+ } finally {
1512
+ releaseReader();
1513
+ }
1514
+ }
1515
+ });
1516
+ }
1412
1517
  async function readResponseBodyAsText({
1413
1518
  response,
1414
1519
  url
@@ -1474,7 +1579,7 @@ var createJsonErrorResponseHandler = ({
1474
1579
  };
1475
1580
  }
1476
1581
  };
1477
- var createEventSourceResponseHandler = (chunkSchema) => async ({ response }) => {
1582
+ var createEventSourceResponseHandler = (chunkSchema) => async ({ response, url, requestBodyValues }) => {
1478
1583
  const responseHeaders = extractResponseHeaders(response);
1479
1584
  if (response.body == null) {
1480
1585
  throw new import_provider12.EmptyResponseBodyError({});
@@ -1482,7 +1587,13 @@ var createEventSourceResponseHandler = (chunkSchema) => async ({ response }) =>
1482
1587
  return {
1483
1588
  responseHeaders,
1484
1589
  value: parseJsonEventStream({
1485
- stream: response.body,
1590
+ stream: wrapResponseBodyStream({
1591
+ stream: response.body,
1592
+ url,
1593
+ requestBodyValues,
1594
+ statusCode: response.status,
1595
+ responseHeaders
1596
+ }),
1486
1597
  schema: chunkSchema
1487
1598
  })
1488
1599
  };