New:Socket for Asana Is Now Available.Learn more
Get Started

@ai-sdk/provider-utils

Package Overview
Dependencies
Maintainers
3
Versions
369
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ai-sdk/provider-utils - npm Package Compare versions

Comparing version
4.0.49
to
4.0.50
+6
-0
CHANGELOG.md
# @ai-sdk/provider-utils
## 4.0.50
### Patch Changes
- cc23556: Mark transient network errors that occur while reading successful response bodies as retryable.
## 4.0.49

@@ -4,0 +10,0 @@

+1
-1
{
"name": "@ai-sdk/provider-utils",
"version": "4.0.49",
"version": "4.0.50",
"license": "Apache-2.0",

@@ -5,0 +5,0 @@ "sideEffects": false,

@@ -6,3 +6,3 @@ import { APICallError } from '@ai-sdk/provider';

const BUN_ERROR_CODES = [
const RETRYABLE_NETWORK_ERROR_CODES = new Set([
'ConnectionRefused',

@@ -15,15 +15,29 @@ 'ConnectionClosed',

'EPIPE',
];
'UND_ERR_SOCKET',
'UND_ERR_HEADERS_TIMEOUT',
'UND_ERR_BODY_TIMEOUT',
'UND_ERR_CONNECT_TIMEOUT',
]);
function isBunNetworkError(error: unknown): error is Error & { code?: string } {
if (!(error instanceof Error)) {
return false;
}
function findNetworkError(
error: unknown,
): (Error & { code?: unknown }) | undefined {
const visited = new Set<Error>();
let current = error;
const code = (error as any).code;
if (typeof code === 'string' && BUN_ERROR_CODES.includes(code)) {
return true;
while (current instanceof Error && !visited.has(current)) {
visited.add(current);
const errorWithCode = current as Error & { code?: unknown };
if (
typeof errorWithCode.code === 'string' &&
RETRYABLE_NETWORK_ERROR_CODES.has(errorWithCode.code)
) {
return errorWithCode;
}
current = (current as Error & { cause?: unknown }).cause;
}
return false;
return undefined;
}

@@ -63,5 +77,23 @@

if (isBunNetworkError(error)) {
const networkError = findNetworkError(error);
if (networkError != null) {
if (APICallError.isInstance(error)) {
return new APICallError({
message: error.message,
cause: error.cause,
url: error.url,
requestBodyValues: error.requestBodyValues,
statusCode: error.statusCode,
responseHeaders: error.responseHeaders,
responseBody: error.responseBody,
data: error.data,
isRetryable: true,
});
}
return new APICallError({
message: `Cannot connect to API: ${error.message}`,
message: `Cannot connect to API: ${
error instanceof Error ? error.message : networkError.message
}`,
cause: error,

@@ -68,0 +100,0 @@ url,

import { APICallError, EmptyResponseBodyError } from '@ai-sdk/provider';
import { extractResponseHeaders } from './extract-response-headers';
import { handleFetchError } from './handle-fetch-error';
import { isAbortError } from './is-abort-error';
import { parseJSON, safeParseJSON, type ParseResult } from './parse-json';

@@ -20,2 +22,70 @@ import { parseJsonEventStream } from './parse-json-event-stream';

function wrapResponseBodyStream({
stream,
url,
requestBodyValues,
statusCode,
responseHeaders,
}: {
stream: ReadableStream<Uint8Array>;
url: string;
requestBodyValues: unknown;
statusCode: number;
responseHeaders: Record<string, string>;
}): ReadableStream<Uint8Array> {
const reader = stream.getReader();
let readerReleased = false;
const releaseReader = () => {
if (!readerReleased) {
reader.releaseLock();
readerReleased = true;
}
};
return new ReadableStream<Uint8Array>({
async pull(controller) {
try {
const { done, value } = await reader.read();
if (done) {
releaseReader();
controller.close();
} else {
controller.enqueue(value);
}
} catch (error) {
releaseReader();
if (isAbortError(error)) {
controller.error(error);
return;
}
controller.error(
handleFetchError({
error: new APICallError({
message: 'Failed to process successful response',
cause: error,
statusCode,
url,
responseHeaders,
requestBodyValues,
}),
url,
requestBodyValues,
}),
);
}
},
async cancel(reason) {
try {
await reader.cancel(reason);
} finally {
releaseReader();
}
},
});
}
async function readResponseBodyAsText({

@@ -106,3 +176,3 @@ response,

): ResponseHandler<ReadableStream<ParseResult<T>>> =>
async ({ response }: { response: Response }) => {
async ({ response, url, requestBodyValues }) => {
const responseHeaders = extractResponseHeaders(response);

@@ -117,3 +187,9 @@

value: parseJsonEventStream({
stream: response.body,
stream: wrapResponseBodyStream({
stream: response.body,
url,
requestBodyValues,
statusCode: response.status,
responseHeaders,
}),
schema: chunkSchema,

@@ -120,0 +196,0 @@ }),

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display