langchain 1.0.5 → 1.1.0
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.
- package/CHANGELOG.md +26 -0
- package/README.md +1 -1
- package/chat_models/universal.cjs +1 -0
- package/chat_models/universal.d.cts +1 -0
- package/chat_models/universal.d.ts +1 -0
- package/chat_models/universal.js +1 -0
- package/dist/agents/ReactAgent.cjs +43 -39
- package/dist/agents/ReactAgent.cjs.map +1 -1
- package/dist/agents/ReactAgent.js +46 -42
- package/dist/agents/ReactAgent.js.map +1 -1
- package/dist/agents/index.d.cts +0 -2
- package/dist/agents/index.d.ts +0 -2
- package/dist/agents/middleware/constants.cjs +16 -0
- package/dist/agents/middleware/constants.cjs.map +1 -0
- package/dist/agents/middleware/constants.js +15 -0
- package/dist/agents/middleware/constants.js.map +1 -0
- package/dist/agents/middleware/contextEditing.cjs.map +1 -1
- package/dist/agents/middleware/contextEditing.d.cts +23 -7
- package/dist/agents/middleware/contextEditing.d.ts +23 -7
- package/dist/agents/middleware/contextEditing.js.map +1 -1
- package/dist/agents/middleware/dynamicSystemPrompt.cjs +5 -2
- package/dist/agents/middleware/dynamicSystemPrompt.cjs.map +1 -1
- package/dist/agents/middleware/dynamicSystemPrompt.d.cts +2 -1
- package/dist/agents/middleware/dynamicSystemPrompt.d.ts +2 -1
- package/dist/agents/middleware/dynamicSystemPrompt.js +4 -2
- package/dist/agents/middleware/dynamicSystemPrompt.js.map +1 -1
- package/dist/agents/middleware/error.cjs +20 -0
- package/dist/agents/middleware/error.cjs.map +1 -0
- package/dist/agents/middleware/error.js +19 -0
- package/dist/agents/middleware/error.js.map +1 -0
- package/dist/agents/middleware/index.cjs +4 -2
- package/dist/agents/middleware/index.d.ts +18 -0
- package/dist/agents/middleware/index.js +4 -2
- package/dist/agents/middleware/modelRetry.cjs +162 -0
- package/dist/agents/middleware/modelRetry.cjs.map +1 -0
- package/dist/agents/middleware/modelRetry.d.cts +134 -0
- package/dist/agents/middleware/modelRetry.d.ts +134 -0
- package/dist/agents/middleware/modelRetry.js +161 -0
- package/dist/agents/middleware/modelRetry.js.map +1 -0
- package/dist/agents/middleware/{promptCaching.cjs → provider/anthropic/promptCaching.cjs} +3 -3
- package/dist/agents/middleware/provider/anthropic/promptCaching.cjs.map +1 -0
- package/dist/agents/middleware/{promptCaching.d.cts → provider/anthropic/promptCaching.d.cts} +2 -2
- package/dist/agents/middleware/{promptCaching.d.ts → provider/anthropic/promptCaching.d.ts} +2 -2
- package/dist/agents/middleware/{promptCaching.js → provider/anthropic/promptCaching.js} +2 -2
- package/dist/agents/middleware/provider/anthropic/promptCaching.js.map +1 -0
- package/dist/agents/middleware/provider/openai/moderation.cjs +299 -0
- package/dist/agents/middleware/provider/openai/moderation.cjs.map +1 -0
- package/dist/agents/middleware/provider/openai/moderation.d.cts +133 -0
- package/dist/agents/middleware/provider/openai/moderation.d.ts +133 -0
- package/dist/agents/middleware/provider/openai/moderation.js +298 -0
- package/dist/agents/middleware/provider/openai/moderation.js.map +1 -0
- package/dist/agents/middleware/summarization.d.cts +0 -4
- package/dist/agents/middleware/summarization.d.ts +0 -4
- package/dist/agents/middleware/todoListMiddleware.cjs +1 -1
- package/dist/agents/middleware/todoListMiddleware.cjs.map +1 -1
- package/dist/agents/middleware/todoListMiddleware.js +1 -1
- package/dist/agents/middleware/todoListMiddleware.js.map +1 -1
- package/dist/agents/middleware/toolRetry.cjs +32 -44
- package/dist/agents/middleware/toolRetry.cjs.map +1 -1
- package/dist/agents/middleware/toolRetry.d.cts +16 -36
- package/dist/agents/middleware/toolRetry.d.ts +16 -36
- package/dist/agents/middleware/toolRetry.js +32 -44
- package/dist/agents/middleware/toolRetry.js.map +1 -1
- package/dist/agents/middleware/types.d.cts +9 -10
- package/dist/agents/middleware/types.d.ts +9 -10
- package/dist/agents/middleware/utils.cjs +23 -0
- package/dist/agents/middleware/utils.cjs.map +1 -1
- package/dist/agents/middleware/utils.d.ts +2 -0
- package/dist/agents/middleware/utils.js +23 -1
- package/dist/agents/middleware/utils.js.map +1 -1
- package/dist/agents/nodes/AgentNode.cjs +72 -28
- package/dist/agents/nodes/AgentNode.cjs.map +1 -1
- package/dist/agents/nodes/AgentNode.js +74 -31
- package/dist/agents/nodes/AgentNode.js.map +1 -1
- package/dist/agents/nodes/ToolNode.cjs +5 -0
- package/dist/agents/nodes/ToolNode.cjs.map +1 -1
- package/dist/agents/nodes/ToolNode.js +5 -1
- package/dist/agents/nodes/ToolNode.js.map +1 -1
- package/dist/agents/nodes/types.d.cts +39 -3
- package/dist/agents/nodes/types.d.ts +39 -3
- package/dist/agents/responses.cjs.map +1 -1
- package/dist/agents/responses.d.cts +2 -19
- package/dist/agents/responses.d.ts +2 -19
- package/dist/agents/responses.js.map +1 -1
- package/dist/agents/runtime.d.ts +1 -0
- package/dist/agents/tests/utils.cjs +10 -1
- package/dist/agents/tests/utils.cjs.map +1 -1
- package/dist/agents/tests/utils.js +10 -1
- package/dist/agents/tests/utils.js.map +1 -1
- package/dist/agents/types.d.cts +68 -2
- package/dist/agents/types.d.ts +68 -2
- package/dist/agents/utils.cjs +15 -12
- package/dist/agents/utils.cjs.map +1 -1
- package/dist/agents/utils.js +16 -13
- package/dist/agents/utils.js.map +1 -1
- package/dist/chat_models/universal.cjs +50 -16
- package/dist/chat_models/universal.cjs.map +1 -1
- package/dist/chat_models/universal.d.cts +19 -1
- package/dist/chat_models/universal.d.ts +19 -1
- package/dist/chat_models/universal.js +50 -16
- package/dist/chat_models/universal.js.map +1 -1
- package/dist/index.cjs +8 -2
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.js +7 -3
- package/dist/load/import_constants.cjs +2 -1
- package/dist/load/import_constants.cjs.map +1 -1
- package/dist/load/import_constants.js +2 -1
- package/dist/load/import_constants.js.map +1 -1
- package/dist/load/import_map.cjs +2 -19
- package/dist/load/import_map.cjs.map +1 -1
- package/dist/load/import_map.js +2 -19
- package/dist/load/import_map.js.map +1 -1
- package/hub/node.cjs +1 -0
- package/hub/node.d.cts +1 -0
- package/hub/node.d.ts +1 -0
- package/hub/node.js +1 -0
- package/hub.cjs +1 -0
- package/hub.d.cts +1 -0
- package/hub.d.ts +1 -0
- package/hub.js +1 -0
- package/load/serializable.cjs +1 -0
- package/load/serializable.d.cts +1 -0
- package/load/serializable.d.ts +1 -0
- package/load/serializable.js +1 -0
- package/load.cjs +1 -0
- package/load.d.cts +1 -0
- package/load.d.ts +1 -0
- package/load.js +1 -0
- package/package.json +65 -52
- package/storage/encoder_backed.cjs +1 -0
- package/storage/encoder_backed.d.cts +1 -0
- package/storage/encoder_backed.d.ts +1 -0
- package/storage/encoder_backed.js +1 -0
- package/storage/file_system.cjs +1 -0
- package/storage/file_system.d.cts +1 -0
- package/storage/file_system.d.ts +1 -0
- package/storage/file_system.js +1 -0
- package/storage/in_memory.cjs +1 -0
- package/storage/in_memory.d.cts +1 -0
- package/storage/in_memory.d.ts +1 -0
- package/storage/in_memory.js +1 -0
- package/dist/agents/ReactAgent.d.cts.map +0 -1
- package/dist/agents/ReactAgent.d.ts.map +0 -1
- package/dist/agents/constants.cjs +0 -7
- package/dist/agents/constants.cjs.map +0 -1
- package/dist/agents/constants.d.cts.map +0 -1
- package/dist/agents/constants.d.ts.map +0 -1
- package/dist/agents/constants.js +0 -6
- package/dist/agents/constants.js.map +0 -1
- package/dist/agents/errors.d.cts.map +0 -1
- package/dist/agents/errors.d.ts.map +0 -1
- package/dist/agents/index.d.cts.map +0 -1
- package/dist/agents/index.d.ts.map +0 -1
- package/dist/agents/middleware/contextEditing.d.cts.map +0 -1
- package/dist/agents/middleware/contextEditing.d.ts.map +0 -1
- package/dist/agents/middleware/dynamicSystemPrompt.d.cts.map +0 -1
- package/dist/agents/middleware/dynamicSystemPrompt.d.ts.map +0 -1
- package/dist/agents/middleware/hitl.d.cts.map +0 -1
- package/dist/agents/middleware/hitl.d.ts.map +0 -1
- package/dist/agents/middleware/llmToolSelector.d.cts.map +0 -1
- package/dist/agents/middleware/llmToolSelector.d.ts.map +0 -1
- package/dist/agents/middleware/modelCallLimit.d.cts.map +0 -1
- package/dist/agents/middleware/modelCallLimit.d.ts.map +0 -1
- package/dist/agents/middleware/modelFallback.d.cts.map +0 -1
- package/dist/agents/middleware/modelFallback.d.ts.map +0 -1
- package/dist/agents/middleware/pii.d.cts.map +0 -1
- package/dist/agents/middleware/pii.d.ts.map +0 -1
- package/dist/agents/middleware/piiRedaction.d.cts.map +0 -1
- package/dist/agents/middleware/piiRedaction.d.ts.map +0 -1
- package/dist/agents/middleware/promptCaching.cjs.map +0 -1
- package/dist/agents/middleware/promptCaching.d.cts.map +0 -1
- package/dist/agents/middleware/promptCaching.d.ts.map +0 -1
- package/dist/agents/middleware/promptCaching.js.map +0 -1
- package/dist/agents/middleware/summarization.d.cts.map +0 -1
- package/dist/agents/middleware/summarization.d.ts.map +0 -1
- package/dist/agents/middleware/todoListMiddleware.d.cts.map +0 -1
- package/dist/agents/middleware/todoListMiddleware.d.ts.map +0 -1
- package/dist/agents/middleware/toolCallLimit.d.cts.map +0 -1
- package/dist/agents/middleware/toolCallLimit.d.ts.map +0 -1
- package/dist/agents/middleware/toolEmulator.d.cts.map +0 -1
- package/dist/agents/middleware/toolEmulator.d.ts.map +0 -1
- package/dist/agents/middleware/toolRetry.d.cts.map +0 -1
- package/dist/agents/middleware/toolRetry.d.ts.map +0 -1
- package/dist/agents/middleware/types.d.cts.map +0 -1
- package/dist/agents/middleware/types.d.ts.map +0 -1
- package/dist/agents/middleware/utils.d.cts.map +0 -1
- package/dist/agents/middleware/utils.d.ts.map +0 -1
- package/dist/agents/middleware.d.cts.map +0 -1
- package/dist/agents/middleware.d.ts.map +0 -1
- package/dist/agents/nodes/types.d.cts.map +0 -1
- package/dist/agents/nodes/types.d.ts.map +0 -1
- package/dist/agents/responses.d.cts.map +0 -1
- package/dist/agents/responses.d.ts.map +0 -1
- package/dist/agents/runtime.d.cts.map +0 -1
- package/dist/agents/runtime.d.ts.map +0 -1
- package/dist/agents/tests/utils.d.cts.map +0 -1
- package/dist/agents/tests/utils.d.ts.map +0 -1
- package/dist/agents/types.d.cts.map +0 -1
- package/dist/agents/types.d.ts.map +0 -1
- package/dist/chat_models/universal.d.cts.map +0 -1
- package/dist/chat_models/universal.d.ts.map +0 -1
- package/dist/hub/base.d.cts.map +0 -1
- package/dist/hub/base.d.ts.map +0 -1
- package/dist/hub/index.d.cts.map +0 -1
- package/dist/hub/index.d.ts.map +0 -1
- package/dist/hub/node.d.cts.map +0 -1
- package/dist/hub/node.d.ts.map +0 -1
- package/dist/load/import_type.d.cts.map +0 -1
- package/dist/load/import_type.d.ts.map +0 -1
- package/dist/load/index.d.cts.map +0 -1
- package/dist/load/index.d.ts.map +0 -1
- package/dist/storage/encoder_backed.d.cts.map +0 -1
- package/dist/storage/encoder_backed.d.ts.map +0 -1
- package/dist/storage/file_system.d.cts.map +0 -1
- package/dist/storage/file_system.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolRetry.js","names":["config: {\n backoffFactor: number;\n initialDelayMs: number;\n maxDelayMs: number;\n jitter: boolean;\n }","retryNumber: number","delay: number","config: ToolRetryMiddlewareConfig","toolFilter: string[]","toolName: string","error: Error","attemptsMade: number","toolCallId: string","content: string"],"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 } from \"./utils.js\";\n\n/**\n * Calculate delay for a retry attempt with exponential backoff and jitter.\n *\n * @param retryNumber - The retry attempt number (0-indexed)\n * @param config - Configuration for backoff calculation\n * @returns Delay in milliseconds before next retry\n *\n * @internal Exported for testing purposes\n */\nexport function calculateRetryDelay(\n config: {\n backoffFactor: number;\n initialDelayMs: number;\n maxDelayMs: number;\n jitter: boolean;\n },\n retryNumber: number\n): number {\n const { backoffFactor, initialDelayMs, maxDelayMs, jitter } = config;\n\n let delay: number;\n if (backoffFactor === 0.0) {\n delay = initialDelayMs;\n } else {\n delay = initialDelayMs * backoffFactor ** retryNumber;\n }\n\n // Cap at maxDelayMs\n delay = Math.min(delay, maxDelayMs);\n\n if (jitter && delay > 0) {\n const jitterAmount = delay * 0.25;\n delay = delay + (Math.random() * 2 - 1) * jitterAmount;\n // Ensure delay is not negative after jitter\n delay = Math.max(0, delay);\n }\n\n return delay;\n}\n\n/**\n * Configuration options for the Tool Retry Middleware.\n */\nexport const ToolRetryMiddlewareOptionsSchema = z.object({\n /**\n * Maximum number of retry attempts after the initial call.\n * Default is 2 retries (3 total attempts). Must be >= 0.\n */\n maxRetries: z.number().min(0).default(2),\n\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 * Either an array of error constructors to retry on, or a function\n * that takes an error and returns `true` if it should be retried.\n * Default is to retry on all errors.\n */\n retryOn: z\n .union([\n z.function().args(z.instanceof(Error)).returns(z.boolean()),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n z.array(z.custom<new (...args: any[]) => Error>()),\n ])\n .default(() => () => true),\n\n /**\n * Behavior when all retries are exhausted. Options:\n * - `\"return_message\"` (default): Return a ToolMessage with error details,\n * allowing the LLM to handle the failure and potentially recover.\n * - `\"raise\"`: Re-raise the exception, stopping agent execution.\n * - Custom function: Function that takes the exception and returns a string\n * for the ToolMessage content, allowing custom error formatting.\n */\n onFailure: z\n .union([\n z.literal(\"raise\"),\n z.literal(\"return_message\"),\n z.function().args(z.instanceof(Error)).returns(z.string()),\n ])\n .default(\"return_message\"),\n\n /**\n * Multiplier for exponential backoff. Each retry waits\n * `initialDelayMs * (backoffFactor ** retryNumber)` milliseconds.\n * Set to 0.0 for constant delay. Default is 2.0.\n */\n backoffFactor: z.number().min(0).default(2.0),\n\n /**\n * Initial delay in milliseconds before first retry. Default is 1000 (1 second).\n */\n initialDelayMs: z.number().min(0).default(1000),\n\n /**\n * Maximum delay in milliseconds between retries. Caps exponential\n * backoff growth. Default is 60000 (60 seconds).\n */\n maxDelayMs: z.number().min(0).default(60000),\n\n /**\n * Whether to add random jitter (±25%) to delay to avoid thundering herd.\n * Default is `true`.\n */\n jitter: z.boolean().default(true),\n});\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 {\n maxRetries,\n tools,\n retryOn,\n onFailure,\n backoffFactor,\n initialDelayMs,\n maxDelayMs,\n jitter,\n } = ToolRetryMiddlewareOptionsSchema.parse(config);\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(\n (ErrorConstructor) => error.constructor === ErrorConstructor\n );\n };\n\n // Use the exported calculateRetryDelay function with our config\n const delayConfig = { backoffFactor, initialDelayMs, maxDelayMs, jitter };\n\n /**\n * Format the failure message when retries are exhausted.\n */\n const formatFailureMessage = (\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 === \"raise\") {\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 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":["config: ToolRetryMiddlewareConfig","toolFilter: string[]","toolName: string","error: Error","error","attemptsMade: number","toolCallId: string","content: string"],"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,7 +1,7 @@
|
|
|
1
1
|
import { JumpToTarget } from "../constants.cjs";
|
|
2
2
|
import { AgentBuiltInState, Runtime } from "../runtime.cjs";
|
|
3
3
|
import { ModelRequest } from "../nodes/types.cjs";
|
|
4
|
-
import { AIMessage, ToolMessage } from "@langchain/core/messages";
|
|
4
|
+
import { AIMessage, SystemMessage, ToolMessage } from "@langchain/core/messages";
|
|
5
5
|
import { ClientTool, ServerTool } from "@langchain/core/tools";
|
|
6
6
|
import { InferInteropZodInput, InferInteropZodOutput, InteropZodDefault, InteropZodObject, InteropZodOptional } from "@langchain/core/utils/types";
|
|
7
7
|
import { ToolCall as ToolCall$1 } from "@langchain/core/messages/tool";
|
|
@@ -58,7 +58,14 @@ type WrapToolCallHook<TSchema extends InteropZodObject | undefined = undefined,
|
|
|
58
58
|
* @param request - The model request containing model, messages, systemPrompt, tools, state, and runtime
|
|
59
59
|
* @returns The AI message response from the model
|
|
60
60
|
*/
|
|
61
|
-
type WrapModelCallHandler<TSchema extends InteropZodObject | undefined = undefined, TContext = unknown> = (request: ModelRequest<NormalizedSchemaInput<TSchema>, TContext
|
|
61
|
+
type WrapModelCallHandler<TSchema extends InteropZodObject | undefined = undefined, TContext = unknown> = (request: Omit<ModelRequest<NormalizedSchemaInput<TSchema>, TContext>,
|
|
62
|
+
/**
|
|
63
|
+
* allow to reset the system prompt or system message
|
|
64
|
+
*/
|
|
65
|
+
"systemPrompt" | "systemMessage"> & {
|
|
66
|
+
systemPrompt?: string;
|
|
67
|
+
systemMessage?: SystemMessage;
|
|
68
|
+
}) => PromiseOrValue<AIMessage>;
|
|
62
69
|
/**
|
|
63
70
|
* Wrapper function type for the wrapModelCall hook.
|
|
64
71
|
* Allows middleware to intercept and modify model execution.
|
|
@@ -325,18 +332,10 @@ type InferMiddlewareStates<T = AgentMiddleware[]> = T extends readonly [] ? {} :
|
|
|
325
332
|
* Helper type to infer merged input state from an array of middleware (with optional defaults)
|
|
326
333
|
*/
|
|
327
334
|
type InferMiddlewareInputStates<T extends readonly AgentMiddleware[]> = T extends readonly [] ? {} : T extends readonly [infer First, ...infer Rest] ? First extends AgentMiddleware ? Rest extends readonly AgentMiddleware[] ? InferMiddlewareInputState<First> & InferMiddlewareInputStates<Rest> : InferMiddlewareInputState<First> : {} : {};
|
|
328
|
-
/**
|
|
329
|
-
* Helper type to infer merged state from an array of middleware (includes built-in state)
|
|
330
|
-
*/
|
|
331
|
-
|
|
332
335
|
/**
|
|
333
336
|
* Helper type to infer the input context schema type from a middleware (with optional defaults)
|
|
334
337
|
*/
|
|
335
338
|
type InferMiddlewareContextInput<T extends AgentMiddleware> = T extends AgentMiddleware<any, infer C, any> ? C extends InteropZodOptional<infer Inner> ? InferInteropZodInput<Inner> | undefined : C extends InteropZodObject ? InferInteropZodInput<C> : {} : {};
|
|
336
|
-
/**
|
|
337
|
-
* Helper type to infer merged context from an array of middleware
|
|
338
|
-
*/
|
|
339
|
-
|
|
340
339
|
/**
|
|
341
340
|
* Helper to merge two context types, preserving undefined unions
|
|
342
341
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { JumpToTarget } from "../constants.js";
|
|
2
2
|
import { AgentBuiltInState, Runtime } from "../runtime.js";
|
|
3
3
|
import { ModelRequest } from "../nodes/types.js";
|
|
4
|
-
import { AIMessage, ToolMessage } from "@langchain/core/messages";
|
|
4
|
+
import { AIMessage, SystemMessage, ToolMessage } from "@langchain/core/messages";
|
|
5
5
|
import { ClientTool, ServerTool } from "@langchain/core/tools";
|
|
6
6
|
import { AnnotationRoot, Command } from "@langchain/langgraph";
|
|
7
7
|
import { InteropZodToStateDefinition } from "@langchain/langgraph/zod";
|
|
@@ -58,7 +58,14 @@ type WrapToolCallHook<TSchema extends InteropZodObject | undefined = undefined,
|
|
|
58
58
|
* @param request - The model request containing model, messages, systemPrompt, tools, state, and runtime
|
|
59
59
|
* @returns The AI message response from the model
|
|
60
60
|
*/
|
|
61
|
-
type WrapModelCallHandler<TSchema extends InteropZodObject | undefined = undefined, TContext = unknown> = (request: ModelRequest<NormalizedSchemaInput<TSchema>, TContext
|
|
61
|
+
type WrapModelCallHandler<TSchema extends InteropZodObject | undefined = undefined, TContext = unknown> = (request: Omit<ModelRequest<NormalizedSchemaInput<TSchema>, TContext>,
|
|
62
|
+
/**
|
|
63
|
+
* allow to reset the system prompt or system message
|
|
64
|
+
*/
|
|
65
|
+
"systemPrompt" | "systemMessage"> & {
|
|
66
|
+
systemPrompt?: string;
|
|
67
|
+
systemMessage?: SystemMessage;
|
|
68
|
+
}) => PromiseOrValue<AIMessage>;
|
|
62
69
|
/**
|
|
63
70
|
* Wrapper function type for the wrapModelCall hook.
|
|
64
71
|
* Allows middleware to intercept and modify model execution.
|
|
@@ -325,18 +332,10 @@ type InferMiddlewareStates<T = AgentMiddleware[]> = T extends readonly [] ? {} :
|
|
|
325
332
|
* Helper type to infer merged input state from an array of middleware (with optional defaults)
|
|
326
333
|
*/
|
|
327
334
|
type InferMiddlewareInputStates<T extends readonly AgentMiddleware[]> = T extends readonly [] ? {} : T extends readonly [infer First, ...infer Rest] ? First extends AgentMiddleware ? Rest extends readonly AgentMiddleware[] ? InferMiddlewareInputState<First> & InferMiddlewareInputStates<Rest> : InferMiddlewareInputState<First> : {} : {};
|
|
328
|
-
/**
|
|
329
|
-
* Helper type to infer merged state from an array of middleware (includes built-in state)
|
|
330
|
-
*/
|
|
331
|
-
|
|
332
335
|
/**
|
|
333
336
|
* Helper type to infer the input context schema type from a middleware (with optional defaults)
|
|
334
337
|
*/
|
|
335
338
|
type InferMiddlewareContextInput<T extends AgentMiddleware> = T extends AgentMiddleware<any, infer C, any> ? C extends InteropZodOptional<infer Inner> ? InferInteropZodInput<Inner> | undefined : C extends InteropZodObject ? InferInteropZodInput<C> : {} : {};
|
|
336
|
-
/**
|
|
337
|
-
* Helper type to infer merged context from an array of middleware
|
|
338
|
-
*/
|
|
339
|
-
|
|
340
339
|
/**
|
|
341
340
|
* Helper to merge two context types, preserving undefined unions
|
|
342
341
|
*/
|
|
@@ -38,8 +38,31 @@ function getHookFunction(arg) {
|
|
|
38
38
|
function sleep(ms) {
|
|
39
39
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Calculate delay for a retry attempt with exponential backoff and jitter.
|
|
43
|
+
*
|
|
44
|
+
* @param retryNumber - The retry attempt number (0-indexed)
|
|
45
|
+
* @param config - Configuration for backoff calculation
|
|
46
|
+
* @returns Delay in milliseconds before next retry
|
|
47
|
+
*
|
|
48
|
+
* @internal Exported for testing purposes
|
|
49
|
+
*/
|
|
50
|
+
function calculateRetryDelay(config, retryNumber) {
|
|
51
|
+
const { backoffFactor, initialDelayMs, maxDelayMs, jitter } = config;
|
|
52
|
+
let delay;
|
|
53
|
+
if (backoffFactor === 0) delay = initialDelayMs;
|
|
54
|
+
else delay = initialDelayMs * backoffFactor ** retryNumber;
|
|
55
|
+
delay = Math.min(delay, maxDelayMs);
|
|
56
|
+
if (jitter && delay > 0) {
|
|
57
|
+
const jitterAmount = delay * .25;
|
|
58
|
+
delay = delay + (Math.random() * 2 - 1) * jitterAmount;
|
|
59
|
+
delay = Math.max(0, delay);
|
|
60
|
+
}
|
|
61
|
+
return delay;
|
|
62
|
+
}
|
|
41
63
|
|
|
42
64
|
//#endregion
|
|
65
|
+
exports.calculateRetryDelay = calculateRetryDelay;
|
|
43
66
|
exports.countTokensApproximately = countTokensApproximately;
|
|
44
67
|
exports.getHookConstraint = getHookConstraint;
|
|
45
68
|
exports.getHookFunction = getHookFunction;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.cjs","names":["messages: BaseMessage[]","textContent: string","AIMessage","ToolMessage","hook:\n | BeforeAgentHook\n | BeforeModelHook\n | AfterAgentHook\n | AfterModelHook\n | undefined","arg: BeforeAgentHook | BeforeModelHook | AfterAgentHook | AfterModelHook","ms: number"],"sources":["../../../src/agents/middleware/utils.ts"],"sourcesContent":["import {\n AIMessage,\n ToolMessage,\n type BaseMessage,\n} from \"@langchain/core/messages\";\nimport {\n AfterModelHook,\n AfterAgentHook,\n BeforeAgentHook,\n BeforeModelHook,\n} from \"./types.js\";\nimport { JumpToTarget } from \"../constants.js\";\n\n/**\n * Default token counter that approximates based on character count\n * @param messages Messages to count tokens for\n * @returns Approximate token count\n */\nexport function countTokensApproximately(messages: BaseMessage[]): number {\n let totalChars = 0;\n for (const msg of messages) {\n let textContent: string;\n if (typeof msg.content === \"string\") {\n textContent = msg.content;\n } else if (Array.isArray(msg.content)) {\n textContent = msg.content\n .map((item) => {\n if (typeof item === \"string\") return item;\n if (item.type === \"text\" && \"text\" in item) return item.text;\n return \"\";\n })\n .join(\"\");\n } else {\n textContent = \"\";\n }\n\n if (\n AIMessage.isInstance(msg) &&\n Array.isArray(msg.tool_calls) &&\n msg.tool_calls.length > 0\n ) {\n textContent += JSON.stringify(msg.tool_calls);\n }\n\n if (ToolMessage.isInstance(msg)) {\n textContent += msg.tool_call_id ?? \"\";\n }\n\n totalChars += textContent.length;\n }\n // Approximate 1 token = 4 characters\n return Math.ceil(totalChars / 4);\n}\n\nexport function getHookConstraint(\n hook:\n | BeforeAgentHook\n | BeforeModelHook\n | AfterAgentHook\n | AfterModelHook\n | undefined\n): JumpToTarget[] | undefined {\n if (!hook || typeof hook === \"function\") {\n return undefined;\n }\n return hook.canJumpTo;\n}\n\nexport function getHookFunction(\n arg: BeforeAgentHook | BeforeModelHook | AfterAgentHook | AfterModelHook\n) {\n if (typeof arg === \"function\") {\n return arg;\n }\n return arg.hook;\n}\n\n/**\n * Sleep for the specified number of milliseconds.\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":";;;;;;;;;AAkBA,SAAgB,yBAAyBA,UAAiC;CACxE,IAAI,aAAa;AACjB,MAAK,MAAM,OAAO,UAAU;EAC1B,IAAIC;AACJ,MAAI,OAAO,IAAI,YAAY,UACzB,cAAc,IAAI;WACT,MAAM,QAAQ,IAAI,QAAQ,EACnC,cAAc,IAAI,QACf,IAAI,CAAC,SAAS;AACb,OAAI,OAAO,SAAS,SAAU,QAAO;AACrC,OAAI,KAAK,SAAS,UAAU,UAAU,KAAM,QAAO,KAAK;AACxD,UAAO;EACR,EAAC,CACD,KAAK,GAAG;OAEX,cAAc;AAGhB,MACEC,oCAAU,WAAW,IAAI,IACzB,MAAM,QAAQ,IAAI,WAAW,IAC7B,IAAI,WAAW,SAAS,GAExB,eAAe,KAAK,UAAU,IAAI,WAAW;AAG/C,MAAIC,sCAAY,WAAW,IAAI,EAC7B,eAAe,IAAI,gBAAgB;EAGrC,cAAc,YAAY;CAC3B;AAED,QAAO,KAAK,KAAK,aAAa,EAAE;AACjC;AAED,SAAgB,kBACdC,MAM4B;AAC5B,KAAI,CAAC,QAAQ,OAAO,SAAS,WAC3B,QAAO;AAET,QAAO,KAAK;AACb;AAED,SAAgB,gBACdC,KACA;AACA,KAAI,OAAO,QAAQ,WACjB,QAAO;AAET,QAAO,IAAI;AACZ;;;;AAKD,SAAgB,MAAMC,IAA2B;AAC/C,QAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG;AACxD"}
|
|
1
|
+
{"version":3,"file":"utils.cjs","names":["messages: BaseMessage[]","textContent: string","AIMessage","ToolMessage","hook:\n | BeforeAgentHook\n | BeforeModelHook\n | AfterAgentHook\n | AfterModelHook\n | undefined","arg: BeforeAgentHook | BeforeModelHook | AfterAgentHook | AfterModelHook","ms: number","config: {\n backoffFactor: number;\n initialDelayMs: number;\n maxDelayMs: number;\n jitter: boolean;\n }","retryNumber: number","delay: number"],"sources":["../../../src/agents/middleware/utils.ts"],"sourcesContent":["import {\n AIMessage,\n ToolMessage,\n type BaseMessage,\n} from \"@langchain/core/messages\";\nimport {\n AfterModelHook,\n AfterAgentHook,\n BeforeAgentHook,\n BeforeModelHook,\n} from \"./types.js\";\nimport { JumpToTarget } from \"../constants.js\";\n\n/**\n * Default token counter that approximates based on character count\n * @param messages Messages to count tokens for\n * @returns Approximate token count\n */\nexport function countTokensApproximately(messages: BaseMessage[]): number {\n let totalChars = 0;\n for (const msg of messages) {\n let textContent: string;\n if (typeof msg.content === \"string\") {\n textContent = msg.content;\n } else if (Array.isArray(msg.content)) {\n textContent = msg.content\n .map((item) => {\n if (typeof item === \"string\") return item;\n if (item.type === \"text\" && \"text\" in item) return item.text;\n return \"\";\n })\n .join(\"\");\n } else {\n textContent = \"\";\n }\n\n if (\n AIMessage.isInstance(msg) &&\n Array.isArray(msg.tool_calls) &&\n msg.tool_calls.length > 0\n ) {\n textContent += JSON.stringify(msg.tool_calls);\n }\n\n if (ToolMessage.isInstance(msg)) {\n textContent += msg.tool_call_id ?? \"\";\n }\n\n totalChars += textContent.length;\n }\n // Approximate 1 token = 4 characters\n return Math.ceil(totalChars / 4);\n}\n\nexport function getHookConstraint(\n hook:\n | BeforeAgentHook\n | BeforeModelHook\n | AfterAgentHook\n | AfterModelHook\n | undefined\n): JumpToTarget[] | undefined {\n if (!hook || typeof hook === \"function\") {\n return undefined;\n }\n return hook.canJumpTo;\n}\n\nexport function getHookFunction(\n arg: BeforeAgentHook | BeforeModelHook | AfterAgentHook | AfterModelHook\n) {\n if (typeof arg === \"function\") {\n return arg;\n }\n return arg.hook;\n}\n\n/**\n * Sleep for the specified number of milliseconds.\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Calculate delay for a retry attempt with exponential backoff and jitter.\n *\n * @param retryNumber - The retry attempt number (0-indexed)\n * @param config - Configuration for backoff calculation\n * @returns Delay in milliseconds before next retry\n *\n * @internal Exported for testing purposes\n */\nexport function calculateRetryDelay(\n config: {\n backoffFactor: number;\n initialDelayMs: number;\n maxDelayMs: number;\n jitter: boolean;\n },\n retryNumber: number\n): number {\n const { backoffFactor, initialDelayMs, maxDelayMs, jitter } = config;\n\n let delay: number;\n if (backoffFactor === 0.0) {\n delay = initialDelayMs;\n } else {\n delay = initialDelayMs * backoffFactor ** retryNumber;\n }\n\n // Cap at maxDelayMs\n delay = Math.min(delay, maxDelayMs);\n\n if (jitter && delay > 0) {\n const jitterAmount = delay * 0.25;\n delay = delay + (Math.random() * 2 - 1) * jitterAmount;\n // Ensure delay is not negative after jitter\n delay = Math.max(0, delay);\n }\n\n return delay;\n}\n"],"mappings":";;;;;;;;;AAkBA,SAAgB,yBAAyBA,UAAiC;CACxE,IAAI,aAAa;AACjB,MAAK,MAAM,OAAO,UAAU;EAC1B,IAAIC;AACJ,MAAI,OAAO,IAAI,YAAY,UACzB,cAAc,IAAI;WACT,MAAM,QAAQ,IAAI,QAAQ,EACnC,cAAc,IAAI,QACf,IAAI,CAAC,SAAS;AACb,OAAI,OAAO,SAAS,SAAU,QAAO;AACrC,OAAI,KAAK,SAAS,UAAU,UAAU,KAAM,QAAO,KAAK;AACxD,UAAO;EACR,EAAC,CACD,KAAK,GAAG;OAEX,cAAc;AAGhB,MACEC,oCAAU,WAAW,IAAI,IACzB,MAAM,QAAQ,IAAI,WAAW,IAC7B,IAAI,WAAW,SAAS,GAExB,eAAe,KAAK,UAAU,IAAI,WAAW;AAG/C,MAAIC,sCAAY,WAAW,IAAI,EAC7B,eAAe,IAAI,gBAAgB;EAGrC,cAAc,YAAY;CAC3B;AAED,QAAO,KAAK,KAAK,aAAa,EAAE;AACjC;AAED,SAAgB,kBACdC,MAM4B;AAC5B,KAAI,CAAC,QAAQ,OAAO,SAAS,WAC3B,QAAO;AAET,QAAO,KAAK;AACb;AAED,SAAgB,gBACdC,KACA;AACA,KAAI,OAAO,QAAQ,WACjB,QAAO;AAET,QAAO,IAAI;AACZ;;;;AAKD,SAAgB,MAAMC,IAA2B;AAC/C,QAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG;AACxD;;;;;;;;;;AAWD,SAAgB,oBACdC,QAMAC,aACQ;CACR,MAAM,EAAE,eAAe,gBAAgB,YAAY,QAAQ,GAAG;CAE9D,IAAIC;AACJ,KAAI,kBAAkB,GACpB,QAAQ;MAER,QAAQ,iBAAiB,iBAAiB;CAI5C,QAAQ,KAAK,IAAI,OAAO,WAAW;AAEnC,KAAI,UAAU,QAAQ,GAAG;EACvB,MAAM,eAAe,QAAQ;EAC7B,QAAQ,SAAS,KAAK,QAAQ,GAAG,IAAI,KAAK;EAE1C,QAAQ,KAAK,IAAI,GAAG,MAAM;CAC3B;AAED,QAAO;AACR"}
|
|
@@ -37,7 +37,29 @@ function getHookFunction(arg) {
|
|
|
37
37
|
function sleep(ms) {
|
|
38
38
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Calculate delay for a retry attempt with exponential backoff and jitter.
|
|
42
|
+
*
|
|
43
|
+
* @param retryNumber - The retry attempt number (0-indexed)
|
|
44
|
+
* @param config - Configuration for backoff calculation
|
|
45
|
+
* @returns Delay in milliseconds before next retry
|
|
46
|
+
*
|
|
47
|
+
* @internal Exported for testing purposes
|
|
48
|
+
*/
|
|
49
|
+
function calculateRetryDelay(config, retryNumber) {
|
|
50
|
+
const { backoffFactor, initialDelayMs, maxDelayMs, jitter } = config;
|
|
51
|
+
let delay;
|
|
52
|
+
if (backoffFactor === 0) delay = initialDelayMs;
|
|
53
|
+
else delay = initialDelayMs * backoffFactor ** retryNumber;
|
|
54
|
+
delay = Math.min(delay, maxDelayMs);
|
|
55
|
+
if (jitter && delay > 0) {
|
|
56
|
+
const jitterAmount = delay * .25;
|
|
57
|
+
delay = delay + (Math.random() * 2 - 1) * jitterAmount;
|
|
58
|
+
delay = Math.max(0, delay);
|
|
59
|
+
}
|
|
60
|
+
return delay;
|
|
61
|
+
}
|
|
40
62
|
|
|
41
63
|
//#endregion
|
|
42
|
-
export { countTokensApproximately, getHookConstraint, getHookFunction, sleep };
|
|
64
|
+
export { calculateRetryDelay, countTokensApproximately, getHookConstraint, getHookFunction, sleep };
|
|
43
65
|
//# sourceMappingURL=utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":["messages: BaseMessage[]","textContent: string","hook:\n | BeforeAgentHook\n | BeforeModelHook\n | AfterAgentHook\n | AfterModelHook\n | undefined","arg: BeforeAgentHook | BeforeModelHook | AfterAgentHook | AfterModelHook","ms: number"],"sources":["../../../src/agents/middleware/utils.ts"],"sourcesContent":["import {\n AIMessage,\n ToolMessage,\n type BaseMessage,\n} from \"@langchain/core/messages\";\nimport {\n AfterModelHook,\n AfterAgentHook,\n BeforeAgentHook,\n BeforeModelHook,\n} from \"./types.js\";\nimport { JumpToTarget } from \"../constants.js\";\n\n/**\n * Default token counter that approximates based on character count\n * @param messages Messages to count tokens for\n * @returns Approximate token count\n */\nexport function countTokensApproximately(messages: BaseMessage[]): number {\n let totalChars = 0;\n for (const msg of messages) {\n let textContent: string;\n if (typeof msg.content === \"string\") {\n textContent = msg.content;\n } else if (Array.isArray(msg.content)) {\n textContent = msg.content\n .map((item) => {\n if (typeof item === \"string\") return item;\n if (item.type === \"text\" && \"text\" in item) return item.text;\n return \"\";\n })\n .join(\"\");\n } else {\n textContent = \"\";\n }\n\n if (\n AIMessage.isInstance(msg) &&\n Array.isArray(msg.tool_calls) &&\n msg.tool_calls.length > 0\n ) {\n textContent += JSON.stringify(msg.tool_calls);\n }\n\n if (ToolMessage.isInstance(msg)) {\n textContent += msg.tool_call_id ?? \"\";\n }\n\n totalChars += textContent.length;\n }\n // Approximate 1 token = 4 characters\n return Math.ceil(totalChars / 4);\n}\n\nexport function getHookConstraint(\n hook:\n | BeforeAgentHook\n | BeforeModelHook\n | AfterAgentHook\n | AfterModelHook\n | undefined\n): JumpToTarget[] | undefined {\n if (!hook || typeof hook === \"function\") {\n return undefined;\n }\n return hook.canJumpTo;\n}\n\nexport function getHookFunction(\n arg: BeforeAgentHook | BeforeModelHook | AfterAgentHook | AfterModelHook\n) {\n if (typeof arg === \"function\") {\n return arg;\n }\n return arg.hook;\n}\n\n/**\n * Sleep for the specified number of milliseconds.\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":";;;;;;;;AAkBA,SAAgB,yBAAyBA,UAAiC;CACxE,IAAI,aAAa;AACjB,MAAK,MAAM,OAAO,UAAU;EAC1B,IAAIC;AACJ,MAAI,OAAO,IAAI,YAAY,UACzB,cAAc,IAAI;WACT,MAAM,QAAQ,IAAI,QAAQ,EACnC,cAAc,IAAI,QACf,IAAI,CAAC,SAAS;AACb,OAAI,OAAO,SAAS,SAAU,QAAO;AACrC,OAAI,KAAK,SAAS,UAAU,UAAU,KAAM,QAAO,KAAK;AACxD,UAAO;EACR,EAAC,CACD,KAAK,GAAG;OAEX,cAAc;AAGhB,MACE,UAAU,WAAW,IAAI,IACzB,MAAM,QAAQ,IAAI,WAAW,IAC7B,IAAI,WAAW,SAAS,GAExB,eAAe,KAAK,UAAU,IAAI,WAAW;AAG/C,MAAI,YAAY,WAAW,IAAI,EAC7B,eAAe,IAAI,gBAAgB;EAGrC,cAAc,YAAY;CAC3B;AAED,QAAO,KAAK,KAAK,aAAa,EAAE;AACjC;AAED,SAAgB,kBACdC,MAM4B;AAC5B,KAAI,CAAC,QAAQ,OAAO,SAAS,WAC3B,QAAO;AAET,QAAO,KAAK;AACb;AAED,SAAgB,gBACdC,KACA;AACA,KAAI,OAAO,QAAQ,WACjB,QAAO;AAET,QAAO,IAAI;AACZ;;;;AAKD,SAAgB,MAAMC,IAA2B;AAC/C,QAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG;AACxD"}
|
|
1
|
+
{"version":3,"file":"utils.js","names":["messages: BaseMessage[]","textContent: string","hook:\n | BeforeAgentHook\n | BeforeModelHook\n | AfterAgentHook\n | AfterModelHook\n | undefined","arg: BeforeAgentHook | BeforeModelHook | AfterAgentHook | AfterModelHook","ms: number","config: {\n backoffFactor: number;\n initialDelayMs: number;\n maxDelayMs: number;\n jitter: boolean;\n }","retryNumber: number","delay: number"],"sources":["../../../src/agents/middleware/utils.ts"],"sourcesContent":["import {\n AIMessage,\n ToolMessage,\n type BaseMessage,\n} from \"@langchain/core/messages\";\nimport {\n AfterModelHook,\n AfterAgentHook,\n BeforeAgentHook,\n BeforeModelHook,\n} from \"./types.js\";\nimport { JumpToTarget } from \"../constants.js\";\n\n/**\n * Default token counter that approximates based on character count\n * @param messages Messages to count tokens for\n * @returns Approximate token count\n */\nexport function countTokensApproximately(messages: BaseMessage[]): number {\n let totalChars = 0;\n for (const msg of messages) {\n let textContent: string;\n if (typeof msg.content === \"string\") {\n textContent = msg.content;\n } else if (Array.isArray(msg.content)) {\n textContent = msg.content\n .map((item) => {\n if (typeof item === \"string\") return item;\n if (item.type === \"text\" && \"text\" in item) return item.text;\n return \"\";\n })\n .join(\"\");\n } else {\n textContent = \"\";\n }\n\n if (\n AIMessage.isInstance(msg) &&\n Array.isArray(msg.tool_calls) &&\n msg.tool_calls.length > 0\n ) {\n textContent += JSON.stringify(msg.tool_calls);\n }\n\n if (ToolMessage.isInstance(msg)) {\n textContent += msg.tool_call_id ?? \"\";\n }\n\n totalChars += textContent.length;\n }\n // Approximate 1 token = 4 characters\n return Math.ceil(totalChars / 4);\n}\n\nexport function getHookConstraint(\n hook:\n | BeforeAgentHook\n | BeforeModelHook\n | AfterAgentHook\n | AfterModelHook\n | undefined\n): JumpToTarget[] | undefined {\n if (!hook || typeof hook === \"function\") {\n return undefined;\n }\n return hook.canJumpTo;\n}\n\nexport function getHookFunction(\n arg: BeforeAgentHook | BeforeModelHook | AfterAgentHook | AfterModelHook\n) {\n if (typeof arg === \"function\") {\n return arg;\n }\n return arg.hook;\n}\n\n/**\n * Sleep for the specified number of milliseconds.\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Calculate delay for a retry attempt with exponential backoff and jitter.\n *\n * @param retryNumber - The retry attempt number (0-indexed)\n * @param config - Configuration for backoff calculation\n * @returns Delay in milliseconds before next retry\n *\n * @internal Exported for testing purposes\n */\nexport function calculateRetryDelay(\n config: {\n backoffFactor: number;\n initialDelayMs: number;\n maxDelayMs: number;\n jitter: boolean;\n },\n retryNumber: number\n): number {\n const { backoffFactor, initialDelayMs, maxDelayMs, jitter } = config;\n\n let delay: number;\n if (backoffFactor === 0.0) {\n delay = initialDelayMs;\n } else {\n delay = initialDelayMs * backoffFactor ** retryNumber;\n }\n\n // Cap at maxDelayMs\n delay = Math.min(delay, maxDelayMs);\n\n if (jitter && delay > 0) {\n const jitterAmount = delay * 0.25;\n delay = delay + (Math.random() * 2 - 1) * jitterAmount;\n // Ensure delay is not negative after jitter\n delay = Math.max(0, delay);\n }\n\n return delay;\n}\n"],"mappings":";;;;;;;;AAkBA,SAAgB,yBAAyBA,UAAiC;CACxE,IAAI,aAAa;AACjB,MAAK,MAAM,OAAO,UAAU;EAC1B,IAAIC;AACJ,MAAI,OAAO,IAAI,YAAY,UACzB,cAAc,IAAI;WACT,MAAM,QAAQ,IAAI,QAAQ,EACnC,cAAc,IAAI,QACf,IAAI,CAAC,SAAS;AACb,OAAI,OAAO,SAAS,SAAU,QAAO;AACrC,OAAI,KAAK,SAAS,UAAU,UAAU,KAAM,QAAO,KAAK;AACxD,UAAO;EACR,EAAC,CACD,KAAK,GAAG;OAEX,cAAc;AAGhB,MACE,UAAU,WAAW,IAAI,IACzB,MAAM,QAAQ,IAAI,WAAW,IAC7B,IAAI,WAAW,SAAS,GAExB,eAAe,KAAK,UAAU,IAAI,WAAW;AAG/C,MAAI,YAAY,WAAW,IAAI,EAC7B,eAAe,IAAI,gBAAgB;EAGrC,cAAc,YAAY;CAC3B;AAED,QAAO,KAAK,KAAK,aAAa,EAAE;AACjC;AAED,SAAgB,kBACdC,MAM4B;AAC5B,KAAI,CAAC,QAAQ,OAAO,SAAS,WAC3B,QAAO;AAET,QAAO,KAAK;AACb;AAED,SAAgB,gBACdC,KACA;AACA,KAAI,OAAO,QAAQ,WACjB,QAAO;AAET,QAAO,IAAI;AACZ;;;;AAKD,SAAgB,MAAMC,IAA2B;AAC/C,QAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG;AACxD;;;;;;;;;;AAWD,SAAgB,oBACdC,QAMAC,aACQ;CACR,MAAM,EAAE,eAAe,gBAAgB,YAAY,QAAQ,GAAG;CAE9D,IAAIC;AACJ,KAAI,kBAAkB,GACpB,QAAQ;MAER,QAAQ,iBAAiB,iBAAiB;CAI5C,QAAQ,KAAK,IAAI,OAAO,WAAW;AAEnC,KAAI,UAAU,QAAQ,GAAG;EACvB,MAAM,eAAe,QAAQ;EAC7B,QAAQ,SAAS,KAAK,QAAQ,GAAG,IAAI,KAAK;EAE1C,QAAQ,KAAK,IAAI,GAAG,MAAM;CAC3B;AAED,QAAO;AACR"}
|
|
@@ -7,18 +7,26 @@ const require_utils$1 = require('./utils.cjs');
|
|
|
7
7
|
const require_withAgentName = require('../withAgentName.cjs');
|
|
8
8
|
const require_responses = require('../responses.cjs');
|
|
9
9
|
const __langchain_core_messages = require_rolldown_runtime.__toESM(require("@langchain/core/messages"));
|
|
10
|
+
const __langchain_core_runnables = require_rolldown_runtime.__toESM(require("@langchain/core/runnables"));
|
|
10
11
|
const __langchain_langgraph = require_rolldown_runtime.__toESM(require("@langchain/langgraph"));
|
|
11
12
|
const __langchain_core_utils_types = require_rolldown_runtime.__toESM(require("@langchain/core/utils/types"));
|
|
12
13
|
|
|
13
14
|
//#region src/agents/nodes/AgentNode.ts
|
|
15
|
+
/**
|
|
16
|
+
* The name of the agent node in the state graph.
|
|
17
|
+
*/
|
|
18
|
+
const AGENT_NODE_NAME = "model_request";
|
|
14
19
|
var AgentNode = class extends require_RunnableCallable.RunnableCallable {
|
|
15
20
|
#options;
|
|
21
|
+
#systemMessage;
|
|
22
|
+
#currentSystemMessage;
|
|
16
23
|
constructor(options) {
|
|
17
24
|
super({
|
|
18
25
|
name: options.name ?? "model",
|
|
19
26
|
func: (input, config) => this.#run(input, config)
|
|
20
27
|
});
|
|
21
28
|
this.#options = options;
|
|
29
|
+
this.#systemMessage = options.systemMessage;
|
|
22
30
|
}
|
|
23
31
|
/**
|
|
24
32
|
* Returns response format primtivies based on given model and response format provided by the user.
|
|
@@ -110,17 +118,15 @@ var AgentNode = class extends require_RunnableCallable.RunnableCallable {
|
|
|
110
118
|
require_utils.validateLLMHasNoBoundTools(request.model);
|
|
111
119
|
const structuredResponseFormat = this.#getResponseFormat(request.model);
|
|
112
120
|
const modelWithTools = await this.#bindTools(request.model, request, structuredResponseFormat);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
};
|
|
121
|
+
/**
|
|
122
|
+
* prepend the system message to the messages if it is not empty
|
|
123
|
+
*/
|
|
124
|
+
const messages = [...this.#currentSystemMessage.text === "" ? [] : [this.#currentSystemMessage], ...request.messages];
|
|
118
125
|
const signal = require_utils$1.mergeAbortSignals(this.#options.signal, config.signal);
|
|
119
|
-
const
|
|
126
|
+
const response = await (0, __langchain_core_runnables.raceWithSignal)(modelWithTools.invoke(messages, {
|
|
120
127
|
...config,
|
|
121
128
|
signal
|
|
122
|
-
};
|
|
123
|
-
const response = await modelWithTools.invoke(modelInput, invokeConfig);
|
|
129
|
+
}), signal);
|
|
124
130
|
/**
|
|
125
131
|
* if the user requests a native schema output, try to parse the response
|
|
126
132
|
* and return the structured response if it is valid
|
|
@@ -202,7 +208,36 @@ var AgentNode = class extends require_RunnableCallable.RunnableCallable {
|
|
|
202
208
|
*/
|
|
203
209
|
const invalidTools = modifiedTools.filter((tool) => require_utils.isClientTool(tool) && this.#options.toolClasses.every((t) => t !== tool));
|
|
204
210
|
if (invalidTools.length > 0) throw new Error(`You have modified a tool in "wrapModelCall" hook of middleware "${currentMiddleware.name}": ${invalidTools.map((tool) => tool.name).join(", ")}. This is not supported.`);
|
|
205
|
-
|
|
211
|
+
let normalizedReq = req;
|
|
212
|
+
const hasSystemPromptChanged = req.systemPrompt !== this.#currentSystemMessage.text;
|
|
213
|
+
const hasSystemMessageChanged = req.systemMessage !== this.#currentSystemMessage;
|
|
214
|
+
if (hasSystemPromptChanged && hasSystemMessageChanged) throw new Error("Cannot change both systemPrompt and systemMessage in the same request.");
|
|
215
|
+
/**
|
|
216
|
+
* Check if systemPrompt is a string was changed, if so create a new SystemMessage
|
|
217
|
+
*/
|
|
218
|
+
if (hasSystemPromptChanged) {
|
|
219
|
+
this.#currentSystemMessage = new __langchain_core_messages.SystemMessage({ content: [{
|
|
220
|
+
type: "text",
|
|
221
|
+
text: req.systemPrompt
|
|
222
|
+
}] });
|
|
223
|
+
normalizedReq = {
|
|
224
|
+
...req,
|
|
225
|
+
systemPrompt: this.#currentSystemMessage.text,
|
|
226
|
+
systemMessage: this.#currentSystemMessage
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* If the systemMessage was changed, update the current system message
|
|
231
|
+
*/
|
|
232
|
+
if (hasSystemMessageChanged) {
|
|
233
|
+
this.#currentSystemMessage = new __langchain_core_messages.SystemMessage({ ...req.systemMessage });
|
|
234
|
+
normalizedReq = {
|
|
235
|
+
...req,
|
|
236
|
+
systemPrompt: this.#currentSystemMessage.text,
|
|
237
|
+
systemMessage: this.#currentSystemMessage
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
return innerHandler(normalizedReq);
|
|
206
241
|
};
|
|
207
242
|
if (!currentMiddleware.wrapModelCall) return handlerWithValidation(requestWithStateAndRuntime);
|
|
208
243
|
try {
|
|
@@ -213,6 +248,9 @@ var AgentNode = class extends require_RunnableCallable.RunnableCallable {
|
|
|
213
248
|
if (!__langchain_core_messages.AIMessage.isInstance(middlewareResponse)) throw new Error(`Invalid response from "wrapModelCall" in middleware "${currentMiddleware.name}": expected AIMessage, got ${typeof middlewareResponse}`);
|
|
214
249
|
return middlewareResponse;
|
|
215
250
|
} catch (error) {
|
|
251
|
+
/**
|
|
252
|
+
* Add middleware context to error if not already added
|
|
253
|
+
*/
|
|
216
254
|
if (error instanceof Error && !error.message.includes(`middleware "${currentMiddleware.name}"`)) error.message = `Error in middleware "${currentMiddleware.name}": ${error.message}`;
|
|
217
255
|
throw error;
|
|
218
256
|
}
|
|
@@ -221,10 +259,14 @@ var AgentNode = class extends require_RunnableCallable.RunnableCallable {
|
|
|
221
259
|
}
|
|
222
260
|
/**
|
|
223
261
|
* Execute the wrapped handler with the initial request
|
|
262
|
+
* Reset current system prompt to initial state and convert to string using .text getter
|
|
263
|
+
* for backwards compatibility with ModelRequest
|
|
224
264
|
*/
|
|
265
|
+
this.#currentSystemMessage = this.#systemMessage;
|
|
225
266
|
const initialRequest = {
|
|
226
267
|
model,
|
|
227
|
-
systemPrompt: this.#
|
|
268
|
+
systemPrompt: this.#currentSystemMessage?.text,
|
|
269
|
+
systemMessage: this.#currentSystemMessage,
|
|
228
270
|
messages: state.messages,
|
|
229
271
|
tools: this.#options.toolClasses,
|
|
230
272
|
state,
|
|
@@ -284,14 +326,19 @@ var AgentNode = class extends require_RunnableCallable.RunnableCallable {
|
|
|
284
326
|
const toolCallId = toolCall.id;
|
|
285
327
|
if (!toolCallId) throw new Error("Tool call ID is required to handle tool output errors. Please provide a tool call ID.");
|
|
286
328
|
/**
|
|
329
|
+
* Default behavior: retry if `errorHandler` is undefined or truthy.
|
|
330
|
+
* Only throw if explicitly set to `false`.
|
|
331
|
+
*/
|
|
332
|
+
if (errorHandler === false) throw error;
|
|
333
|
+
/**
|
|
287
334
|
* retry if:
|
|
288
335
|
*/
|
|
289
|
-
if (typeof errorHandler === "boolean" && errorHandler || Array.isArray(errorHandler) && errorHandler.some((h) => h instanceof require_errors.MultipleStructuredOutputsError)) return new __langchain_langgraph.Command({
|
|
336
|
+
if (errorHandler === void 0 || typeof errorHandler === "boolean" && errorHandler || Array.isArray(errorHandler) && errorHandler.some((h) => h instanceof require_errors.MultipleStructuredOutputsError)) return new __langchain_langgraph.Command({
|
|
290
337
|
update: { messages: [response, new __langchain_core_messages.ToolMessage({
|
|
291
338
|
content: error.message,
|
|
292
339
|
tool_call_id: toolCallId
|
|
293
340
|
})] },
|
|
294
|
-
goto:
|
|
341
|
+
goto: AGENT_NODE_NAME
|
|
295
342
|
});
|
|
296
343
|
/**
|
|
297
344
|
* if `errorHandler` is a string, retry the tool call with given string
|
|
@@ -301,7 +348,7 @@ var AgentNode = class extends require_RunnableCallable.RunnableCallable {
|
|
|
301
348
|
content: errorHandler,
|
|
302
349
|
tool_call_id: toolCallId
|
|
303
350
|
})] },
|
|
304
|
-
goto:
|
|
351
|
+
goto: AGENT_NODE_NAME
|
|
305
352
|
});
|
|
306
353
|
/**
|
|
307
354
|
* if `errorHandler` is a function, retry the tool call with the function
|
|
@@ -314,30 +361,25 @@ var AgentNode = class extends require_RunnableCallable.RunnableCallable {
|
|
|
314
361
|
content,
|
|
315
362
|
tool_call_id: toolCallId
|
|
316
363
|
})] },
|
|
317
|
-
goto:
|
|
364
|
+
goto: AGENT_NODE_NAME
|
|
318
365
|
});
|
|
319
366
|
}
|
|
320
367
|
/**
|
|
321
|
-
*
|
|
368
|
+
* Default: retry if we reach here
|
|
322
369
|
*/
|
|
323
|
-
|
|
370
|
+
return new __langchain_langgraph.Command({
|
|
371
|
+
update: { messages: [response, new __langchain_core_messages.ToolMessage({
|
|
372
|
+
content: error.message,
|
|
373
|
+
tool_call_id: toolCallId
|
|
374
|
+
})] },
|
|
375
|
+
goto: AGENT_NODE_NAME
|
|
376
|
+
});
|
|
324
377
|
}
|
|
325
378
|
#areMoreStepsNeeded(state, response) {
|
|
326
379
|
const allToolsReturnDirect = __langchain_core_messages.AIMessage.isInstance(response) && response.tool_calls?.every((call) => this.#options.shouldReturnDirect.has(call.name));
|
|
327
380
|
const remainingSteps = "remainingSteps" in state ? state.remainingSteps : void 0;
|
|
328
381
|
return Boolean(remainingSteps && (remainingSteps < 1 && allToolsReturnDirect || remainingSteps < 2 && require_utils.hasToolCalls(state.messages.at(-1))));
|
|
329
382
|
}
|
|
330
|
-
#getModelInputState(state) {
|
|
331
|
-
const { messages, llmInputMessages,...rest } = state;
|
|
332
|
-
if (llmInputMessages && llmInputMessages.length > 0) return {
|
|
333
|
-
messages: llmInputMessages,
|
|
334
|
-
...rest
|
|
335
|
-
};
|
|
336
|
-
return {
|
|
337
|
-
messages,
|
|
338
|
-
...rest
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
383
|
async #bindTools(model, preparedOptions, structuredResponseFormat) {
|
|
342
384
|
const options = {};
|
|
343
385
|
const structuredTools = Object.values(structuredResponseFormat && "tools" in structuredResponseFormat ? structuredResponseFormat.tools : {});
|
|
@@ -382,8 +424,9 @@ var AgentNode = class extends require_RunnableCallable.RunnableCallable {
|
|
|
382
424
|
});
|
|
383
425
|
/**
|
|
384
426
|
* Create a model runnable with the prompt and agent name
|
|
427
|
+
* Use current SystemMessage state (which may have been modified by middleware)
|
|
385
428
|
*/
|
|
386
|
-
const modelRunnable =
|
|
429
|
+
const modelRunnable = this.#options.includeAgentName === "inline" ? require_withAgentName.withAgentName(modelWithTools, this.#options.includeAgentName) : modelWithTools;
|
|
387
430
|
return modelRunnable;
|
|
388
431
|
}
|
|
389
432
|
getState() {
|
|
@@ -397,5 +440,6 @@ var AgentNode = class extends require_RunnableCallable.RunnableCallable {
|
|
|
397
440
|
};
|
|
398
441
|
|
|
399
442
|
//#endregion
|
|
443
|
+
exports.AGENT_NODE_NAME = AGENT_NODE_NAME;
|
|
400
444
|
exports.AgentNode = AgentNode;
|
|
401
445
|
//# sourceMappingURL=AgentNode.cjs.map
|