hts/packages/isdk/core/util/retry-with-exponential-back...

83 lines
2.3 KiB
TypeScript

import { APICallError, RetryError } from '@ai-sdk/provider';
import { getErrorMessage } from '../../spec';
import { delay } from './delay';
export type RetryFunction = <OUTPUT>(
fn: () => PromiseLike<OUTPUT>,
) => PromiseLike<OUTPUT>;
/**
* The `retryWithExponentialBackoff` strategy retries a failed API call with an exponential backoff.
* You can configure the maximum number of retries, the initial delay, and the backoff factor.
*/
export const retryWithExponentialBackoff =
({
maxRetries = 2,
initialDelayInMs = 2000,
backoffFactor = 2,
} = {}): RetryFunction =>
async <OUTPUT>(f: () => PromiseLike<OUTPUT>) =>
_retryWithExponentialBackoff(f, {
maxRetries,
delayInMs: initialDelayInMs,
backoffFactor,
});
async function _retryWithExponentialBackoff<OUTPUT>(
f: () => PromiseLike<OUTPUT>,
{
maxRetries,
delayInMs,
backoffFactor,
}: { maxRetries: number; delayInMs: number; backoffFactor: number },
errors: unknown[] = [],
): Promise<OUTPUT> {
try {
return await f();
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
throw error; // don't retry when the request was aborted
}
if (maxRetries === 0) {
throw error; // don't wrap the error when retries are disabled
}
const errorMessage = getErrorMessage(error);
const newErrors = [...errors, error];
const tryNumber = newErrors.length;
if (tryNumber > maxRetries) {
throw new RetryError({
message: `Failed after ${tryNumber} attemps. Last error: ${errorMessage}`,
reason: 'maxRetriesExceeded',
errors: newErrors,
});
}
if (
error instanceof Error &&
APICallError.isAPICallError(error) &&
error.isRetryable === true &&
tryNumber <= maxRetries
) {
await delay(delayInMs);
return _retryWithExponentialBackoff(
f,
{ maxRetries, delayInMs: backoffFactor * delayInMs, backoffFactor },
newErrors,
);
}
if (tryNumber === 1) {
throw error; // don't wrap the error when a non-retryable error occurs on the first try
}
throw new RetryError({
message: `Failed after ${tryNumber} attemps with non-retryable error: '${errorMessage}'`,
reason: 'errorNotRetryable',
errors: newErrors,
});
}
}