agsamantha/node_modules/langsmith/dist/utils/async_caller.cjs

140 lines
5.3 KiB
JavaScript
Raw Permalink Normal View History

2024-10-02 15:15:21 -05:00
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsyncCaller = void 0;
const p_retry_1 = __importDefault(require("p-retry"));
const p_queue_1 = __importDefault(require("p-queue"));
const fetch_js_1 = require("../singletons/fetch.cjs");
const STATUS_NO_RETRY = [
400, // Bad Request
401, // Unauthorized
403, // Forbidden
404, // Not Found
405, // Method Not Allowed
406, // Not Acceptable
407, // Proxy Authentication Required
408, // Request Timeout
];
const STATUS_IGNORE = [
409, // Conflict
];
/**
* A class that can be used to make async calls with concurrency and retry logic.
*
* This is useful for making calls to any kind of "expensive" external resource,
* be it because it's rate-limited, subject to network issues, etc.
*
* Concurrent calls are limited by the `maxConcurrency` parameter, which defaults
* to `Infinity`. This means that by default, all calls will be made in parallel.
*
* Retries are limited by the `maxRetries` parameter, which defaults to 6. This
* means that by default, each call will be retried up to 6 times, with an
* exponential backoff between each attempt.
*/
class AsyncCaller {
constructor(params) {
Object.defineProperty(this, "maxConcurrency", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "maxRetries", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "queue", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "onFailedResponseHook", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.maxConcurrency = params.maxConcurrency ?? Infinity;
this.maxRetries = params.maxRetries ?? 6;
if ("default" in p_queue_1.default) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.queue = new p_queue_1.default.default({
concurrency: this.maxConcurrency,
});
}
else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.queue = new p_queue_1.default({ concurrency: this.maxConcurrency });
}
this.onFailedResponseHook = params?.onFailedResponseHook;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
call(callable, ...args) {
const onFailedResponseHook = this.onFailedResponseHook;
return this.queue.add(() => (0, p_retry_1.default)(() => callable(...args).catch((error) => {
// eslint-disable-next-line no-instanceof/no-instanceof
if (error instanceof Error) {
throw error;
}
else {
throw new Error(error);
}
}), {
async onFailedAttempt(error) {
if (error.message.startsWith("Cancel") ||
error.message.startsWith("TimeoutError") ||
error.message.startsWith("AbortError")) {
throw error;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (error?.code === "ECONNABORTED") {
throw error;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const response = error?.response;
const status = response?.status;
if (status) {
if (STATUS_NO_RETRY.includes(+status)) {
throw error;
}
else if (STATUS_IGNORE.includes(+status)) {
return;
}
if (onFailedResponseHook) {
await onFailedResponseHook(response);
}
}
},
// If needed we can change some of the defaults here,
// but they're quite sensible.
retries: this.maxRetries,
randomize: true,
}), { throwOnTimeout: true });
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
callWithOptions(options, callable, ...args) {
// Note this doesn't cancel the underlying request,
// when available prefer to use the signal option of the underlying call
if (options.signal) {
return Promise.race([
this.call(callable, ...args),
new Promise((_, reject) => {
options.signal?.addEventListener("abort", () => {
reject(new Error("AbortError"));
});
}),
]);
}
return this.call(callable, ...args);
}
fetch(...args) {
return this.call(() => (0, fetch_js_1._getFetchImplementation)()(...args).then((res) => res.ok ? res : Promise.reject(res)));
}
}
exports.AsyncCaller = AsyncCaller;