langchain 1.2.25 → 1.2.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/agents/ReactAgent.cjs.map +1 -1
  3. package/dist/agents/ReactAgent.d.cts +2 -2
  4. package/dist/agents/ReactAgent.d.ts +2 -2
  5. package/dist/agents/ReactAgent.js.map +1 -1
  6. package/dist/agents/index.d.cts +1 -1
  7. package/dist/agents/index.d.ts +1 -1
  8. package/dist/agents/middleware/dynamicSystemPrompt.cjs +1 -1
  9. package/dist/agents/middleware/dynamicSystemPrompt.cjs.map +1 -1
  10. package/dist/agents/middleware/dynamicSystemPrompt.d.cts +1 -1
  11. package/dist/agents/middleware/dynamicSystemPrompt.d.ts +1 -1
  12. package/dist/agents/middleware/dynamicSystemPrompt.js +1 -1
  13. package/dist/agents/middleware/dynamicSystemPrompt.js.map +1 -1
  14. package/dist/agents/middleware/llmToolSelector.cjs.map +1 -1
  15. package/dist/agents/middleware/llmToolSelector.d.cts +37 -2
  16. package/dist/agents/middleware/llmToolSelector.d.cts.map +1 -1
  17. package/dist/agents/middleware/llmToolSelector.d.ts +37 -2
  18. package/dist/agents/middleware/llmToolSelector.d.ts.map +1 -1
  19. package/dist/agents/middleware/llmToolSelector.js.map +1 -1
  20. package/dist/agents/middleware/modelFallback.cjs.map +1 -1
  21. package/dist/agents/middleware/modelFallback.d.cts +2 -1
  22. package/dist/agents/middleware/modelFallback.d.cts.map +1 -1
  23. package/dist/agents/middleware/modelFallback.d.ts +2 -1
  24. package/dist/agents/middleware/modelFallback.d.ts.map +1 -1
  25. package/dist/agents/middleware/modelFallback.js.map +1 -1
  26. package/dist/agents/middleware/modelRetry.cjs.map +1 -1
  27. package/dist/agents/middleware/modelRetry.d.cts +43 -1
  28. package/dist/agents/middleware/modelRetry.d.cts.map +1 -1
  29. package/dist/agents/middleware/modelRetry.d.ts +43 -1
  30. package/dist/agents/middleware/modelRetry.d.ts.map +1 -1
  31. package/dist/agents/middleware/modelRetry.js.map +1 -1
  32. package/dist/agents/middleware/pii.cjs +7 -4
  33. package/dist/agents/middleware/pii.cjs.map +1 -1
  34. package/dist/agents/middleware/pii.d.cts +28 -2
  35. package/dist/agents/middleware/pii.d.cts.map +1 -1
  36. package/dist/agents/middleware/pii.d.ts +28 -2
  37. package/dist/agents/middleware/pii.d.ts.map +1 -1
  38. package/dist/agents/middleware/pii.js +7 -4
  39. package/dist/agents/middleware/pii.js.map +1 -1
  40. package/dist/agents/middleware/piiRedaction.cjs.map +1 -1
  41. package/dist/agents/middleware/piiRedaction.d.cts +15 -2
  42. package/dist/agents/middleware/piiRedaction.d.cts.map +1 -1
  43. package/dist/agents/middleware/piiRedaction.d.ts +15 -2
  44. package/dist/agents/middleware/piiRedaction.d.ts.map +1 -1
  45. package/dist/agents/middleware/piiRedaction.js.map +1 -1
  46. package/dist/agents/middleware/provider/anthropic/promptCaching.cjs +4 -4
  47. package/dist/agents/middleware/provider/anthropic/promptCaching.cjs.map +1 -1
  48. package/dist/agents/middleware/provider/anthropic/promptCaching.d.cts +4 -4
  49. package/dist/agents/middleware/provider/anthropic/promptCaching.d.ts +4 -4
  50. package/dist/agents/middleware/provider/anthropic/promptCaching.js +4 -4
  51. package/dist/agents/middleware/provider/anthropic/promptCaching.js.map +1 -1
  52. package/dist/agents/middleware/toolEmulator.cjs.map +1 -1
  53. package/dist/agents/middleware/toolEmulator.d.cts +2 -2
  54. package/dist/agents/middleware/toolEmulator.d.cts.map +1 -1
  55. package/dist/agents/middleware/toolEmulator.d.ts +2 -2
  56. package/dist/agents/middleware/toolEmulator.d.ts.map +1 -1
  57. package/dist/agents/middleware/toolEmulator.js.map +1 -1
  58. package/dist/agents/middleware/toolRetry.cjs.map +1 -1
  59. package/dist/agents/middleware/toolRetry.d.cts +55 -1
  60. package/dist/agents/middleware/toolRetry.d.cts.map +1 -1
  61. package/dist/agents/middleware/toolRetry.d.ts +55 -1
  62. package/dist/agents/middleware/toolRetry.d.ts.map +1 -1
  63. package/dist/agents/middleware/toolRetry.js.map +1 -1
  64. package/dist/agents/middleware/types.cjs.map +1 -1
  65. package/dist/agents/middleware/types.d.cts +22 -18
  66. package/dist/agents/middleware/types.d.cts.map +1 -1
  67. package/dist/agents/middleware/types.d.ts +22 -18
  68. package/dist/agents/middleware/types.d.ts.map +1 -1
  69. package/dist/agents/middleware/types.js.map +1 -1
  70. package/dist/agents/nodes/AgentNode.cjs +2 -0
  71. package/dist/agents/nodes/AgentNode.cjs.map +1 -1
  72. package/dist/agents/nodes/AgentNode.js +2 -0
  73. package/dist/agents/nodes/AgentNode.js.map +1 -1
  74. package/dist/agents/types.d.cts +7 -7
  75. package/dist/agents/types.d.cts.map +1 -1
  76. package/dist/agents/types.d.ts +7 -7
  77. package/dist/agents/types.d.ts.map +1 -1
  78. package/dist/chat_models/universal.cjs +3 -3
  79. package/dist/chat_models/universal.cjs.map +1 -1
  80. package/dist/chat_models/universal.js +3 -3
  81. package/dist/chat_models/universal.js.map +1 -1
  82. package/dist/index.d.cts +2 -2
  83. package/dist/index.d.ts +2 -2
  84. package/package.json +7 -7
@@ -77,7 +77,7 @@ type PromptCachingMiddlewareConfig = Partial<InferInteropZodInput<typeof context
77
77
  * import { anthropicPromptCachingMiddleware } from "langchain";
78
78
  *
79
79
  * const agent = createAgent({
80
- * model: "anthropic:claude-3-5-sonnet",
80
+ * model: "anthropic:claude-sonnet-4-5",
81
81
  * middleware: [
82
82
  * anthropicPromptCachingMiddleware()
83
83
  * ]
@@ -93,7 +93,7 @@ type PromptCachingMiddlewareConfig = Partial<InferInteropZodInput<typeof context
93
93
  * });
94
94
  *
95
95
  * const agent = createAgent({
96
- * model: "anthropic:claude-3-5-sonnet",
96
+ * model: "anthropic:claude-sonnet-4-5",
97
97
  * systemPrompt: "You are a helpful assistant with deep knowledge of...", // Long system prompt
98
98
  * middleware: [cachingMiddleware]
99
99
  * });
@@ -103,7 +103,7 @@ type PromptCachingMiddlewareConfig = Partial<InferInteropZodInput<typeof context
103
103
  * Conditional caching based on runtime context
104
104
  * ```typescript
105
105
  * const agent = createAgent({
106
- * model: "anthropic:claude-3-5-sonnet",
106
+ * model: "anthropic:claude-sonnet-4-5",
107
107
  * middleware: [
108
108
  * anthropicPromptCachingMiddleware({
109
109
  * enableCaching: true,
@@ -127,7 +127,7 @@ type PromptCachingMiddlewareConfig = Partial<InferInteropZodInput<typeof context
127
127
  * Optimal setup for customer support chatbot
128
128
  * ```typescript
129
129
  * const supportAgent = createAgent({
130
- * model: "anthropic:claude-3-5-sonnet",
130
+ * model: "anthropic:claude-sonnet-4-5",
131
131
  * systemPrompt: `You are a customer support agent for ACME Corp.
132
132
  *
133
133
  * Company policies:
@@ -59,7 +59,7 @@ var PromptCachingMiddlewareError = class extends Error {
59
59
  * import { anthropicPromptCachingMiddleware } from "langchain";
60
60
  *
61
61
  * const agent = createAgent({
62
- * model: "anthropic:claude-3-5-sonnet",
62
+ * model: "anthropic:claude-sonnet-4-5",
63
63
  * middleware: [
64
64
  * anthropicPromptCachingMiddleware()
65
65
  * ]
@@ -75,7 +75,7 @@ var PromptCachingMiddlewareError = class extends Error {
75
75
  * });
76
76
  *
77
77
  * const agent = createAgent({
78
- * model: "anthropic:claude-3-5-sonnet",
78
+ * model: "anthropic:claude-sonnet-4-5",
79
79
  * systemPrompt: "You are a helpful assistant with deep knowledge of...", // Long system prompt
80
80
  * middleware: [cachingMiddleware]
81
81
  * });
@@ -85,7 +85,7 @@ var PromptCachingMiddlewareError = class extends Error {
85
85
  * Conditional caching based on runtime context
86
86
  * ```typescript
87
87
  * const agent = createAgent({
88
- * model: "anthropic:claude-3-5-sonnet",
88
+ * model: "anthropic:claude-sonnet-4-5",
89
89
  * middleware: [
90
90
  * anthropicPromptCachingMiddleware({
91
91
  * enableCaching: true,
@@ -109,7 +109,7 @@ var PromptCachingMiddlewareError = class extends Error {
109
109
  * Optimal setup for customer support chatbot
110
110
  * ```typescript
111
111
  * const supportAgent = createAgent({
112
- * model: "anthropic:claude-3-5-sonnet",
112
+ * model: "anthropic:claude-sonnet-4-5",
113
113
  * systemPrompt: `You are a customer support agent for ACME Corp.
114
114
  *
115
115
  * Company policies:
@@ -1 +1 @@
1
- {"version":3,"file":"promptCaching.js","names":[],"sources":["../../../../../src/agents/middleware/provider/anthropic/promptCaching.ts"],"sourcesContent":["import { z } from \"zod/v3\";\nimport { InferInteropZodInput } from \"@langchain/core/utils/types\";\n\nimport { ConfigurableModel } from \"../../../../chat_models/universal.js\";\nimport { createMiddleware } from \"../../../middleware.js\";\n\nconst DEFAULT_ENABLE_CACHING = true;\nconst DEFAULT_TTL = \"5m\";\nconst DEFAULT_MIN_MESSAGES_TO_CACHE = 3;\nconst DEFAULT_UNSUPPORTED_MODEL_BEHAVIOR = \"warn\";\n\nconst contextSchema = z.object({\n /**\n * Whether to enable prompt caching.\n * @default true\n */\n enableCaching: z.boolean().optional(),\n /**\n * The time-to-live for the cached prompt.\n * @default \"5m\"\n */\n ttl: z.enum([\"5m\", \"1h\"]).optional(),\n /**\n * The minimum number of messages required before caching is applied.\n * @default 3\n */\n minMessagesToCache: z.number().optional(),\n /**\n * The behavior to take when an unsupported model is used.\n * - \"ignore\" will ignore the unsupported model and continue without caching.\n * - \"warn\" will warn the user and continue without caching.\n * - \"raise\" will raise an error and stop the agent.\n * @default \"warn\"\n */\n unsupportedModelBehavior: z.enum([\"ignore\", \"warn\", \"raise\"]).optional(),\n});\nexport type PromptCachingMiddlewareConfig = Partial<\n InferInteropZodInput<typeof contextSchema>\n>;\n\nclass PromptCachingMiddlewareError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PromptCachingMiddlewareError\";\n }\n}\n\n/**\n * Creates a prompt caching middleware for Anthropic models to optimize API usage.\n *\n * This middleware automatically adds cache control headers to the last messages when using Anthropic models,\n * enabling their prompt caching feature. This can significantly reduce costs for applications with repetitive\n * prompts, long system messages, or extensive conversation histories.\n *\n * ## How It Works\n *\n * The middleware intercepts model requests and adds cache control metadata that tells Anthropic's\n * API to cache processed prompt prefixes. On subsequent requests with matching prefixes, the\n * cached representations are reused, skipping redundant token processing.\n *\n * ## Benefits\n *\n * - **Cost Reduction**: Avoid reprocessing the same tokens repeatedly (up to 90% savings on cached portions)\n * - **Lower Latency**: Cached prompts are processed faster as embeddings are pre-computed\n * - **Better Scalability**: Reduced computational load enables handling more requests\n * - **Consistent Performance**: Stable response times for repetitive queries\n *\n * @param middlewareOptions - Configuration options for the caching behavior\n * @param middlewareOptions.enableCaching - Whether to enable prompt caching (default: `true`)\n * @param middlewareOptions.ttl - Cache time-to-live: `\"5m\"` for 5 minutes or `\"1h\"` for 1 hour (default: `\"5m\"`)\n * @param middlewareOptions.minMessagesToCache - Minimum number of messages required before caching is applied (default: `3`)\n * @param middlewareOptions.unsupportedModelBehavior - The behavior to take when an unsupported model is used (default: `\"warn\"`)\n *\n * @returns A middleware instance that can be passed to `createAgent`\n *\n * @throws {Error} If used with non-Anthropic models\n *\n * @example\n * Basic usage with default settings\n * ```typescript\n * import { createAgent } from \"langchain\";\n * import { anthropicPromptCachingMiddleware } from \"langchain\";\n *\n * const agent = createAgent({\n * model: \"anthropic:claude-3-5-sonnet\",\n * middleware: [\n * anthropicPromptCachingMiddleware()\n * ]\n * });\n * ```\n *\n * @example\n * Custom configuration for longer conversations\n * ```typescript\n * const cachingMiddleware = anthropicPromptCachingMiddleware({\n * ttl: \"1h\", // Cache for 1 hour instead of default 5 minutes\n * minMessagesToCache: 5 // Only cache after 5 messages\n * });\n *\n * const agent = createAgent({\n * model: \"anthropic:claude-3-5-sonnet\",\n * systemPrompt: \"You are a helpful assistant with deep knowledge of...\", // Long system prompt\n * middleware: [cachingMiddleware]\n * });\n * ```\n *\n * @example\n * Conditional caching based on runtime context\n * ```typescript\n * const agent = createAgent({\n * model: \"anthropic:claude-3-5-sonnet\",\n * middleware: [\n * anthropicPromptCachingMiddleware({\n * enableCaching: true,\n * ttl: \"5m\"\n * })\n * ]\n * });\n *\n * // Disable caching for specific requests\n * await agent.invoke(\n * { messages: [new HumanMessage(\"Process this without caching\")] },\n * {\n * configurable: {\n * middleware_context: { enableCaching: false }\n * }\n * }\n * );\n * ```\n *\n * @example\n * Optimal setup for customer support chatbot\n * ```typescript\n * const supportAgent = createAgent({\n * model: \"anthropic:claude-3-5-sonnet\",\n * systemPrompt: `You are a customer support agent for ACME Corp.\n *\n * Company policies:\n * - Always be polite and professional\n * - Refer to knowledge base for product information\n * - Escalate billing issues to human agents\n * ... (extensive policies and guidelines)\n * `,\n * tools: [searchKnowledgeBase, createTicket, checkOrderStatus],\n * middleware: [\n * anthropicPromptCachingMiddleware({\n * ttl: \"1h\", // Long TTL for stable system prompt\n * minMessagesToCache: 1 // Cache immediately due to large system prompt\n * })\n * ]\n * });\n * ```\n *\n * @remarks\n * - **Anthropic Only**: This middleware only works with Anthropic models and will throw an error if used with other providers\n * - **Automatic Application**: Caching is applied automatically when message count exceeds `minMessagesToCache`\n * - **Cache Scope**: Caches are isolated per API key and cannot be shared across different keys\n * - **TTL Options**: Only supports \"5m\" (5 minutes) and \"1h\" (1 hour) as TTL values per Anthropic's API\n * - **Best Use Cases**: Long system prompts, multi-turn conversations, repetitive queries, RAG applications\n * - **Cost Impact**: Cached tokens are billed at 10% of the base input token price, cache writes are billed at 25% of the base\n *\n * @see {@link createAgent} for agent creation\n * @see {@link https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching} Anthropic's prompt caching documentation\n * @public\n */\nexport function anthropicPromptCachingMiddleware(\n middlewareOptions?: PromptCachingMiddlewareConfig\n) {\n return createMiddleware({\n name: \"PromptCachingMiddleware\",\n contextSchema,\n wrapModelCall: (request, handler) => {\n /**\n * Prefer runtime context values over middleware options values over defaults\n */\n const enableCaching =\n request.runtime.context.enableCaching ??\n middlewareOptions?.enableCaching ??\n DEFAULT_ENABLE_CACHING;\n const ttl =\n request.runtime.context.ttl ?? middlewareOptions?.ttl ?? DEFAULT_TTL;\n const minMessagesToCache =\n request.runtime.context.minMessagesToCache ??\n middlewareOptions?.minMessagesToCache ??\n DEFAULT_MIN_MESSAGES_TO_CACHE;\n const unsupportedModelBehavior =\n request.runtime.context.unsupportedModelBehavior ??\n middlewareOptions?.unsupportedModelBehavior ??\n DEFAULT_UNSUPPORTED_MODEL_BEHAVIOR;\n\n // Skip if caching is disabled\n if (!enableCaching || !request.model) {\n return handler(request);\n }\n\n const isAnthropicModel =\n request.model.getName() === \"ChatAnthropic\" ||\n (request.model.getName() === \"ConfigurableModel\" &&\n (request.model as ConfigurableModel)._defaultConfig?.modelProvider ===\n \"anthropic\");\n if (!isAnthropicModel) {\n // Get model name for better error context\n const modelName = request.model.getName();\n const modelInfo =\n request.model.getName() === \"ConfigurableModel\"\n ? `${modelName} (${\n (request.model as ConfigurableModel)._defaultConfig\n ?.modelProvider\n })`\n : modelName;\n\n const baseMessage = `Unsupported model '${modelInfo}'. Prompt caching requires an Anthropic model`;\n\n if (unsupportedModelBehavior === \"raise\") {\n throw new PromptCachingMiddlewareError(\n `${baseMessage} (e.g., 'anthropic:claude-4-0-sonnet').`\n );\n } else if (unsupportedModelBehavior === \"warn\") {\n console.warn(\n `PromptCachingMiddleware: Skipping caching for ${modelName}. Consider switching to an Anthropic model for caching benefits.`\n );\n }\n return handler(request);\n }\n\n const messagesCount =\n request.state.messages.length + (request.systemPrompt ? 1 : 0);\n\n if (messagesCount < minMessagesToCache) {\n return handler(request);\n }\n\n /**\n * The cache_control is applied at the final message formatting layer in ChatAnthropic,\n * which avoids issues with message content block manipulation during earlier\n * processing stages (e.g., streaming response reassembly).\n *\n * @see https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching\n */\n return handler({\n ...request,\n modelSettings: {\n ...request.modelSettings,\n cache_control: {\n type: \"ephemeral\" as const,\n ttl,\n },\n },\n });\n },\n });\n}\n"],"mappings":";;;;AAMA,MAAM,yBAAyB;AAC/B,MAAM,cAAc;AACpB,MAAM,gCAAgC;AACtC,MAAM,qCAAqC;AAE3C,MAAM,gBAAgB,EAAE,OAAO;CAK7B,eAAe,EAAE,SAAS,CAAC,UAAU;CAKrC,KAAK,EAAE,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,UAAU;CAKpC,oBAAoB,EAAE,QAAQ,CAAC,UAAU;CAQzC,0BAA0B,EAAE,KAAK;EAAC;EAAU;EAAQ;EAAQ,CAAC,CAAC,UAAU;CACzE,CAAC;AAKF,IAAM,+BAAN,cAA2C,MAAM;CAC/C,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0HhB,SAAgB,iCACd,mBACA;AACA,QAAO,iBAAiB;EACtB,MAAM;EACN;EACA,gBAAgB,SAAS,YAAY;;;;GAInC,MAAM,gBACJ,QAAQ,QAAQ,QAAQ,iBACxB,mBAAmB,iBACnB;GACF,MAAM,MACJ,QAAQ,QAAQ,QAAQ,OAAO,mBAAmB,OAAO;GAC3D,MAAM,qBACJ,QAAQ,QAAQ,QAAQ,sBACxB,mBAAmB,sBACnB;GACF,MAAM,2BACJ,QAAQ,QAAQ,QAAQ,4BACxB,mBAAmB,4BACnB;AAGF,OAAI,CAAC,iBAAiB,CAAC,QAAQ,MAC7B,QAAO,QAAQ,QAAQ;AAQzB,OAAI,EAJF,QAAQ,MAAM,SAAS,KAAK,mBAC3B,QAAQ,MAAM,SAAS,KAAK,uBAC1B,QAAQ,MAA4B,gBAAgB,kBACnD,cACiB;IAErB,MAAM,YAAY,QAAQ,MAAM,SAAS;IASzC,MAAM,cAAc,sBAPlB,QAAQ,MAAM,SAAS,KAAK,sBACxB,GAAG,UAAU,IACV,QAAQ,MAA4B,gBACjC,cACL,KACD,UAE8C;AAEpD,QAAI,6BAA6B,QAC/B,OAAM,IAAI,6BACR,GAAG,YAAY,yCAChB;aACQ,6BAA6B,OACtC,SAAQ,KACN,iDAAiD,UAAU,kEAC5D;AAEH,WAAO,QAAQ,QAAQ;;AAMzB,OAFE,QAAQ,MAAM,SAAS,UAAU,QAAQ,eAAe,IAAI,KAE1C,mBAClB,QAAO,QAAQ,QAAQ;;;;;;;;AAUzB,UAAO,QAAQ;IACb,GAAG;IACH,eAAe;KACb,GAAG,QAAQ;KACX,eAAe;MACb,MAAM;MACN;MACD;KACF;IACF,CAAC;;EAEL,CAAC"}
1
+ {"version":3,"file":"promptCaching.js","names":[],"sources":["../../../../../src/agents/middleware/provider/anthropic/promptCaching.ts"],"sourcesContent":["import { z } from \"zod/v3\";\nimport { InferInteropZodInput } from \"@langchain/core/utils/types\";\n\nimport { ConfigurableModel } from \"../../../../chat_models/universal.js\";\nimport { createMiddleware } from \"../../../middleware.js\";\n\nconst DEFAULT_ENABLE_CACHING = true;\nconst DEFAULT_TTL = \"5m\";\nconst DEFAULT_MIN_MESSAGES_TO_CACHE = 3;\nconst DEFAULT_UNSUPPORTED_MODEL_BEHAVIOR = \"warn\";\n\nconst contextSchema = z.object({\n /**\n * Whether to enable prompt caching.\n * @default true\n */\n enableCaching: z.boolean().optional(),\n /**\n * The time-to-live for the cached prompt.\n * @default \"5m\"\n */\n ttl: z.enum([\"5m\", \"1h\"]).optional(),\n /**\n * The minimum number of messages required before caching is applied.\n * @default 3\n */\n minMessagesToCache: z.number().optional(),\n /**\n * The behavior to take when an unsupported model is used.\n * - \"ignore\" will ignore the unsupported model and continue without caching.\n * - \"warn\" will warn the user and continue without caching.\n * - \"raise\" will raise an error and stop the agent.\n * @default \"warn\"\n */\n unsupportedModelBehavior: z.enum([\"ignore\", \"warn\", \"raise\"]).optional(),\n});\nexport type PromptCachingMiddlewareConfig = Partial<\n InferInteropZodInput<typeof contextSchema>\n>;\n\nclass PromptCachingMiddlewareError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PromptCachingMiddlewareError\";\n }\n}\n\n/**\n * Creates a prompt caching middleware for Anthropic models to optimize API usage.\n *\n * This middleware automatically adds cache control headers to the last messages when using Anthropic models,\n * enabling their prompt caching feature. This can significantly reduce costs for applications with repetitive\n * prompts, long system messages, or extensive conversation histories.\n *\n * ## How It Works\n *\n * The middleware intercepts model requests and adds cache control metadata that tells Anthropic's\n * API to cache processed prompt prefixes. On subsequent requests with matching prefixes, the\n * cached representations are reused, skipping redundant token processing.\n *\n * ## Benefits\n *\n * - **Cost Reduction**: Avoid reprocessing the same tokens repeatedly (up to 90% savings on cached portions)\n * - **Lower Latency**: Cached prompts are processed faster as embeddings are pre-computed\n * - **Better Scalability**: Reduced computational load enables handling more requests\n * - **Consistent Performance**: Stable response times for repetitive queries\n *\n * @param middlewareOptions - Configuration options for the caching behavior\n * @param middlewareOptions.enableCaching - Whether to enable prompt caching (default: `true`)\n * @param middlewareOptions.ttl - Cache time-to-live: `\"5m\"` for 5 minutes or `\"1h\"` for 1 hour (default: `\"5m\"`)\n * @param middlewareOptions.minMessagesToCache - Minimum number of messages required before caching is applied (default: `3`)\n * @param middlewareOptions.unsupportedModelBehavior - The behavior to take when an unsupported model is used (default: `\"warn\"`)\n *\n * @returns A middleware instance that can be passed to `createAgent`\n *\n * @throws {Error} If used with non-Anthropic models\n *\n * @example\n * Basic usage with default settings\n * ```typescript\n * import { createAgent } from \"langchain\";\n * import { anthropicPromptCachingMiddleware } from \"langchain\";\n *\n * const agent = createAgent({\n * model: \"anthropic:claude-sonnet-4-5\",\n * middleware: [\n * anthropicPromptCachingMiddleware()\n * ]\n * });\n * ```\n *\n * @example\n * Custom configuration for longer conversations\n * ```typescript\n * const cachingMiddleware = anthropicPromptCachingMiddleware({\n * ttl: \"1h\", // Cache for 1 hour instead of default 5 minutes\n * minMessagesToCache: 5 // Only cache after 5 messages\n * });\n *\n * const agent = createAgent({\n * model: \"anthropic:claude-sonnet-4-5\",\n * systemPrompt: \"You are a helpful assistant with deep knowledge of...\", // Long system prompt\n * middleware: [cachingMiddleware]\n * });\n * ```\n *\n * @example\n * Conditional caching based on runtime context\n * ```typescript\n * const agent = createAgent({\n * model: \"anthropic:claude-sonnet-4-5\",\n * middleware: [\n * anthropicPromptCachingMiddleware({\n * enableCaching: true,\n * ttl: \"5m\"\n * })\n * ]\n * });\n *\n * // Disable caching for specific requests\n * await agent.invoke(\n * { messages: [new HumanMessage(\"Process this without caching\")] },\n * {\n * configurable: {\n * middleware_context: { enableCaching: false }\n * }\n * }\n * );\n * ```\n *\n * @example\n * Optimal setup for customer support chatbot\n * ```typescript\n * const supportAgent = createAgent({\n * model: \"anthropic:claude-sonnet-4-5\",\n * systemPrompt: `You are a customer support agent for ACME Corp.\n *\n * Company policies:\n * - Always be polite and professional\n * - Refer to knowledge base for product information\n * - Escalate billing issues to human agents\n * ... (extensive policies and guidelines)\n * `,\n * tools: [searchKnowledgeBase, createTicket, checkOrderStatus],\n * middleware: [\n * anthropicPromptCachingMiddleware({\n * ttl: \"1h\", // Long TTL for stable system prompt\n * minMessagesToCache: 1 // Cache immediately due to large system prompt\n * })\n * ]\n * });\n * ```\n *\n * @remarks\n * - **Anthropic Only**: This middleware only works with Anthropic models and will throw an error if used with other providers\n * - **Automatic Application**: Caching is applied automatically when message count exceeds `minMessagesToCache`\n * - **Cache Scope**: Caches are isolated per API key and cannot be shared across different keys\n * - **TTL Options**: Only supports \"5m\" (5 minutes) and \"1h\" (1 hour) as TTL values per Anthropic's API\n * - **Best Use Cases**: Long system prompts, multi-turn conversations, repetitive queries, RAG applications\n * - **Cost Impact**: Cached tokens are billed at 10% of the base input token price, cache writes are billed at 25% of the base\n *\n * @see {@link createAgent} for agent creation\n * @see {@link https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching} Anthropic's prompt caching documentation\n * @public\n */\nexport function anthropicPromptCachingMiddleware(\n middlewareOptions?: PromptCachingMiddlewareConfig\n) {\n return createMiddleware({\n name: \"PromptCachingMiddleware\",\n contextSchema,\n wrapModelCall: (request, handler) => {\n /**\n * Prefer runtime context values over middleware options values over defaults\n */\n const enableCaching =\n request.runtime.context.enableCaching ??\n middlewareOptions?.enableCaching ??\n DEFAULT_ENABLE_CACHING;\n const ttl =\n request.runtime.context.ttl ?? middlewareOptions?.ttl ?? DEFAULT_TTL;\n const minMessagesToCache =\n request.runtime.context.minMessagesToCache ??\n middlewareOptions?.minMessagesToCache ??\n DEFAULT_MIN_MESSAGES_TO_CACHE;\n const unsupportedModelBehavior =\n request.runtime.context.unsupportedModelBehavior ??\n middlewareOptions?.unsupportedModelBehavior ??\n DEFAULT_UNSUPPORTED_MODEL_BEHAVIOR;\n\n // Skip if caching is disabled\n if (!enableCaching || !request.model) {\n return handler(request);\n }\n\n const isAnthropicModel =\n request.model.getName() === \"ChatAnthropic\" ||\n (request.model.getName() === \"ConfigurableModel\" &&\n (request.model as ConfigurableModel)._defaultConfig?.modelProvider ===\n \"anthropic\");\n if (!isAnthropicModel) {\n // Get model name for better error context\n const modelName = request.model.getName();\n const modelInfo =\n request.model.getName() === \"ConfigurableModel\"\n ? `${modelName} (${\n (request.model as ConfigurableModel)._defaultConfig\n ?.modelProvider\n })`\n : modelName;\n\n const baseMessage = `Unsupported model '${modelInfo}'. Prompt caching requires an Anthropic model`;\n\n if (unsupportedModelBehavior === \"raise\") {\n throw new PromptCachingMiddlewareError(\n `${baseMessage} (e.g., 'anthropic:claude-4-0-sonnet').`\n );\n } else if (unsupportedModelBehavior === \"warn\") {\n console.warn(\n `PromptCachingMiddleware: Skipping caching for ${modelName}. Consider switching to an Anthropic model for caching benefits.`\n );\n }\n return handler(request);\n }\n\n const messagesCount =\n request.state.messages.length + (request.systemPrompt ? 1 : 0);\n\n if (messagesCount < minMessagesToCache) {\n return handler(request);\n }\n\n /**\n * The cache_control is applied at the final message formatting layer in ChatAnthropic,\n * which avoids issues with message content block manipulation during earlier\n * processing stages (e.g., streaming response reassembly).\n *\n * @see https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching\n */\n return handler({\n ...request,\n modelSettings: {\n ...request.modelSettings,\n cache_control: {\n type: \"ephemeral\" as const,\n ttl,\n },\n },\n });\n },\n });\n}\n"],"mappings":";;;;AAMA,MAAM,yBAAyB;AAC/B,MAAM,cAAc;AACpB,MAAM,gCAAgC;AACtC,MAAM,qCAAqC;AAE3C,MAAM,gBAAgB,EAAE,OAAO;CAK7B,eAAe,EAAE,SAAS,CAAC,UAAU;CAKrC,KAAK,EAAE,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,UAAU;CAKpC,oBAAoB,EAAE,QAAQ,CAAC,UAAU;CAQzC,0BAA0B,EAAE,KAAK;EAAC;EAAU;EAAQ;EAAQ,CAAC,CAAC,UAAU;CACzE,CAAC;AAKF,IAAM,+BAAN,cAA2C,MAAM;CAC/C,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0HhB,SAAgB,iCACd,mBACA;AACA,QAAO,iBAAiB;EACtB,MAAM;EACN;EACA,gBAAgB,SAAS,YAAY;;;;GAInC,MAAM,gBACJ,QAAQ,QAAQ,QAAQ,iBACxB,mBAAmB,iBACnB;GACF,MAAM,MACJ,QAAQ,QAAQ,QAAQ,OAAO,mBAAmB,OAAO;GAC3D,MAAM,qBACJ,QAAQ,QAAQ,QAAQ,sBACxB,mBAAmB,sBACnB;GACF,MAAM,2BACJ,QAAQ,QAAQ,QAAQ,4BACxB,mBAAmB,4BACnB;AAGF,OAAI,CAAC,iBAAiB,CAAC,QAAQ,MAC7B,QAAO,QAAQ,QAAQ;AAQzB,OAAI,EAJF,QAAQ,MAAM,SAAS,KAAK,mBAC3B,QAAQ,MAAM,SAAS,KAAK,uBAC1B,QAAQ,MAA4B,gBAAgB,kBACnD,cACiB;IAErB,MAAM,YAAY,QAAQ,MAAM,SAAS;IASzC,MAAM,cAAc,sBAPlB,QAAQ,MAAM,SAAS,KAAK,sBACxB,GAAG,UAAU,IACV,QAAQ,MAA4B,gBACjC,cACL,KACD,UAE8C;AAEpD,QAAI,6BAA6B,QAC/B,OAAM,IAAI,6BACR,GAAG,YAAY,yCAChB;aACQ,6BAA6B,OACtC,SAAQ,KACN,iDAAiD,UAAU,kEAC5D;AAEH,WAAO,QAAQ,QAAQ;;AAMzB,OAFE,QAAQ,MAAM,SAAS,UAAU,QAAQ,eAAe,IAAI,KAE1C,mBAClB,QAAO,QAAQ,QAAQ;;;;;;;;AAUzB,UAAO,QAAQ;IACb,GAAG;IACH,eAAe;KACb,GAAG,QAAQ;KACX,eAAe;MACb,MAAM;MACN;MACD;KACF;IACF,CAAC;;EAEL,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"toolEmulator.cjs","names":["initChatModel","createMiddleware","HumanMessage","ToolMessage"],"sources":["../../../src/agents/middleware/toolEmulator.ts"],"sourcesContent":["import { HumanMessage, ToolMessage } from \"@langchain/core/messages\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport type { ClientTool, ServerTool } from \"@langchain/core/tools\";\nimport { initChatModel } from \"../../chat_models/universal.js\";\nimport { createMiddleware } from \"../middleware.js\";\n\n/**\n * Options for configuring the Tool Emulator middleware.\n */\nexport interface ToolEmulatorOptions {\n /**\n * List of tool names (string) or tool instances to emulate.\n * - If `undefined` (default), ALL tools will be emulated.\n * - If empty array, no tools will be emulated.\n * - If array with tool names/instances, only those tools will be emulated.\n */\n tools?: (string | ClientTool | ServerTool)[];\n\n /**\n * Model to use for emulation.\n * - Can be a model identifier string (e.g., \"anthropic:claude-sonnet-4-5-20250929\")\n * - Can be a BaseChatModel instance\n * - Defaults to agent model\n */\n model?: string | BaseChatModel;\n}\n\n/**\n * Middleware that emulates specified tools using an LLM instead of executing them.\n *\n * This middleware allows selective emulation of tools for testing purposes.\n * By default (when `tools` is undefined), all tools are emulated. You can specify\n * which tools to emulate by passing a list of tool names or tool instances.\n *\n * @param options - Configuration options for the middleware\n * @param options.tools - List of tool names or tool instances to emulate. If undefined, all tools are emulated.\n * @param options.model - Model to use for emulation. Defaults to \"anthropic:claude-sonnet-4-5-20250929\".\n *\n * @example Emulate all tools (default behavior)\n * ```ts\n * import { toolEmulatorMiddleware } from \"@langchain/langchain/agents/middleware\";\n * import { createAgent } from \"@langchain/langchain/agents\";\n *\n * const middleware = toolEmulatorMiddleware();\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [getWeather, getUserLocation, calculator],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example Emulate specific tools by name\n * ```ts\n * const middleware = toolEmulatorMiddleware({\n * tools: [\"get_weather\", \"get_user_location\"]\n * });\n * ```\n *\n * @example Use a custom model for emulation\n * ```ts\n * const middleware = toolEmulatorMiddleware({\n * tools: [\"get_weather\"],\n * model: \"anthropic:claude-sonnet-4-5-20250929\"\n * });\n * ```\n *\n * @example Emulate specific tools by passing tool instances\n * ```ts\n * const middleware = toolEmulatorMiddleware({\n * tools: [getWeather, getUserLocation]\n * });\n * ```\n */\nexport function toolEmulatorMiddleware(\n options: ToolEmulatorOptions = {}\n): ReturnType<typeof createMiddleware> {\n let agentModel: BaseChatModel | undefined;\n const { tools, model } = options;\n\n /**\n * Extract tool names from tools\n */\n const emulateAll = !tools || tools.length === 0;\n const toolsToEmulate = new Set<string>();\n\n if (!emulateAll && tools) {\n for (const tool of tools) {\n if (typeof tool === \"string\") {\n toolsToEmulate.add(tool);\n } else {\n // Assume tool instance with .name property\n const toolName =\n typeof tool.name === \"string\" ? tool.name : String(tool.name);\n toolsToEmulate.add(toolName);\n }\n }\n }\n\n /**\n * Initialize emulator model\n * We'll initialize it lazily in wrapToolCall to handle async initChatModel\n */\n let emulatorModel: BaseChatModel | undefined;\n const getEmulatorModel = async (): Promise<BaseChatModel> => {\n if (typeof model === \"object\") {\n return model;\n }\n if (typeof model === \"string\") {\n emulatorModel =\n emulatorModel ??\n (await initChatModel(model, { temperature: 1 }).catch((err) => {\n console.error(\n \"Error initializing emulator model, using agent model:\",\n err\n );\n return agentModel as BaseChatModel;\n }));\n return emulatorModel;\n }\n return agentModel as BaseChatModel;\n };\n\n return createMiddleware({\n name: \"ToolEmulatorMiddleware\",\n wrapModelCall: async (request, handler) => {\n agentModel = request.model as BaseChatModel;\n return handler(request);\n },\n wrapToolCall: async (request, handler) => {\n const toolName = request.toolCall.name;\n\n // Check if this tool should be emulated\n const shouldEmulate = emulateAll || toolsToEmulate.has(toolName);\n\n if (!shouldEmulate) {\n // Let it execute normally by calling the handler\n return handler(request);\n }\n\n // Extract tool information for emulation\n const toolArgs = request.toolCall.args;\n const toolDescription =\n request.tool?.description || \"No description available\";\n\n // Build prompt for emulator LLM\n const toolArgsString =\n typeof toolArgs === \"string\" ? toolArgs : JSON.stringify(toolArgs);\n const prompt = `You are emulating a tool call for testing purposes.\n\nTool: ${toolName}\nDescription: ${toolDescription}\nArguments: ${toolArgsString}\n\nGenerate a realistic response that this tool would return given these arguments.\nReturn ONLY the tool's output, no explanation or preamble. Introduce variation into your responses.`;\n\n // Get emulated response from LLM\n const emulator = await getEmulatorModel();\n const response = await emulator.invoke([new HumanMessage(prompt)]);\n\n // Extract content from response\n const content =\n typeof response.content === \"string\"\n ? response.content\n : JSON.stringify(response.content);\n\n // Short-circuit: return emulated result without executing real tool\n return new ToolMessage({\n content,\n tool_call_id: request.toolCall.id ?? \"\",\n name: toolName,\n });\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EA,SAAgB,uBACd,UAA+B,EAAE,EACI;CACrC,IAAI;CACJ,MAAM,EAAE,OAAO,UAAU;;;;CAKzB,MAAM,aAAa,CAAC,SAAS,MAAM,WAAW;CAC9C,MAAM,iCAAiB,IAAI,KAAa;AAExC,KAAI,CAAC,cAAc,MACjB,MAAK,MAAM,QAAQ,MACjB,KAAI,OAAO,SAAS,SAClB,gBAAe,IAAI,KAAK;MACnB;EAEL,MAAM,WACJ,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,OAAO,KAAK,KAAK;AAC/D,iBAAe,IAAI,SAAS;;;;;;CASlC,IAAI;CACJ,MAAM,mBAAmB,YAAoC;AAC3D,MAAI,OAAO,UAAU,SACnB,QAAO;AAET,MAAI,OAAO,UAAU,UAAU;AAC7B,mBACE,iBACC,MAAMA,4CAAc,OAAO,EAAE,aAAa,GAAG,CAAC,CAAC,OAAO,QAAQ;AAC7D,YAAQ,MACN,yDACA,IACD;AACD,WAAO;KACP;AACJ,UAAO;;AAET,SAAO;;AAGT,QAAOC,oCAAiB;EACtB,MAAM;EACN,eAAe,OAAO,SAAS,YAAY;AACzC,gBAAa,QAAQ;AACrB,UAAO,QAAQ,QAAQ;;EAEzB,cAAc,OAAO,SAAS,YAAY;GACxC,MAAM,WAAW,QAAQ,SAAS;AAKlC,OAAI,EAFkB,cAAc,eAAe,IAAI,SAAS,EAI9D,QAAO,QAAQ,QAAQ;GAIzB,MAAM,WAAW,QAAQ,SAAS;GAOlC,MAAM,SAAS;;QAEb,SAAS;eAPT,QAAQ,MAAM,eAAe,2BAQN;aAJvB,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,SAAS,CAK9C;;;;GAOtB,MAAM,WAAW,OADA,MAAM,kBAAkB,EACT,OAAO,CAAC,IAAIC,sCAAa,OAAO,CAAC,CAAC;AASlE,UAAO,IAAIC,qCAAY;IACrB,SANA,OAAO,SAAS,YAAY,WACxB,SAAS,UACT,KAAK,UAAU,SAAS,QAAQ;IAKpC,cAAc,QAAQ,SAAS,MAAM;IACrC,MAAM;IACP,CAAC;;EAEL,CAAC"}
1
+ {"version":3,"file":"toolEmulator.cjs","names":["initChatModel","createMiddleware","HumanMessage","ToolMessage"],"sources":["../../../src/agents/middleware/toolEmulator.ts"],"sourcesContent":["import { HumanMessage, ToolMessage } from \"@langchain/core/messages\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport type { ClientTool, ServerTool } from \"@langchain/core/tools\";\nimport { initChatModel } from \"../../chat_models/universal.js\";\nimport { createMiddleware } from \"../middleware.js\";\n\n/**\n * Options for configuring the Tool Emulator middleware.\n */\nexport interface ToolEmulatorOptions {\n /**\n * List of tool names (string) or tool instances to emulate.\n * - If `undefined` (default), ALL tools will be emulated.\n * - If empty array, no tools will be emulated.\n * - If array with tool names/instances, only those tools will be emulated.\n */\n tools?: (string | ClientTool | ServerTool)[];\n\n /**\n * Model to use for emulation.\n * - Can be a model identifier string (e.g., \"anthropic:claude-sonnet-4-5-20250929\")\n * - Can be a BaseChatModel instance\n * - Defaults to agent model\n */\n model?: string | BaseChatModel;\n}\n\n/**\n * Middleware that emulates specified tools using an LLM instead of executing them.\n *\n * This middleware allows selective emulation of tools for testing purposes.\n * By default (when `tools` is undefined), all tools are emulated. You can specify\n * which tools to emulate by passing a list of tool names or tool instances.\n *\n * @param options - Configuration options for the middleware\n * @param options.tools - List of tool names or tool instances to emulate. If undefined, all tools are emulated.\n * @param options.model - Model to use for emulation. Defaults to \"anthropic:claude-sonnet-4-5-20250929\".\n *\n * @example Emulate all tools (default behavior)\n * ```ts\n * import { toolEmulatorMiddleware } from \"@langchain/langchain/agents/middleware\";\n * import { createAgent } from \"@langchain/langchain/agents\";\n *\n * const middleware = toolEmulatorMiddleware();\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [getWeather, getUserLocation, calculator],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example Emulate specific tools by name\n * ```ts\n * const middleware = toolEmulatorMiddleware({\n * tools: [\"get_weather\", \"get_user_location\"]\n * });\n * ```\n *\n * @example Use a custom model for emulation\n * ```ts\n * const middleware = toolEmulatorMiddleware({\n * tools: [\"get_weather\"],\n * model: \"anthropic:claude-sonnet-4-5-20250929\"\n * });\n * ```\n *\n * @example Emulate specific tools by passing tool instances\n * ```ts\n * const middleware = toolEmulatorMiddleware({\n * tools: [getWeather, getUserLocation]\n * });\n * ```\n */\nexport function toolEmulatorMiddleware(options: ToolEmulatorOptions = {}) {\n let agentModel: BaseChatModel | undefined;\n const { tools, model } = options;\n\n /**\n * Extract tool names from tools\n */\n const emulateAll = !tools || tools.length === 0;\n const toolsToEmulate = new Set<string>();\n\n if (!emulateAll && tools) {\n for (const tool of tools) {\n if (typeof tool === \"string\") {\n toolsToEmulate.add(tool);\n } else {\n // Assume tool instance with .name property\n const toolName =\n typeof tool.name === \"string\" ? tool.name : String(tool.name);\n toolsToEmulate.add(toolName);\n }\n }\n }\n\n /**\n * Initialize emulator model\n * We'll initialize it lazily in wrapToolCall to handle async initChatModel\n */\n let emulatorModel: BaseChatModel | undefined;\n const getEmulatorModel = async (): Promise<BaseChatModel> => {\n if (typeof model === \"object\") {\n return model;\n }\n if (typeof model === \"string\") {\n emulatorModel =\n emulatorModel ??\n (await initChatModel(model, { temperature: 1 }).catch((err) => {\n console.error(\n \"Error initializing emulator model, using agent model:\",\n err\n );\n return agentModel as BaseChatModel;\n }));\n return emulatorModel;\n }\n return agentModel as BaseChatModel;\n };\n\n return createMiddleware({\n name: \"ToolEmulatorMiddleware\",\n wrapModelCall: async (request, handler) => {\n agentModel = request.model as BaseChatModel;\n return handler(request);\n },\n wrapToolCall: async (request, handler) => {\n const toolName = request.toolCall.name;\n\n // Check if this tool should be emulated\n const shouldEmulate = emulateAll || toolsToEmulate.has(toolName);\n\n if (!shouldEmulate) {\n // Let it execute normally by calling the handler\n return handler(request);\n }\n\n // Extract tool information for emulation\n const toolArgs = request.toolCall.args;\n const toolDescription =\n request.tool?.description || \"No description available\";\n\n // Build prompt for emulator LLM\n const toolArgsString =\n typeof toolArgs === \"string\" ? toolArgs : JSON.stringify(toolArgs);\n const prompt = `You are emulating a tool call for testing purposes.\n\nTool: ${toolName}\nDescription: ${toolDescription}\nArguments: ${toolArgsString}\n\nGenerate a realistic response that this tool would return given these arguments.\nReturn ONLY the tool's output, no explanation or preamble. Introduce variation into your responses.`;\n\n // Get emulated response from LLM\n const emulator = await getEmulatorModel();\n const response = await emulator.invoke([new HumanMessage(prompt)]);\n\n // Extract content from response\n const content =\n typeof response.content === \"string\"\n ? response.content\n : JSON.stringify(response.content);\n\n // Short-circuit: return emulated result without executing real tool\n return new ToolMessage({\n content,\n tool_call_id: request.toolCall.id ?? \"\",\n name: toolName,\n });\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EA,SAAgB,uBAAuB,UAA+B,EAAE,EAAE;CACxE,IAAI;CACJ,MAAM,EAAE,OAAO,UAAU;;;;CAKzB,MAAM,aAAa,CAAC,SAAS,MAAM,WAAW;CAC9C,MAAM,iCAAiB,IAAI,KAAa;AAExC,KAAI,CAAC,cAAc,MACjB,MAAK,MAAM,QAAQ,MACjB,KAAI,OAAO,SAAS,SAClB,gBAAe,IAAI,KAAK;MACnB;EAEL,MAAM,WACJ,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,OAAO,KAAK,KAAK;AAC/D,iBAAe,IAAI,SAAS;;;;;;CASlC,IAAI;CACJ,MAAM,mBAAmB,YAAoC;AAC3D,MAAI,OAAO,UAAU,SACnB,QAAO;AAET,MAAI,OAAO,UAAU,UAAU;AAC7B,mBACE,iBACC,MAAMA,4CAAc,OAAO,EAAE,aAAa,GAAG,CAAC,CAAC,OAAO,QAAQ;AAC7D,YAAQ,MACN,yDACA,IACD;AACD,WAAO;KACP;AACJ,UAAO;;AAET,SAAO;;AAGT,QAAOC,oCAAiB;EACtB,MAAM;EACN,eAAe,OAAO,SAAS,YAAY;AACzC,gBAAa,QAAQ;AACrB,UAAO,QAAQ,QAAQ;;EAEzB,cAAc,OAAO,SAAS,YAAY;GACxC,MAAM,WAAW,QAAQ,SAAS;AAKlC,OAAI,EAFkB,cAAc,eAAe,IAAI,SAAS,EAI9D,QAAO,QAAQ,QAAQ;GAIzB,MAAM,WAAW,QAAQ,SAAS;GAOlC,MAAM,SAAS;;QAEb,SAAS;eAPT,QAAQ,MAAM,eAAe,2BAQN;aAJvB,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,SAAS,CAK9C;;;;GAOtB,MAAM,WAAW,OADA,MAAM,kBAAkB,EACT,OAAO,CAAC,IAAIC,sCAAa,OAAO,CAAC,CAAC;AASlE,UAAO,IAAIC,qCAAY;IACrB,SANA,OAAO,SAAS,YAAY,WACxB,SAAS,UACT,KAAK,UAAU,SAAS,QAAQ;IAKpC,cAAc,QAAQ,SAAS,MAAM;IACrC,MAAM;IACP,CAAC;;EAEL,CAAC"}
@@ -1,4 +1,4 @@
1
- import { createMiddleware } from "../middleware.cjs";
1
+ import { AgentMiddleware } from "./types.cjs";
2
2
  import { BaseChatModel } from "@langchain/core/language_models/chat_models";
3
3
  import { ClientTool, ServerTool } from "@langchain/core/tools";
4
4
 
@@ -69,7 +69,7 @@ interface ToolEmulatorOptions {
69
69
  * });
70
70
  * ```
71
71
  */
72
- declare function toolEmulatorMiddleware(options?: ToolEmulatorOptions): ReturnType<typeof createMiddleware>;
72
+ declare function toolEmulatorMiddleware(options?: ToolEmulatorOptions): AgentMiddleware<undefined, undefined, unknown, readonly (ServerTool | ClientTool)[]>;
73
73
  //#endregion
74
74
  export { ToolEmulatorOptions, toolEmulatorMiddleware };
75
75
  //# sourceMappingURL=toolEmulator.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toolEmulator.d.cts","names":[],"sources":["../../../src/agents/middleware/toolEmulator.ts"],"mappings":";;;;;;;AASA;UAAiB,mBAAA;;;;;;;EAOf,KAAA,aAAkB,UAAA,GAAa,UAAA;EAAb;;;;;;EAQlB,KAAA,YAAiB,aAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkDH,sBAAA,CACd,OAAA,GAAS,mBAAA,GACR,UAAA,QAAkB,gBAAA"}
1
+ {"version":3,"file":"toolEmulator.d.cts","names":[],"sources":["../../../src/agents/middleware/toolEmulator.ts"],"mappings":";;;;;;;;UASiB,mBAAA;EAAmB;;;;;;EAOlC,KAAA,aAAkB,UAAA,GAAa,UAAA;EAA/B;;;;;;EAQA,KAAA,YAAiB,aAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkDH,sBAAA,CAAuB,OAAA,GAAS,mBAAA,GAAwB,eAAA,0CAAA,UAAA,GAAA,UAAA"}
@@ -1,4 +1,4 @@
1
- import { createMiddleware } from "../middleware.js";
1
+ import { AgentMiddleware } from "./types.js";
2
2
  import { BaseChatModel } from "@langchain/core/language_models/chat_models";
3
3
  import { ClientTool, ServerTool } from "@langchain/core/tools";
4
4
 
@@ -69,7 +69,7 @@ interface ToolEmulatorOptions {
69
69
  * });
70
70
  * ```
71
71
  */
72
- declare function toolEmulatorMiddleware(options?: ToolEmulatorOptions): ReturnType<typeof createMiddleware>;
72
+ declare function toolEmulatorMiddleware(options?: ToolEmulatorOptions): AgentMiddleware<undefined, undefined, unknown, readonly (ServerTool | ClientTool)[]>;
73
73
  //#endregion
74
74
  export { ToolEmulatorOptions, toolEmulatorMiddleware };
75
75
  //# sourceMappingURL=toolEmulator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toolEmulator.d.ts","names":[],"sources":["../../../src/agents/middleware/toolEmulator.ts"],"mappings":";;;;;;;AASA;UAAiB,mBAAA;;;;;;;EAOf,KAAA,aAAkB,UAAA,GAAa,UAAA;EAAb;;;;;;EAQlB,KAAA,YAAiB,aAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkDH,sBAAA,CACd,OAAA,GAAS,mBAAA,GACR,UAAA,QAAkB,gBAAA"}
1
+ {"version":3,"file":"toolEmulator.d.ts","names":[],"sources":["../../../src/agents/middleware/toolEmulator.ts"],"mappings":";;;;;;;;UASiB,mBAAA;EAAmB;;;;;;EAOlC,KAAA,aAAkB,UAAA,GAAa,UAAA;EAA/B;;;;;;EAQA,KAAA,YAAiB,aAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkDH,sBAAA,CAAuB,OAAA,GAAS,mBAAA,GAAwB,eAAA,0CAAA,UAAA,GAAA,UAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"toolEmulator.js","names":[],"sources":["../../../src/agents/middleware/toolEmulator.ts"],"sourcesContent":["import { HumanMessage, ToolMessage } from \"@langchain/core/messages\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport type { ClientTool, ServerTool } from \"@langchain/core/tools\";\nimport { initChatModel } from \"../../chat_models/universal.js\";\nimport { createMiddleware } from \"../middleware.js\";\n\n/**\n * Options for configuring the Tool Emulator middleware.\n */\nexport interface ToolEmulatorOptions {\n /**\n * List of tool names (string) or tool instances to emulate.\n * - If `undefined` (default), ALL tools will be emulated.\n * - If empty array, no tools will be emulated.\n * - If array with tool names/instances, only those tools will be emulated.\n */\n tools?: (string | ClientTool | ServerTool)[];\n\n /**\n * Model to use for emulation.\n * - Can be a model identifier string (e.g., \"anthropic:claude-sonnet-4-5-20250929\")\n * - Can be a BaseChatModel instance\n * - Defaults to agent model\n */\n model?: string | BaseChatModel;\n}\n\n/**\n * Middleware that emulates specified tools using an LLM instead of executing them.\n *\n * This middleware allows selective emulation of tools for testing purposes.\n * By default (when `tools` is undefined), all tools are emulated. You can specify\n * which tools to emulate by passing a list of tool names or tool instances.\n *\n * @param options - Configuration options for the middleware\n * @param options.tools - List of tool names or tool instances to emulate. If undefined, all tools are emulated.\n * @param options.model - Model to use for emulation. Defaults to \"anthropic:claude-sonnet-4-5-20250929\".\n *\n * @example Emulate all tools (default behavior)\n * ```ts\n * import { toolEmulatorMiddleware } from \"@langchain/langchain/agents/middleware\";\n * import { createAgent } from \"@langchain/langchain/agents\";\n *\n * const middleware = toolEmulatorMiddleware();\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [getWeather, getUserLocation, calculator],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example Emulate specific tools by name\n * ```ts\n * const middleware = toolEmulatorMiddleware({\n * tools: [\"get_weather\", \"get_user_location\"]\n * });\n * ```\n *\n * @example Use a custom model for emulation\n * ```ts\n * const middleware = toolEmulatorMiddleware({\n * tools: [\"get_weather\"],\n * model: \"anthropic:claude-sonnet-4-5-20250929\"\n * });\n * ```\n *\n * @example Emulate specific tools by passing tool instances\n * ```ts\n * const middleware = toolEmulatorMiddleware({\n * tools: [getWeather, getUserLocation]\n * });\n * ```\n */\nexport function toolEmulatorMiddleware(\n options: ToolEmulatorOptions = {}\n): ReturnType<typeof createMiddleware> {\n let agentModel: BaseChatModel | undefined;\n const { tools, model } = options;\n\n /**\n * Extract tool names from tools\n */\n const emulateAll = !tools || tools.length === 0;\n const toolsToEmulate = new Set<string>();\n\n if (!emulateAll && tools) {\n for (const tool of tools) {\n if (typeof tool === \"string\") {\n toolsToEmulate.add(tool);\n } else {\n // Assume tool instance with .name property\n const toolName =\n typeof tool.name === \"string\" ? tool.name : String(tool.name);\n toolsToEmulate.add(toolName);\n }\n }\n }\n\n /**\n * Initialize emulator model\n * We'll initialize it lazily in wrapToolCall to handle async initChatModel\n */\n let emulatorModel: BaseChatModel | undefined;\n const getEmulatorModel = async (): Promise<BaseChatModel> => {\n if (typeof model === \"object\") {\n return model;\n }\n if (typeof model === \"string\") {\n emulatorModel =\n emulatorModel ??\n (await initChatModel(model, { temperature: 1 }).catch((err) => {\n console.error(\n \"Error initializing emulator model, using agent model:\",\n err\n );\n return agentModel as BaseChatModel;\n }));\n return emulatorModel;\n }\n return agentModel as BaseChatModel;\n };\n\n return createMiddleware({\n name: \"ToolEmulatorMiddleware\",\n wrapModelCall: async (request, handler) => {\n agentModel = request.model as BaseChatModel;\n return handler(request);\n },\n wrapToolCall: async (request, handler) => {\n const toolName = request.toolCall.name;\n\n // Check if this tool should be emulated\n const shouldEmulate = emulateAll || toolsToEmulate.has(toolName);\n\n if (!shouldEmulate) {\n // Let it execute normally by calling the handler\n return handler(request);\n }\n\n // Extract tool information for emulation\n const toolArgs = request.toolCall.args;\n const toolDescription =\n request.tool?.description || \"No description available\";\n\n // Build prompt for emulator LLM\n const toolArgsString =\n typeof toolArgs === \"string\" ? toolArgs : JSON.stringify(toolArgs);\n const prompt = `You are emulating a tool call for testing purposes.\n\nTool: ${toolName}\nDescription: ${toolDescription}\nArguments: ${toolArgsString}\n\nGenerate a realistic response that this tool would return given these arguments.\nReturn ONLY the tool's output, no explanation or preamble. Introduce variation into your responses.`;\n\n // Get emulated response from LLM\n const emulator = await getEmulatorModel();\n const response = await emulator.invoke([new HumanMessage(prompt)]);\n\n // Extract content from response\n const content =\n typeof response.content === \"string\"\n ? response.content\n : JSON.stringify(response.content);\n\n // Short-circuit: return emulated result without executing real tool\n return new ToolMessage({\n content,\n tool_call_id: request.toolCall.id ?? \"\",\n name: toolName,\n });\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EA,SAAgB,uBACd,UAA+B,EAAE,EACI;CACrC,IAAI;CACJ,MAAM,EAAE,OAAO,UAAU;;;;CAKzB,MAAM,aAAa,CAAC,SAAS,MAAM,WAAW;CAC9C,MAAM,iCAAiB,IAAI,KAAa;AAExC,KAAI,CAAC,cAAc,MACjB,MAAK,MAAM,QAAQ,MACjB,KAAI,OAAO,SAAS,SAClB,gBAAe,IAAI,KAAK;MACnB;EAEL,MAAM,WACJ,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,OAAO,KAAK,KAAK;AAC/D,iBAAe,IAAI,SAAS;;;;;;CASlC,IAAI;CACJ,MAAM,mBAAmB,YAAoC;AAC3D,MAAI,OAAO,UAAU,SACnB,QAAO;AAET,MAAI,OAAO,UAAU,UAAU;AAC7B,mBACE,iBACC,MAAM,cAAc,OAAO,EAAE,aAAa,GAAG,CAAC,CAAC,OAAO,QAAQ;AAC7D,YAAQ,MACN,yDACA,IACD;AACD,WAAO;KACP;AACJ,UAAO;;AAET,SAAO;;AAGT,QAAO,iBAAiB;EACtB,MAAM;EACN,eAAe,OAAO,SAAS,YAAY;AACzC,gBAAa,QAAQ;AACrB,UAAO,QAAQ,QAAQ;;EAEzB,cAAc,OAAO,SAAS,YAAY;GACxC,MAAM,WAAW,QAAQ,SAAS;AAKlC,OAAI,EAFkB,cAAc,eAAe,IAAI,SAAS,EAI9D,QAAO,QAAQ,QAAQ;GAIzB,MAAM,WAAW,QAAQ,SAAS;GAOlC,MAAM,SAAS;;QAEb,SAAS;eAPT,QAAQ,MAAM,eAAe,2BAQN;aAJvB,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,SAAS,CAK9C;;;;GAOtB,MAAM,WAAW,OADA,MAAM,kBAAkB,EACT,OAAO,CAAC,IAAI,aAAa,OAAO,CAAC,CAAC;AASlE,UAAO,IAAI,YAAY;IACrB,SANA,OAAO,SAAS,YAAY,WACxB,SAAS,UACT,KAAK,UAAU,SAAS,QAAQ;IAKpC,cAAc,QAAQ,SAAS,MAAM;IACrC,MAAM;IACP,CAAC;;EAEL,CAAC"}
1
+ {"version":3,"file":"toolEmulator.js","names":[],"sources":["../../../src/agents/middleware/toolEmulator.ts"],"sourcesContent":["import { HumanMessage, ToolMessage } from \"@langchain/core/messages\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport type { ClientTool, ServerTool } from \"@langchain/core/tools\";\nimport { initChatModel } from \"../../chat_models/universal.js\";\nimport { createMiddleware } from \"../middleware.js\";\n\n/**\n * Options for configuring the Tool Emulator middleware.\n */\nexport interface ToolEmulatorOptions {\n /**\n * List of tool names (string) or tool instances to emulate.\n * - If `undefined` (default), ALL tools will be emulated.\n * - If empty array, no tools will be emulated.\n * - If array with tool names/instances, only those tools will be emulated.\n */\n tools?: (string | ClientTool | ServerTool)[];\n\n /**\n * Model to use for emulation.\n * - Can be a model identifier string (e.g., \"anthropic:claude-sonnet-4-5-20250929\")\n * - Can be a BaseChatModel instance\n * - Defaults to agent model\n */\n model?: string | BaseChatModel;\n}\n\n/**\n * Middleware that emulates specified tools using an LLM instead of executing them.\n *\n * This middleware allows selective emulation of tools for testing purposes.\n * By default (when `tools` is undefined), all tools are emulated. You can specify\n * which tools to emulate by passing a list of tool names or tool instances.\n *\n * @param options - Configuration options for the middleware\n * @param options.tools - List of tool names or tool instances to emulate. If undefined, all tools are emulated.\n * @param options.model - Model to use for emulation. Defaults to \"anthropic:claude-sonnet-4-5-20250929\".\n *\n * @example Emulate all tools (default behavior)\n * ```ts\n * import { toolEmulatorMiddleware } from \"@langchain/langchain/agents/middleware\";\n * import { createAgent } from \"@langchain/langchain/agents\";\n *\n * const middleware = toolEmulatorMiddleware();\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [getWeather, getUserLocation, calculator],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example Emulate specific tools by name\n * ```ts\n * const middleware = toolEmulatorMiddleware({\n * tools: [\"get_weather\", \"get_user_location\"]\n * });\n * ```\n *\n * @example Use a custom model for emulation\n * ```ts\n * const middleware = toolEmulatorMiddleware({\n * tools: [\"get_weather\"],\n * model: \"anthropic:claude-sonnet-4-5-20250929\"\n * });\n * ```\n *\n * @example Emulate specific tools by passing tool instances\n * ```ts\n * const middleware = toolEmulatorMiddleware({\n * tools: [getWeather, getUserLocation]\n * });\n * ```\n */\nexport function toolEmulatorMiddleware(options: ToolEmulatorOptions = {}) {\n let agentModel: BaseChatModel | undefined;\n const { tools, model } = options;\n\n /**\n * Extract tool names from tools\n */\n const emulateAll = !tools || tools.length === 0;\n const toolsToEmulate = new Set<string>();\n\n if (!emulateAll && tools) {\n for (const tool of tools) {\n if (typeof tool === \"string\") {\n toolsToEmulate.add(tool);\n } else {\n // Assume tool instance with .name property\n const toolName =\n typeof tool.name === \"string\" ? tool.name : String(tool.name);\n toolsToEmulate.add(toolName);\n }\n }\n }\n\n /**\n * Initialize emulator model\n * We'll initialize it lazily in wrapToolCall to handle async initChatModel\n */\n let emulatorModel: BaseChatModel | undefined;\n const getEmulatorModel = async (): Promise<BaseChatModel> => {\n if (typeof model === \"object\") {\n return model;\n }\n if (typeof model === \"string\") {\n emulatorModel =\n emulatorModel ??\n (await initChatModel(model, { temperature: 1 }).catch((err) => {\n console.error(\n \"Error initializing emulator model, using agent model:\",\n err\n );\n return agentModel as BaseChatModel;\n }));\n return emulatorModel;\n }\n return agentModel as BaseChatModel;\n };\n\n return createMiddleware({\n name: \"ToolEmulatorMiddleware\",\n wrapModelCall: async (request, handler) => {\n agentModel = request.model as BaseChatModel;\n return handler(request);\n },\n wrapToolCall: async (request, handler) => {\n const toolName = request.toolCall.name;\n\n // Check if this tool should be emulated\n const shouldEmulate = emulateAll || toolsToEmulate.has(toolName);\n\n if (!shouldEmulate) {\n // Let it execute normally by calling the handler\n return handler(request);\n }\n\n // Extract tool information for emulation\n const toolArgs = request.toolCall.args;\n const toolDescription =\n request.tool?.description || \"No description available\";\n\n // Build prompt for emulator LLM\n const toolArgsString =\n typeof toolArgs === \"string\" ? toolArgs : JSON.stringify(toolArgs);\n const prompt = `You are emulating a tool call for testing purposes.\n\nTool: ${toolName}\nDescription: ${toolDescription}\nArguments: ${toolArgsString}\n\nGenerate a realistic response that this tool would return given these arguments.\nReturn ONLY the tool's output, no explanation or preamble. Introduce variation into your responses.`;\n\n // Get emulated response from LLM\n const emulator = await getEmulatorModel();\n const response = await emulator.invoke([new HumanMessage(prompt)]);\n\n // Extract content from response\n const content =\n typeof response.content === \"string\"\n ? response.content\n : JSON.stringify(response.content);\n\n // Short-circuit: return emulated result without executing real tool\n return new ToolMessage({\n content,\n tool_call_id: request.toolCall.id ?? \"\",\n name: toolName,\n });\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EA,SAAgB,uBAAuB,UAA+B,EAAE,EAAE;CACxE,IAAI;CACJ,MAAM,EAAE,OAAO,UAAU;;;;CAKzB,MAAM,aAAa,CAAC,SAAS,MAAM,WAAW;CAC9C,MAAM,iCAAiB,IAAI,KAAa;AAExC,KAAI,CAAC,cAAc,MACjB,MAAK,MAAM,QAAQ,MACjB,KAAI,OAAO,SAAS,SAClB,gBAAe,IAAI,KAAK;MACnB;EAEL,MAAM,WACJ,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,OAAO,KAAK,KAAK;AAC/D,iBAAe,IAAI,SAAS;;;;;;CASlC,IAAI;CACJ,MAAM,mBAAmB,YAAoC;AAC3D,MAAI,OAAO,UAAU,SACnB,QAAO;AAET,MAAI,OAAO,UAAU,UAAU;AAC7B,mBACE,iBACC,MAAM,cAAc,OAAO,EAAE,aAAa,GAAG,CAAC,CAAC,OAAO,QAAQ;AAC7D,YAAQ,MACN,yDACA,IACD;AACD,WAAO;KACP;AACJ,UAAO;;AAET,SAAO;;AAGT,QAAO,iBAAiB;EACtB,MAAM;EACN,eAAe,OAAO,SAAS,YAAY;AACzC,gBAAa,QAAQ;AACrB,UAAO,QAAQ,QAAQ;;EAEzB,cAAc,OAAO,SAAS,YAAY;GACxC,MAAM,WAAW,QAAQ,SAAS;AAKlC,OAAI,EAFkB,cAAc,eAAe,IAAI,SAAS,EAI9D,QAAO,QAAQ,QAAQ;GAIzB,MAAM,WAAW,QAAQ,SAAS;GAOlC,MAAM,SAAS;;QAEb,SAAS;eAPT,QAAQ,MAAM,eAAe,2BAQN;aAJvB,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,SAAS,CAK9C;;;;GAOtB,MAAM,WAAW,OADA,MAAM,kBAAkB,EACT,OAAO,CAAC,IAAI,aAAa,OAAO,CAAC,CAAC;AASlE,UAAO,IAAI,YAAY;IACrB,SANA,OAAO,SAAS,YAAY,WACxB,SAAS,UACT,KAAK,UAAU,SAAS,QAAQ;IAKpC,cAAc,QAAQ,SAAS,MAAM;IACrC,MAAM;IACP,CAAC;;EAEL,CAAC"}
@@ -1 +1 @@
1
- {"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 type { AgentMiddleware } from \"./types.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(\n config: ToolRetryMiddlewareConfig = {}\n): AgentMiddleware {\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 // eslint-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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"}
1
+ {"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 // eslint-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":";;;;;;;;;;;;;;;AAeA,MAAa,mCAAmCA,SAC7C,OAAO;CAMN,OAAOA,SACJ,MACCA,SAAE,MAAM;EAACA,SAAE,QAAoB;EAAEA,SAAE,QAAoB;EAAEA,SAAE,QAAQ;EAAC,CAAC,CACtE,CACA,UAAU;CAcb,WAAWA,SACR,MAAM;EACLA,SAAE,QAAQ,QAAQ;EAClBA,SAAE,QAAQ,WAAW;EAIrBA,SAAE,QAAQ,QAAQ;EAIlBA,SAAE,QAAQ,iBAAiB;EAC3BA,SAAE,UAAU,CAAC,KAAKA,SAAE,WAAW,MAAM,CAAC,CAAC,QAAQA,SAAE,QAAQ,CAAC;EAC3D,CAAC,CACD,QAAQ,WAAW;CACvB,CAAC,CACD,MAAMC,8BAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyGrB,SAAgB,oBAAoB,SAAoC,EAAE,EAAE;CAC1E,MAAM,EAAE,SAAS,OAAO,SACtB,iCAAiC,UAAU,OAAO;AACpD,KAAI,CAAC,QACH,OAAM,IAAIC,sCAAwB,MAAM;CAE1C,MAAM,EACJ,YACA,OACA,SACA,WAAW,iBACX,eACA,gBACA,YACA,WACE;CAEJ,IAAI,YAAY;AAChB,KAAI,oBAAoB,SAAS;AAC/B,UAAQ,KACN,2EACD;AACD,cAAY;YACH,oBAAoB,kBAAkB;AAC/C,UAAQ,KACN,uFACD;AACD,cAAY;;CAId,MAAM,aAAuB,EAAE;AAC/B,MAAK,MAAM,QAAQ,SAAS,EAAE,CAC5B,KAAI,OAAO,SAAS,SAClB,YAAW,KAAK,KAAK;UACZ,UAAU,QAAQ,OAAO,KAAK,SAAS,SAChD,YAAW,KAAK,KAAK,KAAK;KAE1B,OAAM,IAAI,UACR,mFACD;;;;CAOL,MAAM,mBAAmB,aAA8B;AACrD,MAAI,WAAW,WAAW,EACxB,QAAO;AAET,SAAO,WAAW,SAAS,SAAS;;;;;CAMtC,MAAM,wBAAwB,UAA0B;AACtD,MAAI,OAAO,YAAY,WACrB,QAAO,QAAQ,MAAM;AAGvB,SAAO,QAAQ,MAAM,qBAAqB;AAExC,UAAO,iBAAiB;IACxB;;CAIJ,MAAM,cAAc;EAAE;EAAe;EAAgB;EAAY;EAAQ;;;;CAKzE,MAAM,wBACJ,UACA,OACA,iBACW;EACX,MAAM,YAAY,MAAM,YAAY;AAEpC,SAAO,SAAS,SAAS,iBAAiB,aAAa,GADnC,iBAAiB,IAAI,YAAY,WACiB,QAAQ;;;;;CAMhF,MAAM,iBACJ,UACA,YACA,OACA,iBACgB;AAChB,MAAI,cAAc,QAChB,OAAM;EAGR,IAAI;AACJ,MAAI,OAAO,cAAc,WACvB,WAAU,UAAU,MAAM;MAE1B,WAAU,qBAAqB,UAAU,OAAO,aAAa;AAG/D,SAAO,IAAIC,qCAAY;GACrB;GACA,cAAc;GACd,MAAM;GACN,QAAQ;GACT,CAAC;;AAGJ,QAAOC,oCAAiB;EACtB,MAAM;EACN,eAAe;EACf,cAAc,OAAO,SAAS,YAAY;GACxC,MAAM,WAAY,QAAQ,MAAM,QAAQ,QAAQ,SAAS;AAGzD,OAAI,CAAC,gBAAgB,SAAS,CAC5B,QAAO,QAAQ,QAAQ;GAGzB,MAAM,aAAa,QAAQ,SAAS,MAAM;AAG1C,QAAK,IAAI,UAAU,GAAG,WAAW,YAAY,UAC3C,KAAI;AACF,WAAO,MAAM,QAAQ,QAAQ;YACtB,OAAO;IACd,MAAM,eAAe,UAAU;IAG/B,MAAM,MACJ,SAAS,OAAO,UAAU,YAAY,aAAa,QAC9C,QACD,IAAI,MAAM,OAAO,MAAM,CAAC;AAG9B,QAAI,CAAC,qBAAqB,IAAI,CAE5B,QAAO,cAAc,UAAU,YAAY,KAAK,aAAa;AAI/D,QAAI,UAAU,YAAY;KAExB,MAAM,QAAQC,kCAAoB,aAAa,QAAQ;AACvD,SAAI,QAAQ,EACV,OAAMC,oBAAM,MAAM;UAKpB,QAAO,cAAc,UAAU,YAAY,KAAK,aAAa;;AAMnE,SAAM,IAAI,MAAM,qDAAqD;;EAExE,CAAC"}
@@ -152,7 +152,61 @@ type ToolRetryMiddlewareConfig = z.input<typeof ToolRetryMiddlewareOptionsSchema
152
152
  * @param config - Configuration options for the retry middleware
153
153
  * @returns A middleware instance that handles tool failures with retries
154
154
  */
155
- declare function toolRetryMiddleware(config?: ToolRetryMiddlewareConfig): AgentMiddleware;
155
+ declare function toolRetryMiddleware(config?: ToolRetryMiddlewareConfig): AgentMiddleware<undefined, z.ZodObject<{
156
+ /**
157
+ * Optional list of tools or tool names to apply retry logic to.
158
+ * Can be a list of `BaseTool` instances or tool name strings.
159
+ * If `undefined`, applies to all tools. Default is `undefined`.
160
+ */
161
+ tools: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodType<ClientTool, z.ZodTypeDef, ClientTool>, z.ZodType<ServerTool, z.ZodTypeDef, ServerTool>, z.ZodString]>, "many">>;
162
+ /**
163
+ * Behavior when all retries are exhausted. Options:
164
+ * - `"continue"` (default): Return an AIMessage with error details, allowing
165
+ * the agent to potentially handle the failure gracefully.
166
+ * - `"error"`: Re-raise the exception, stopping agent execution.
167
+ * - Custom function: Function that takes the exception and returns a string
168
+ * for the AIMessage content, allowing custom error formatting.
169
+ *
170
+ * Deprecated values:
171
+ * - `"raise"`: use `"error"` instead.
172
+ * - `"return_message"`: use `"continue"` instead.
173
+ */
174
+ onFailure: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"error">, z.ZodLiteral<"continue">, z.ZodLiteral<"raise">, z.ZodLiteral<"return_message">, z.ZodFunction<z.ZodTuple<[z.ZodType<Error, z.ZodTypeDef, Error>], z.ZodUnknown>, z.ZodString>]>>;
175
+ } & {
176
+ maxRetries: z.ZodDefault<z.ZodNumber>;
177
+ retryOn: z.ZodDefault<z.ZodUnion<[z.ZodFunction<z.ZodTuple<[z.ZodType<Error, z.ZodTypeDef, Error>], z.ZodUnknown>, z.ZodBoolean>, z.ZodArray<z.ZodType<new (...args: any[]) => Error, z.ZodTypeDef, new (...args: any[]) => Error>, "many">]>>;
178
+ backoffFactor: z.ZodDefault<z.ZodNumber>;
179
+ initialDelayMs: z.ZodDefault<z.ZodNumber>;
180
+ maxDelayMs: z.ZodDefault<z.ZodNumber>;
181
+ jitter: z.ZodDefault<z.ZodBoolean>;
182
+ }, "strip", z.ZodTypeAny, {
183
+ maxRetries: number;
184
+ retryOn: (new (...args: any[]) => Error)[] | ((args_0: Error, ...args: unknown[]) => boolean);
185
+ backoffFactor: number;
186
+ initialDelayMs: number;
187
+ maxDelayMs: number;
188
+ jitter: boolean;
189
+ tools?: (string | ServerTool | ClientTool)[] | undefined;
190
+ onFailure: "continue" | "error" | "raise" | "return_message" | ((args_0: Error, ...args: unknown[]) => string);
191
+ }, {
192
+ maxRetries?: number | undefined;
193
+ retryOn?: (new (...args: any[]) => Error)[] | ((args_0: Error, ...args: unknown[]) => boolean) | undefined;
194
+ backoffFactor?: number | undefined;
195
+ initialDelayMs?: number | undefined;
196
+ maxDelayMs?: number | undefined;
197
+ jitter?: boolean | undefined;
198
+ tools?: (string | ServerTool | ClientTool)[] | undefined;
199
+ onFailure?: "continue" | "error" | "raise" | "return_message" | ((args_0: Error, ...args: unknown[]) => string) | undefined;
200
+ }>, {
201
+ maxRetries: number;
202
+ retryOn: (new (...args: any[]) => Error)[] | ((args_0: Error, ...args: unknown[]) => boolean);
203
+ backoffFactor: number;
204
+ initialDelayMs: number;
205
+ maxDelayMs: number;
206
+ jitter: boolean;
207
+ tools?: (string | ServerTool | ClientTool)[] | undefined;
208
+ onFailure: "continue" | "error" | "raise" | "return_message" | ((args_0: Error, ...args: unknown[]) => string);
209
+ }, readonly (ServerTool | ClientTool)[]>;
156
210
  //#endregion
157
211
  export { ToolRetryMiddlewareConfig, toolRetryMiddleware };
158
212
  //# sourceMappingURL=toolRetry.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toolRetry.d.cts","names":[],"sources":["../../../src/agents/middleware/toolRetry.ts"],"mappings":";;;;;;;;cAgBa,gCAAA,EAAgC,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA2CjC,yBAAA,GAA4B,CAAA,CAAE,KAAA,QACjC,gCAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsGO,mBAAA,CACd,MAAA,GAAQ,yBAAA,GACP,eAAA"}
1
+ {"version":3,"file":"toolRetry.d.cts","names":[],"sources":["../../../src/agents/middleware/toolRetry.ts"],"mappings":";;;;;AAeA;;;AAAA,cAAa,gCAAA,EAAgC,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA2CjC,yBAAA,GAA4B,CAAA,CAAE,KAAA,QACjC,gCAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsGO,mBAAA,CAAoB,MAAA,GAAQ,yBAAA,8BAA8B,CAAA,CAAA,SAAA"}
@@ -152,7 +152,61 @@ type ToolRetryMiddlewareConfig = z.input<typeof ToolRetryMiddlewareOptionsSchema
152
152
  * @param config - Configuration options for the retry middleware
153
153
  * @returns A middleware instance that handles tool failures with retries
154
154
  */
155
- declare function toolRetryMiddleware(config?: ToolRetryMiddlewareConfig): AgentMiddleware;
155
+ declare function toolRetryMiddleware(config?: ToolRetryMiddlewareConfig): AgentMiddleware<undefined, z.ZodObject<{
156
+ /**
157
+ * Optional list of tools or tool names to apply retry logic to.
158
+ * Can be a list of `BaseTool` instances or tool name strings.
159
+ * If `undefined`, applies to all tools. Default is `undefined`.
160
+ */
161
+ tools: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodType<ClientTool, z.ZodTypeDef, ClientTool>, z.ZodType<ServerTool, z.ZodTypeDef, ServerTool>, z.ZodString]>, "many">>;
162
+ /**
163
+ * Behavior when all retries are exhausted. Options:
164
+ * - `"continue"` (default): Return an AIMessage with error details, allowing
165
+ * the agent to potentially handle the failure gracefully.
166
+ * - `"error"`: Re-raise the exception, stopping agent execution.
167
+ * - Custom function: Function that takes the exception and returns a string
168
+ * for the AIMessage content, allowing custom error formatting.
169
+ *
170
+ * Deprecated values:
171
+ * - `"raise"`: use `"error"` instead.
172
+ * - `"return_message"`: use `"continue"` instead.
173
+ */
174
+ onFailure: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"error">, z.ZodLiteral<"continue">, z.ZodLiteral<"raise">, z.ZodLiteral<"return_message">, z.ZodFunction<z.ZodTuple<[z.ZodType<Error, z.ZodTypeDef, Error>], z.ZodUnknown>, z.ZodString>]>>;
175
+ } & {
176
+ maxRetries: z.ZodDefault<z.ZodNumber>;
177
+ retryOn: z.ZodDefault<z.ZodUnion<[z.ZodFunction<z.ZodTuple<[z.ZodType<Error, z.ZodTypeDef, Error>], z.ZodUnknown>, z.ZodBoolean>, z.ZodArray<z.ZodType<new (...args: any[]) => Error, z.ZodTypeDef, new (...args: any[]) => Error>, "many">]>>;
178
+ backoffFactor: z.ZodDefault<z.ZodNumber>;
179
+ initialDelayMs: z.ZodDefault<z.ZodNumber>;
180
+ maxDelayMs: z.ZodDefault<z.ZodNumber>;
181
+ jitter: z.ZodDefault<z.ZodBoolean>;
182
+ }, "strip", z.ZodTypeAny, {
183
+ maxRetries: number;
184
+ retryOn: (new (...args: any[]) => Error)[] | ((args_0: Error, ...args: unknown[]) => boolean);
185
+ backoffFactor: number;
186
+ initialDelayMs: number;
187
+ maxDelayMs: number;
188
+ jitter: boolean;
189
+ tools?: (string | ServerTool | ClientTool)[] | undefined;
190
+ onFailure: "continue" | "error" | "raise" | "return_message" | ((args_0: Error, ...args: unknown[]) => string);
191
+ }, {
192
+ maxRetries?: number | undefined;
193
+ retryOn?: (new (...args: any[]) => Error)[] | ((args_0: Error, ...args: unknown[]) => boolean) | undefined;
194
+ backoffFactor?: number | undefined;
195
+ initialDelayMs?: number | undefined;
196
+ maxDelayMs?: number | undefined;
197
+ jitter?: boolean | undefined;
198
+ tools?: (string | ServerTool | ClientTool)[] | undefined;
199
+ onFailure?: "continue" | "error" | "raise" | "return_message" | ((args_0: Error, ...args: unknown[]) => string) | undefined;
200
+ }>, {
201
+ maxRetries: number;
202
+ retryOn: (new (...args: any[]) => Error)[] | ((args_0: Error, ...args: unknown[]) => boolean);
203
+ backoffFactor: number;
204
+ initialDelayMs: number;
205
+ maxDelayMs: number;
206
+ jitter: boolean;
207
+ tools?: (string | ServerTool | ClientTool)[] | undefined;
208
+ onFailure: "continue" | "error" | "raise" | "return_message" | ((args_0: Error, ...args: unknown[]) => string);
209
+ }, readonly (ServerTool | ClientTool)[]>;
156
210
  //#endregion
157
211
  export { ToolRetryMiddlewareConfig, toolRetryMiddleware };
158
212
  //# sourceMappingURL=toolRetry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toolRetry.d.ts","names":[],"sources":["../../../src/agents/middleware/toolRetry.ts"],"mappings":";;;;;;;;cAgBa,gCAAA,EAAgC,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA2CjC,yBAAA,GAA4B,CAAA,CAAE,KAAA,QACjC,gCAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsGO,mBAAA,CACd,MAAA,GAAQ,yBAAA,GACP,eAAA"}
1
+ {"version":3,"file":"toolRetry.d.ts","names":[],"sources":["../../../src/agents/middleware/toolRetry.ts"],"mappings":";;;;;AAeA;;;AAAA,cAAa,gCAAA,EAAgC,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA2CjC,yBAAA,GAA4B,CAAA,CAAE,KAAA,QACjC,gCAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsGO,mBAAA,CAAoB,MAAA,GAAQ,yBAAA,8BAA8B,CAAA,CAAA,SAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"toolRetry.js","names":[],"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 type { AgentMiddleware } from \"./types.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(\n config: ToolRetryMiddlewareConfig = {}\n): AgentMiddleware {\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 // eslint-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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"}
1
+ {"version":3,"file":"toolRetry.js","names":[],"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 // eslint-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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"}