{"version":3,"file":"modelRetry.cjs","names":["z","RetrySchema","InvalidRetryConfigError","AIMessage","createMiddleware","calculateRetryDelay","sleep"],"sources":["../../../src/agents/middleware/modelRetry.ts"],"sourcesContent":["/**\n * Model retry middleware for agents.\n */\nimport { z } from \"zod/v3\";\nimport { AIMessage } from \"@langchain/core/messages\";\n\nimport { createMiddleware } from \"../middleware.js\";\nimport { sleep, calculateRetryDelay } from \"./utils.js\";\nimport { RetrySchema } from \"./constants.js\";\nimport { InvalidRetryConfigError } from \"./error.js\";\n\n/**\n * Configuration options for the Model Retry Middleware.\n */\nexport const ModelRetryMiddlewareOptionsSchema = z\n  .object({\n    /**\n     * Behavior when all retries are exhausted. Options:\n     * - `\"continue\"` (default): Return an AIMessage with error details, allowing\n     *   the agent to potentially handle the failure gracefully.\n     * - `\"error\"`: Re-raise the exception, stopping agent execution.\n     * - Custom function: Function that takes the exception and returns a string\n     *   for the AIMessage content, allowing custom error formatting.\n     */\n    onFailure: z\n      .union([\n        z.literal(\"error\"),\n        z.literal(\"continue\"),\n        z.function().args(z.instanceof(Error)).returns(z.string()),\n      ])\n      .default(\"continue\"),\n  })\n  .merge(RetrySchema);\n\nexport type ModelRetryMiddlewareConfig = z.input<\n  typeof ModelRetryMiddlewareOptionsSchema\n>;\n\n/**\n * Middleware that automatically retries failed model calls with configurable backoff.\n *\n * Supports retrying on specific exceptions and exponential backoff.\n *\n * @example Basic usage with default settings (2 retries, exponential backoff)\n * ```ts\n * import { createAgent, modelRetryMiddleware } from \"langchain\";\n *\n * const agent = createAgent({\n *   model: \"openai:gpt-4o\",\n *   tools: [searchTool],\n *   middleware: [modelRetryMiddleware()],\n * });\n * ```\n *\n * @example Retry specific exceptions only\n * ```ts\n * import { modelRetryMiddleware } from \"langchain\";\n *\n * const retry = modelRetryMiddleware({\n *   maxRetries: 4,\n *   retryOn: [TimeoutError, NetworkError],\n *   backoffFactor: 1.5,\n * });\n * ```\n *\n * @example Custom exception filtering\n * ```ts\n * function shouldRetry(error: Error): boolean {\n *   // Only retry on rate limit errors\n *   if (error.name === \"RateLimitError\") {\n *     return true;\n *   }\n *   // Or check for specific HTTP status codes\n *   if (error.name === \"HTTPError\" && \"statusCode\" in error) {\n *     const statusCode = (error as any).statusCode;\n *     return statusCode === 429 || statusCode === 503;\n *   }\n *   return false;\n * }\n *\n * const retry = modelRetryMiddleware({\n *   maxRetries: 3,\n *   retryOn: shouldRetry,\n * });\n * ```\n *\n * @example Return error message instead of raising\n * ```ts\n * const retry = modelRetryMiddleware({\n *   maxRetries: 4,\n *   onFailure: \"continue\", // Return AIMessage with error instead of throwing\n * });\n * ```\n *\n * @example Custom error message formatting\n * ```ts\n * const formatError = (error: Error) =>\n *   `Model call failed: ${error.message}. Please try again later.`;\n *\n * const retry = modelRetryMiddleware({\n *   maxRetries: 4,\n *   onFailure: formatError,\n * });\n * ```\n *\n * @example Constant backoff (no exponential growth)\n * ```ts\n * const retry = modelRetryMiddleware({\n *   maxRetries: 5,\n *   backoffFactor: 0.0, // No exponential growth\n *   initialDelayMs: 2000, // Always wait 2 seconds\n * });\n * ```\n *\n * @example Raise exception on failure\n * ```ts\n * const retry = modelRetryMiddleware({\n *   maxRetries: 2,\n *   onFailure: \"error\", // Re-raise exception instead of returning message\n * });\n * ```\n *\n * @param config - Configuration options for the retry middleware\n * @returns A middleware instance that handles model failures with retries\n */\nexport function modelRetryMiddleware(config: ModelRetryMiddlewareConfig = {}) {\n  const { success, error, data } =\n    ModelRetryMiddlewareOptionsSchema.safeParse(config);\n  if (!success) {\n    throw new InvalidRetryConfigError(error);\n  }\n  const {\n    maxRetries,\n    retryOn,\n    onFailure,\n    backoffFactor,\n    initialDelayMs,\n    maxDelayMs,\n    jitter,\n  } = data;\n\n  /**\n   * Check if the exception should trigger a retry.\n   */\n  const shouldRetryException = (error: Error): boolean => {\n    if (typeof retryOn === \"function\") {\n      return retryOn(error);\n    }\n    // retryOn is an array of error constructors\n    return retryOn.some(\n      (ErrorConstructor) => error.constructor === ErrorConstructor\n    );\n  };\n\n  // Use the exported calculateRetryDelay function with our config\n  const delayConfig = { backoffFactor, initialDelayMs, maxDelayMs, jitter };\n\n  /**\n   * Format the failure message when retries are exhausted.\n   */\n  const formatFailureMessage = (error: Error, attemptsMade: number): string => {\n    const errorType = error.constructor.name;\n    const attemptWord = attemptsMade === 1 ? \"attempt\" : \"attempts\";\n    return `Model call failed after ${attemptsMade} ${attemptWord} with ${errorType}: ${error.message}`;\n  };\n\n  /**\n   * Handle failure when all retries are exhausted.\n   */\n  const handleFailure = (error: Error, attemptsMade: number): AIMessage => {\n    if (onFailure === \"error\") {\n      throw error;\n    }\n\n    let content: string;\n    if (typeof onFailure === \"function\") {\n      content = onFailure(error);\n    } else {\n      content = formatFailureMessage(error, attemptsMade);\n    }\n\n    return new AIMessage({\n      content,\n    });\n  };\n\n  return createMiddleware({\n    name: \"modelRetryMiddleware\",\n    contextSchema: ModelRetryMiddlewareOptionsSchema,\n    wrapModelCall: async (request, handler) => {\n      // Initial attempt + retries\n      for (let attempt = 0; attempt <= maxRetries; attempt++) {\n        try {\n          return await handler(request);\n        } catch (error) {\n          const attemptsMade = attempt + 1; // attempt is 0-indexed\n\n          // Ensure error is an Error instance\n          const err =\n            error && typeof error === \"object\" && \"message\" in error\n              ? (error as Error)\n              : new Error(String(error));\n\n          // Check if we should retry this exception\n          if (!shouldRetryException(err)) {\n            // Exception is not retryable, handle failure immediately\n            return handleFailure(err, attemptsMade);\n          }\n\n          // Check if we have more retries left\n          if (attempt < maxRetries) {\n            // Calculate and apply backoff delay\n            const delay = calculateRetryDelay(delayConfig, attempt);\n            if (delay > 0) {\n              await sleep(delay);\n            }\n            // Continue to next retry\n          } else {\n            // No more retries, handle failure\n            return handleFailure(err, attemptsMade);\n          }\n        }\n      }\n\n      // Unreachable: loop always returns via handler success or handleFailure\n      throw new Error(\"Unexpected: retry loop completed without returning\");\n    },\n  });\n}\n"],"mappings":";;;;;;;;;;;;;AAcA,MAAa,oCAAoCA,OAAAA,EAC9C,OAAO;;;;;;;;;AASN,WAAWA,OAAAA,EACR,MAAM;CACLA,OAAAA,EAAE,QAAQ,OAAO;CACjBA,OAAAA,EAAE,QAAQ,UAAU;CACpBA,OAAAA,EAAE,SAAS,CAAC,CAAC,KAAKA,OAAAA,EAAE,WAAW,KAAK,CAAC,CAAC,CAAC,QAAQA,OAAAA,EAAE,OAAO,CAAC;AAC3D,CAAC,CAAC,CACD,QAAQ,UAAU,EACvB,CAAC,CAAC,CACD,MAAMC,kBAAAA,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6FpB,SAAgB,qBAAqB,SAAqC,CAAC,GAAG;CAC5E,MAAM,EAAE,SAAS,OAAO,SACtB,kCAAkC,UAAU,MAAM;CACpD,IAAI,CAAC,SACH,MAAM,IAAIC,cAAAA,wBAAwB,KAAK;CAEzC,MAAM,EACJ,YACA,SACA,WACA,eACA,gBACA,YACA,WACE;;;;CAKJ,MAAM,wBAAwB,UAA0B;EACtD,IAAI,OAAO,YAAY,YACrB,OAAO,QAAQ,KAAK;EAGtB,OAAO,QAAQ,MACZ,qBAAqB,MAAM,gBAAgB,gBAC9C;CACF;CAGA,MAAM,cAAc;EAAE;EAAe;EAAgB;EAAY;CAAO;;;;CAKxE,MAAM,wBAAwB,OAAc,iBAAiC;EAC3E,MAAM,YAAY,MAAM,YAAY;EAEpC,OAAO,2BAA2B,aAAa,GAD3B,iBAAiB,IAAI,YAAY,WACS,QAAQ,UAAU,IAAI,MAAM;CAC5F;;;;CAKA,MAAM,iBAAiB,OAAc,iBAAoC;EACvE,IAAI,cAAc,SAChB,MAAM;EAGR,IAAI;EACJ,IAAI,OAAO,cAAc,YACvB,UAAU,UAAU,KAAK;OAEzB,UAAU,qBAAqB,OAAO,YAAY;EAGpD,OAAO,IAAIC,yBAAAA,UAAU,EACnB,QACF,CAAC;CACH;CAEA,OAAOC,mBAAAA,iBAAiB;EACtB,MAAM;EACN,eAAe;EACf,eAAe,OAAO,SAAS,YAAY;GAEzC,KAAK,IAAI,UAAU,GAAG,WAAW,YAAY,WAC3C,IAAI;IACF,OAAO,MAAM,QAAQ,OAAO;GAC9B,SAAS,OAAO;IACd,MAAM,eAAe,UAAU;IAG/B,MAAM,MACJ,SAAS,OAAO,UAAU,YAAY,aAAa,QAC9C,QACD,IAAI,MAAM,OAAO,KAAK,CAAC;IAG7B,IAAI,CAAC,qBAAqB,GAAG,GAE3B,OAAO,cAAc,KAAK,YAAY;IAIxC,IAAI,UAAU,YAAY;KAExB,MAAM,QAAQC,cAAAA,oBAAoB,aAAa,OAAO;KACtD,IAAI,QAAQ,GACV,MAAMC,cAAAA,MAAM,KAAK;IAGrB,OAEE,OAAO,cAAc,KAAK,YAAY;GAE1C;GAIF,MAAM,IAAI,MAAM,oDAAoD;EACtE;CACF,CAAC;AACH"}