langchain
Version:
Typescript bindings for langchain
162 lines (160 loc) • 5.45 kB
JavaScript
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
const require_utils = require('./utils.cjs');
const require_middleware = require('../middleware.cjs');
const require_constants = require('./constants.cjs');
const require_error = require('./error.cjs');
const __langchain_core_messages = require_rolldown_runtime.__toESM(require("@langchain/core/messages"));
const zod_v3 = require_rolldown_runtime.__toESM(require("zod/v3"));
//#region src/agents/middleware/modelRetry.ts
/**
* Configuration options for the Model Retry Middleware.
*/
const ModelRetryMiddlewareOptionsSchema = zod_v3.z.object({ onFailure: zod_v3.z.union([
zod_v3.z.literal("error"),
zod_v3.z.literal("continue"),
zod_v3.z.function().args(zod_v3.z.instanceof(Error)).returns(zod_v3.z.string())
]).default("continue") }).merge(require_constants.RetrySchema);
/**
* Middleware that automatically retries failed model calls with configurable backoff.
*
* Supports retrying on specific exceptions and exponential backoff.
*
* @example Basic usage with default settings (2 retries, exponential backoff)
* ```ts
* import { createAgent, modelRetryMiddleware } from "langchain";
*
* const agent = createAgent({
* model: "openai:gpt-4o",
* tools: [searchTool],
* middleware: [modelRetryMiddleware()],
* });
* ```
*
* @example Retry specific exceptions only
* ```ts
* import { modelRetryMiddleware } from "langchain";
*
* const retry = modelRetryMiddleware({
* maxRetries: 4,
* retryOn: [TimeoutError, NetworkError],
* backoffFactor: 1.5,
* });
* ```
*
* @example Custom exception filtering
* ```ts
* function shouldRetry(error: Error): boolean {
* // Only retry on rate limit errors
* if (error.name === "RateLimitError") {
* return true;
* }
* // Or check for specific HTTP status codes
* if (error.name === "HTTPError" && "statusCode" in error) {
* const statusCode = (error as any).statusCode;
* return statusCode === 429 || statusCode === 503;
* }
* return false;
* }
*
* const retry = modelRetryMiddleware({
* maxRetries: 3,
* retryOn: shouldRetry,
* });
* ```
*
* @example Return error message instead of raising
* ```ts
* const retry = modelRetryMiddleware({
* maxRetries: 4,
* onFailure: "continue", // Return AIMessage with error instead of throwing
* });
* ```
*
* @example Custom error message formatting
* ```ts
* const formatError = (error: Error) =>
* `Model call failed: ${error.message}. Please try again later.`;
*
* const retry = modelRetryMiddleware({
* maxRetries: 4,
* onFailure: formatError,
* });
* ```
*
* @example Constant backoff (no exponential growth)
* ```ts
* const retry = modelRetryMiddleware({
* maxRetries: 5,
* backoffFactor: 0.0, // No exponential growth
* initialDelayMs: 2000, // Always wait 2 seconds
* });
* ```
*
* @example Raise exception on failure
* ```ts
* const retry = modelRetryMiddleware({
* maxRetries: 2,
* onFailure: "error", // Re-raise exception instead of returning message
* });
* ```
*
* @param config - Configuration options for the retry middleware
* @returns A middleware instance that handles model failures with retries
*/
function modelRetryMiddleware(config = {}) {
const { success, error, data } = ModelRetryMiddlewareOptionsSchema.safeParse(config);
if (!success) throw new require_error.InvalidRetryConfigError(error);
const { maxRetries, retryOn, onFailure, backoffFactor, initialDelayMs, maxDelayMs, jitter } = data;
/**
* Check if the exception should trigger a retry.
*/
const shouldRetryException = (error$1) => {
if (typeof retryOn === "function") return retryOn(error$1);
return retryOn.some((ErrorConstructor) => error$1.constructor === ErrorConstructor);
};
const delayConfig = {
backoffFactor,
initialDelayMs,
maxDelayMs,
jitter
};
/**
* Format the failure message when retries are exhausted.
*/
const formatFailureMessage = (error$1, attemptsMade) => {
const errorType = error$1.constructor.name;
const attemptWord = attemptsMade === 1 ? "attempt" : "attempts";
return `Model call failed after ${attemptsMade} ${attemptWord} with ${errorType}: ${error$1.message}`;
};
/**
* Handle failure when all retries are exhausted.
*/
const handleFailure = (error$1, attemptsMade) => {
if (onFailure === "error") throw error$1;
let content;
if (typeof onFailure === "function") content = onFailure(error$1);
else content = formatFailureMessage(error$1, attemptsMade);
return new __langchain_core_messages.AIMessage({ content });
};
return require_middleware.createMiddleware({
name: "modelRetryMiddleware",
contextSchema: ModelRetryMiddlewareOptionsSchema,
wrapModelCall: async (request, handler) => {
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
return await handler(request);
} catch (error$1) {
const attemptsMade = attempt + 1;
const err = error$1 && typeof error$1 === "object" && "message" in error$1 ? error$1 : new Error(String(error$1));
if (!shouldRetryException(err)) return handleFailure(err, attemptsMade);
if (attempt < maxRetries) {
const delay = require_utils.calculateRetryDelay(delayConfig, attempt);
if (delay > 0) await require_utils.sleep(delay);
} else return handleFailure(err, attemptsMade);
}
throw new Error("Unexpected: retry loop completed without returning");
}
});
}
//#endregion
exports.modelRetryMiddleware = modelRetryMiddleware;
//# sourceMappingURL=modelRetry.cjs.map