{"version":3,"file":"toolRetry.cjs","names":["z","RetrySchema","InvalidRetryConfigError","ToolMessage","createMiddleware","calculateRetryDelay","sleep"],"sources":["../../../src/agents/middleware/toolRetry.ts"],"sourcesContent":["/**\n * Tool retry middleware for agents.\n */\nimport { z } from \"zod/v3\";\nimport { ToolMessage } from \"@langchain/core/messages\";\nimport type { ClientTool, ServerTool } from \"@langchain/core/tools\";\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 Tool Retry Middleware.\n */\nexport const ToolRetryMiddlewareOptionsSchema = z\n  .object({\n    /**\n     * Optional list of tools or tool names to apply retry logic to.\n     * Can be a list of `BaseTool` instances or tool name strings.\n     * If `undefined`, applies to all tools. Default is `undefined`.\n     */\n    tools: z\n      .array(\n        z.union([z.custom<ClientTool>(), z.custom<ServerTool>(), z.string()])\n      )\n      .optional(),\n\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     * Deprecated values:\n     * - `\"raise\"`: use `\"error\"` instead.\n     * - `\"return_message\"`: use `\"continue\"` instead.\n     */\n    onFailure: z\n      .union([\n        z.literal(\"error\"),\n        z.literal(\"continue\"),\n        /**\n         * @deprecated Use `\"error\"` instead.\n         */\n        z.literal(\"raise\"),\n        /**\n         * @deprecated Use `\"continue\"` instead.\n         */\n        z.literal(\"return_message\"),\n        z.function().args(z.instanceof(Error)).returns(z.string()),\n      ])\n      .default(\"continue\"),\n  })\n  .merge(RetrySchema);\n\nexport type ToolRetryMiddlewareConfig = z.input<\n  typeof ToolRetryMiddlewareOptionsSchema\n>;\n\n/**\n * Middleware that automatically retries failed tool 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, toolRetryMiddleware } from \"langchain\";\n *\n * const agent = createAgent({\n *   model: \"openai:gpt-4o\",\n *   tools: [searchTool],\n *   middleware: [toolRetryMiddleware()],\n * });\n * ```\n *\n * @example Retry specific exceptions only\n * ```ts\n * import { toolRetryMiddleware } from \"langchain\";\n *\n * const retry = toolRetryMiddleware({\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 5xx errors\n *   if (error.name === \"HTTPError\" && \"statusCode\" in error) {\n *     const statusCode = (error as any).statusCode;\n *     return 500 <= statusCode && statusCode < 600;\n *   }\n *   return false;\n * }\n *\n * const retry = toolRetryMiddleware({\n *   maxRetries: 3,\n *   retryOn: shouldRetry,\n * });\n * ```\n *\n * @example Apply to specific tools with custom error handling\n * ```ts\n * const formatError = (error: Error) =>\n *   \"Database temporarily unavailable. Please try again later.\";\n *\n * const retry = toolRetryMiddleware({\n *   maxRetries: 4,\n *   tools: [\"search_database\"],\n *   onFailure: formatError,\n * });\n * ```\n *\n * @example Apply to specific tools using BaseTool instances\n * ```ts\n * import { tool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const searchDatabase = tool(\n *   async ({ query }) => {\n *     // Search implementation\n *     return results;\n *   },\n *   {\n *     name: \"search_database\",\n *     description: \"Search the database\",\n *     schema: z.object({ query: z.string() }),\n *   }\n * );\n *\n * const retry = toolRetryMiddleware({\n *   maxRetries: 4,\n *   tools: [searchDatabase], // Pass BaseTool instance\n * });\n * ```\n *\n * @example Constant backoff (no exponential growth)\n * ```ts\n * const retry = toolRetryMiddleware({\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 = toolRetryMiddleware({\n *   maxRetries: 2,\n *   onFailure: \"raise\", // 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 tool failures with retries\n */\nexport function toolRetryMiddleware(config: ToolRetryMiddlewareConfig = {}) {\n  const { success, error, data } =\n    ToolRetryMiddlewareOptionsSchema.safeParse(config);\n  if (!success) {\n    throw new InvalidRetryConfigError(error);\n  }\n  const {\n    maxRetries,\n    tools,\n    retryOn,\n    onFailure: onFailureConfig,\n    backoffFactor,\n    initialDelayMs,\n    maxDelayMs,\n    jitter,\n  } = data;\n\n  let onFailure = onFailureConfig;\n  if (onFailureConfig === \"raise\") {\n    console.warn(\n      \"⚠️ `onFailure: 'raise'` is deprecated. Use `onFailure: 'error'` instead.\"\n    );\n    onFailure = \"error\";\n  } else if (onFailureConfig === \"return_message\") {\n    console.warn(\n      \"⚠️ `onFailure: 'return_message'` is deprecated. Use `onFailure: 'continue'` instead.\"\n    );\n    onFailure = \"continue\";\n  }\n\n  // Extract tool names from BaseTool instances or strings\n  const toolFilter: string[] = [];\n  for (const tool of tools ?? []) {\n    if (typeof tool === \"string\") {\n      toolFilter.push(tool);\n    } else if (\"name\" in tool && typeof tool.name === \"string\") {\n      toolFilter.push(tool.name);\n    } else {\n      throw new TypeError(\n        \"Expected a tool name string or tool instance to be passed to toolRetryMiddleware\"\n      );\n    }\n  }\n\n  /**\n   * Check if retry logic should apply to this tool.\n   */\n  const shouldRetryTool = (toolName: string): boolean => {\n    if (toolFilter.length === 0) {\n      return true;\n    }\n    return toolFilter.includes(toolName);\n  };\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((ErrorConstructor) => {\n      // oxlint-disable-next-line no-instanceof/no-instanceof\n      return error instanceof 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 = (\n    toolName: string,\n    error: Error,\n    attemptsMade: number\n  ): string => {\n    const errorType = error.constructor.name;\n    const attemptWord = attemptsMade === 1 ? \"attempt\" : \"attempts\";\n    return `Tool '${toolName}' failed after ${attemptsMade} ${attemptWord} with ${errorType}`;\n  };\n\n  /**\n   * Handle failure when all retries are exhausted.\n   */\n  const handleFailure = (\n    toolName: string,\n    toolCallId: string,\n    error: Error,\n    attemptsMade: number\n  ): ToolMessage => {\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(toolName, error, attemptsMade);\n    }\n\n    return new ToolMessage({\n      content,\n      tool_call_id: toolCallId,\n      name: toolName,\n      status: \"error\",\n    });\n  };\n\n  return createMiddleware({\n    name: \"toolRetryMiddleware\",\n    contextSchema: ToolRetryMiddlewareOptionsSchema,\n    wrapToolCall: async (request, handler) => {\n      const toolName = (request.tool?.name ?? request.toolCall.name) as string;\n\n      // Check if retry should apply to this tool\n      if (!shouldRetryTool(toolName)) {\n        return handler(request);\n      }\n\n      const toolCallId = request.toolCall.id ?? \"\";\n\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(toolName, toolCallId, 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(toolName, toolCallId, 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":"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"}