{"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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"}