UNPKG

langchain

Version:
765 lines (764 loc) 35.1 kB
require("../_virtual/_rolldown/runtime.cjs"); const require_annotation = require("./annotation.cjs"); const require_utils = require("./utils.cjs"); const require_utils$1 = require("./nodes/utils.cjs"); const require_AgentNode = require("./nodes/AgentNode.cjs"); const require_ToolNode = require("./nodes/ToolNode.cjs"); const require_utils$2 = require("./middleware/utils.cjs"); const require_BeforeAgentNode = require("./nodes/BeforeAgentNode.cjs"); const require_BeforeModelNode = require("./nodes/BeforeModelNode.cjs"); const require_AfterModelNode = require("./nodes/AfterModelNode.cjs"); const require_AfterAgentNode = require("./nodes/AfterAgentNode.cjs"); const require_tool_call = require("./transformers/tool-call.cjs"); const require_subagent = require("./transformers/subagent.cjs"); require("./transformers/index.cjs"); let _langchain_core_messages = require("@langchain/core/messages"); let _langchain_core_runnables = require("@langchain/core/runnables"); let _langchain_langgraph = require("@langchain/langgraph"); //#region src/agents/ReactAgent.ts /** * ReactAgent is a production-ready ReAct (Reasoning + Acting) agent that combines * language models with tools and middleware. * * The agent is parameterized by a single type bag `Types` that encapsulates all * type information: * * @typeParam Types - An {@link AgentTypeConfig} that bundles: * - `Response`: The structured response type * - `State`: The custom state schema type * - `Context`: The context schema type * - `Middleware`: The middleware array type * - `Tools`: The combined tools type from agent and middleware * * @example * ```typescript * // Using the type bag pattern * type MyTypes = AgentTypeConfig< * { name: string }, // Response * typeof myState, // State * typeof myContext, // Context * typeof middleware, // Middleware * typeof tools // Tools * >; * * const agent: ReactAgent<MyTypes> = createAgent({ ... }); * ``` */ var ReactAgent = class ReactAgent { #graph; #toolBehaviorVersion = "v2"; #agentNode; #defaultConfig; constructor(options, defaultConfig) { this.options = options; this.#defaultConfig = (0, _langchain_core_runnables.mergeConfigs)(defaultConfig ?? {}, { metadata: { ls_integration: "langchain_create_agent" }, configurable: { ls_agent_type: "root" } }); if (options.name) this.#defaultConfig = (0, _langchain_core_runnables.mergeConfigs)(this.#defaultConfig, { metadata: { lc_agent_name: options.name } }); this.#toolBehaviorVersion = options.version ?? this.#toolBehaviorVersion; /** * validate that model option is provided */ if (!options.model) throw new Error("`model` option is required to create an agent."); /** * Check if the LLM already has bound tools and throw if it does. */ if (typeof options.model !== "string") require_utils.validateLLMHasNoBoundTools(options.model); /** * define complete list of tools based on options and middleware */ const middlewareTools = this.options.middleware?.filter((m) => m.tools).flatMap((m) => m.tools) ?? []; const toolClasses = [...options.tools ?? [], ...middlewareTools]; /** * If any of the tools are configured to return_directly after running, * our graph needs to check if these were called */ const shouldReturnDirect = new Set(toolClasses.filter(require_utils.isClientTool).filter((tool) => "returnDirect" in tool && tool.returnDirect).map((tool) => tool.name)); /** * Create a schema that merges agent base schema with middleware state schemas * Using Zod with withLangGraph ensures LangGraph Studio gets proper metadata */ const hasDynamicStructuredResponse = Boolean(this.options.middleware?.some((middleware) => middleware.wrapModelCall)); const { state, input, output } = require_annotation.createAgentState(this.options.responseFormat !== void 0 || hasDynamicStructuredResponse, this.options.stateSchema, this.options.middleware); const allNodeWorkflows = new _langchain_langgraph.StateGraph(state, { input, output, context: this.options.contextSchema }); const beforeAgentNodes = []; const beforeModelNodes = []; const afterModelNodes = []; const afterAgentNodes = []; const wrapModelCallHookMiddleware = []; this.#agentNode = new require_AgentNode.AgentNode({ model: this.options.model, systemMessage: require_utils.normalizeSystemPrompt(this.options.systemPrompt), includeAgentName: this.options.includeAgentName, name: this.options.name, responseFormat: this.options.responseFormat, middleware: this.options.middleware, toolClasses, shouldReturnDirect, signal: this.options.signal, wrapModelCallHookMiddleware }); const middlewareNames = /* @__PURE__ */ new Set(); const middleware = this.options.middleware ?? []; for (let i = 0; i < middleware.length; i++) { let beforeAgentNode; let beforeModelNode; let afterModelNode; let afterAgentNode; const m = middleware[i]; if (middlewareNames.has(m.name)) throw new Error(`Middleware ${m.name} is defined multiple times`); middlewareNames.add(m.name); if (m.beforeAgent) { beforeAgentNode = new require_BeforeAgentNode.BeforeAgentNode(m); const name = `${m.name}.before_agent`; beforeAgentNodes.push({ index: i, name, allowed: require_utils$2.getHookConstraint(m.beforeAgent) }); allNodeWorkflows.addNode(name, beforeAgentNode, beforeAgentNode.nodeOptions); } if (m.beforeModel) { beforeModelNode = new require_BeforeModelNode.BeforeModelNode(m); const name = `${m.name}.before_model`; beforeModelNodes.push({ index: i, name, allowed: require_utils$2.getHookConstraint(m.beforeModel) }); allNodeWorkflows.addNode(name, beforeModelNode, beforeModelNode.nodeOptions); } if (m.afterModel) { afterModelNode = new require_AfterModelNode.AfterModelNode(m); const name = `${m.name}.after_model`; afterModelNodes.push({ index: i, name, allowed: require_utils$2.getHookConstraint(m.afterModel) }); allNodeWorkflows.addNode(name, afterModelNode, afterModelNode.nodeOptions); } if (m.afterAgent) { afterAgentNode = new require_AfterAgentNode.AfterAgentNode(m); const name = `${m.name}.after_agent`; afterAgentNodes.push({ index: i, name, allowed: require_utils$2.getHookConstraint(m.afterAgent) }); allNodeWorkflows.addNode(name, afterAgentNode, afterAgentNode.nodeOptions); } if (m.wrapModelCall) wrapModelCallHookMiddleware.push(m); } /** * Add Nodes */ allNodeWorkflows.addNode(require_AgentNode.AGENT_NODE_NAME, this.#agentNode); /** * Check if any middleware has wrapToolCall defined. * If so, we need to create a ToolNode even without pre-registered tools * to allow middleware to handle dynamically registered tools. */ const hasWrapToolCallMiddleware = middleware.some((m) => m.wrapToolCall); const clientTools = toolClasses.filter(require_utils.isClientTool); /** * Create ToolNode if we have client-side tools OR if middleware defines wrapToolCall * (which may handle dynamically registered tools) */ if (clientTools.length > 0 || hasWrapToolCallMiddleware) { const toolNode = new require_ToolNode.ToolNode(clientTools, { signal: this.options.signal, wrapToolCall: require_utils.wrapToolCall(middleware) }); allNodeWorkflows.addNode(require_ToolNode.TOOLS_NODE_NAME, toolNode); } /** * Add Edges */ let entryNode; if (beforeAgentNodes.length > 0) entryNode = beforeAgentNodes[0].name; else if (beforeModelNodes.length > 0) entryNode = beforeModelNodes[0].name; else entryNode = require_AgentNode.AGENT_NODE_NAME; const loopEntryNode = beforeModelNodes.length > 0 ? beforeModelNodes[0].name : require_AgentNode.AGENT_NODE_NAME; const exitNode = afterAgentNodes.length > 0 ? afterAgentNodes[afterAgentNodes.length - 1].name : _langchain_langgraph.END; allNodeWorkflows.addEdge(_langchain_langgraph.START, entryNode); /** * Determine if we have tools available for routing. * This includes both registered client tools AND dynamic tools via middleware. */ const hasToolsAvailable = clientTools.length > 0 || hasWrapToolCallMiddleware; for (let i = 0; i < beforeAgentNodes.length; i++) { const node = beforeAgentNodes[i]; const current = node.name; const nextDefault = i === beforeAgentNodes.length - 1 ? loopEntryNode : beforeAgentNodes[i + 1].name; if (node.allowed && node.allowed.length > 0) { const allowedMapped = node.allowed.map((t) => require_utils$1.parseJumpToTarget(t)).filter((dest) => dest !== "tools" || hasToolsAvailable); const destinations = Array.from(new Set([nextDefault, ...allowedMapped.map((dest) => dest === _langchain_langgraph.END ? exitNode : dest)])); allNodeWorkflows.addConditionalEdges(current, this.#createBeforeAgentRouter(clientTools, nextDefault, exitNode, hasToolsAvailable), destinations); } else allNodeWorkflows.addEdge(current, nextDefault); } for (let i = 0; i < beforeModelNodes.length; i++) { const node = beforeModelNodes[i]; const current = node.name; const nextDefault = i === beforeModelNodes.length - 1 ? require_AgentNode.AGENT_NODE_NAME : beforeModelNodes[i + 1].name; if (node.allowed && node.allowed.length > 0) { const allowedMapped = node.allowed.map((t) => require_utils$1.parseJumpToTarget(t)).filter((dest) => dest !== "tools" || hasToolsAvailable); const destinations = Array.from(new Set([nextDefault, ...allowedMapped])); allNodeWorkflows.addConditionalEdges(current, this.#createBeforeModelRouter(clientTools, nextDefault, hasToolsAvailable), destinations); } else allNodeWorkflows.addEdge(current, nextDefault); } const lastAfterModelNode = afterModelNodes.at(-1); if (afterModelNodes.length > 0 && lastAfterModelNode) allNodeWorkflows.addEdge(require_AgentNode.AGENT_NODE_NAME, lastAfterModelNode.name); else { const destinations = this.#getModelPaths(clientTools, false, hasToolsAvailable).map((p) => p === _langchain_langgraph.END ? exitNode : p); if (destinations.length === 1) allNodeWorkflows.addEdge(require_AgentNode.AGENT_NODE_NAME, destinations[0]); else allNodeWorkflows.addConditionalEdges(require_AgentNode.AGENT_NODE_NAME, this.#createModelRouter(exitNode), destinations); } for (let i = afterModelNodes.length - 1; i > 0; i--) { const node = afterModelNodes[i]; const current = node.name; const nextDefault = afterModelNodes[i - 1].name; if (node.allowed && node.allowed.length > 0) { const allowedMapped = node.allowed.map((t) => require_utils$1.parseJumpToTarget(t)).filter((dest) => dest !== "tools" || hasToolsAvailable); const destinations = Array.from(new Set([nextDefault, ...allowedMapped])); allNodeWorkflows.addConditionalEdges(current, this.#createAfterModelSequenceRouter(clientTools, node.allowed, nextDefault, hasToolsAvailable), destinations); } else allNodeWorkflows.addEdge(current, nextDefault); } if (afterModelNodes.length > 0) { const firstAfterModel = afterModelNodes[0]; const firstAfterModelNode = firstAfterModel.name; const modelPaths = this.#getModelPaths(clientTools, true, hasToolsAvailable).filter((p) => p !== "tools" || hasToolsAvailable); const allowJump = Boolean(firstAfterModel.allowed && firstAfterModel.allowed.length > 0); const destinations = modelPaths.map((p) => p === _langchain_langgraph.END ? exitNode : p); allNodeWorkflows.addConditionalEdges(firstAfterModelNode, this.#createAfterModelRouter(clientTools, allowJump, exitNode, hasToolsAvailable), destinations); } for (let i = afterAgentNodes.length - 1; i > 0; i--) { const node = afterAgentNodes[i]; const current = node.name; const nextDefault = afterAgentNodes[i - 1].name; if (node.allowed && node.allowed.length > 0) { const allowedMapped = node.allowed.map((t) => require_utils$1.parseJumpToTarget(t)).filter((dest) => dest !== "tools" || hasToolsAvailable); const destinations = Array.from(new Set([nextDefault, ...allowedMapped])); allNodeWorkflows.addConditionalEdges(current, this.#createAfterModelSequenceRouter(clientTools, node.allowed, nextDefault, hasToolsAvailable), destinations); } else allNodeWorkflows.addEdge(current, nextDefault); } if (afterAgentNodes.length > 0) { const firstAfterAgent = afterAgentNodes[0]; const firstAfterAgentNode = firstAfterAgent.name; if (firstAfterAgent.allowed && firstAfterAgent.allowed.length > 0) { const allowedMapped = firstAfterAgent.allowed.map((t) => require_utils$1.parseJumpToTarget(t)).filter((dest) => dest !== "tools" || hasToolsAvailable); /** * For after_agent, only use explicitly allowed destinations (don't add loopEntryNode) * The default destination (when no jump occurs) should be END */ const destinations = Array.from(new Set([_langchain_langgraph.END, ...allowedMapped])); allNodeWorkflows.addConditionalEdges(firstAfterAgentNode, this.#createAfterModelSequenceRouter(clientTools, firstAfterAgent.allowed, _langchain_langgraph.END, hasToolsAvailable), destinations); } else allNodeWorkflows.addEdge(firstAfterAgentNode, _langchain_langgraph.END); } /** * add edges for tools node (includes both registered tools and dynamic tools via middleware) */ if (hasToolsAvailable) { const toolReturnTarget = loopEntryNode; if (shouldReturnDirect.size > 0) allNodeWorkflows.addConditionalEdges(require_ToolNode.TOOLS_NODE_NAME, this.#createToolsRouter(shouldReturnDirect, exitNode, toolReturnTarget), [toolReturnTarget, exitNode]); else allNodeWorkflows.addEdge(require_ToolNode.TOOLS_NODE_NAME, toolReturnTarget); } /** * compile the graph with native + user-defined stream transformers */ const middlewareStreamTransformers = (this.options.middleware ?? []).flatMap((m) => m.streamTransformers ?? []); const compileTransformers = [ require_tool_call.createToolCallTransformer([]), require_subagent.createSubagentTransformer([]), ...middlewareStreamTransformers, ...this.options.streamTransformers ?? [] ]; this.#graph = allNodeWorkflows.compile({ checkpointer: this.options.checkpointer, store: this.options.store, name: this.options.name, description: this.options.description, transformers: compileTransformers }); /** * LangGraph API resolves exported agents by unwrapping ReactAgent to the * inner compiled graph (see langgraph-api load.utils `afterResolve`) and * calls streamEvents on that pregel directly. That path only sees config * baked into the graph via `.withConfig()`, not ReactAgent's #defaultConfig * merged at invoke/stream time — so propagate static defaults here. */ const graphDefaultConfig = require_utils.toGraphDefaultConfig(this.#defaultConfig); if (Object.keys(graphDefaultConfig).length > 0) this.#graph = this.#graph.withConfig(graphDefaultConfig); } /** * Get the compiled {@link https://docs.langchain.com/oss/javascript/langgraph/use-graph-api | StateGraph}. */ get graph() { return this.#graph; } get checkpointer() { return this.#graph.checkpointer; } set checkpointer(value) { this.#graph.checkpointer = value; } get store() { return this.#graph.store; } set store(value) { this.#graph.store = value; } /** * Creates a new ReactAgent with the given config merged into the existing config. * Follows the same pattern as LangGraph's Pregel.withConfig(). * * The merged config is applied as a default that gets merged with any config * passed at invocation time (invoke/stream). Invocation-time config takes precedence. * * @param config - Configuration to merge with existing config * @returns A new ReactAgent instance with the merged configuration * * @example * ```typescript * const agent = createAgent({ model: "gpt-4o", tools: [...] }); * * // Set a default recursion limit * const configuredAgent = agent.withConfig({ recursionLimit: 1000 }); * * // Chain multiple configs * const debugAgent = agent * .withConfig({ recursionLimit: 1000 }) * .withConfig({ tags: ["debug"] }); * ``` */ withConfig(config) { return new ReactAgent(this.options, (0, _langchain_core_runnables.mergeConfigs)(this.#defaultConfig, config)); } /** * Get possible edge destinations from model node. * @param toolClasses names of tools to call * @param includeModelRequest whether to include "model_request" as a valid path (for jumpTo routing) * @param hasToolsAvailable whether tools are available (includes dynamic tools via middleware) * @returns list of possible edge destinations */ #getModelPaths(toolClasses, includeModelRequest = false, hasToolsAvailable = toolClasses.length > 0) { const paths = []; if (hasToolsAvailable) paths.push(require_ToolNode.TOOLS_NODE_NAME); if (includeModelRequest) paths.push(require_AgentNode.AGENT_NODE_NAME); paths.push(_langchain_langgraph.END); return paths; } /** * Create routing function for tools node conditional edges. */ #createToolsRouter(shouldReturnDirect, exitNode, toolReturnTarget) { return (state) => { const messages = state.messages; const lastMessage = messages[messages.length - 1]; if (_langchain_core_messages.ToolMessage.isInstance(lastMessage) && lastMessage.name && shouldReturnDirect.has(lastMessage.name)) return this.options.responseFormat ? toolReturnTarget : exitNode; return toolReturnTarget; }; } /** * Create routing function for model node conditional edges. * @param exitNode - The exit node to route to (could be after_agent or END) */ #createModelRouter(exitNode = _langchain_langgraph.END) { /** * determine if the agent should continue or not */ return (state) => { const lastMessage = state.messages.at(-1); if (!_langchain_core_messages.AIMessage.isInstance(lastMessage) || !lastMessage.tool_calls || lastMessage.tool_calls.length === 0) return exitNode; if (lastMessage.tool_calls.every((toolCall) => toolCall.name.startsWith("extract-"))) return exitNode; /** * The tool node processes a single message. */ if (this.#toolBehaviorVersion === "v1") return require_ToolNode.TOOLS_NODE_NAME; /** * Route to tools node (filter out any structured response tool calls) */ const regularToolCalls = lastMessage.tool_calls.filter((toolCall) => !toolCall.name.startsWith("extract-")); if (regularToolCalls.length === 0) return exitNode; return regularToolCalls.map((toolCall) => new _langchain_langgraph.Send(require_ToolNode.TOOLS_NODE_NAME, { ...state, lg_tool_call: toolCall })); }; } /** * Create routing function for jumpTo functionality after afterModel hooks. * * This router checks if the `jumpTo` property is set in the state after afterModel middleware * execution. If set, it routes to the specified target ("model_request" or "tools"). * If not set, it falls back to the normal model routing logic for afterModel context. * * The jumpTo property is automatically cleared after use to prevent infinite loops. * * @param toolClasses - Available tool classes for validation * @param allowJump - Whether jumping is allowed * @param exitNode - The exit node to route to (could be after_agent or END) * @param hasToolsAvailable - Whether tools are available (includes dynamic tools via middleware) * @returns Router function that handles jumpTo logic and normal routing */ #createAfterModelRouter(toolClasses, allowJump, exitNode, hasToolsAvailable = toolClasses.length > 0) { const hasStructuredResponse = Boolean(this.options.responseFormat); return (state) => { const builtInState = state; const messages = builtInState.messages; const lastMessage = messages.at(-1); if (_langchain_core_messages.AIMessage.isInstance(lastMessage) && (!lastMessage.tool_calls || lastMessage.tool_calls.length === 0)) return exitNode; if (allowJump && builtInState.jumpTo) { const destination = require_utils$1.parseJumpToTarget(builtInState.jumpTo); if (destination === _langchain_langgraph.END) return exitNode; if (destination === "tools") { if (!hasToolsAvailable) return exitNode; return new _langchain_langgraph.Send(require_ToolNode.TOOLS_NODE_NAME, { ...state, jumpTo: void 0 }); } return new _langchain_langgraph.Send(require_AgentNode.AGENT_NODE_NAME, { ...state, jumpTo: void 0 }); } const toolMessages = messages.filter(_langchain_core_messages.ToolMessage.isInstance); const lastAiMessage = messages.filter(_langchain_core_messages.AIMessage.isInstance).at(-1); const pendingToolCalls = lastAiMessage?.tool_calls?.filter((call) => !toolMessages.some((m) => m.tool_call_id === call.id)); if (pendingToolCalls && pendingToolCalls.length > 0) { /** * v1: route the full message to the ToolNode; it filters already-processed * calls internally and runs the remaining ones via Promise.all. * v2: dispatch each pending call as a separate Send task. */ if (this.#toolBehaviorVersion === "v1") return require_ToolNode.TOOLS_NODE_NAME; return pendingToolCalls.map((toolCall) => new _langchain_langgraph.Send(require_ToolNode.TOOLS_NODE_NAME, { ...state, lg_tool_call: toolCall })); } const hasStructuredResponseCalls = lastAiMessage?.tool_calls?.some((toolCall) => toolCall.name.startsWith("extract-")); if (pendingToolCalls && pendingToolCalls.length === 0 && !hasStructuredResponseCalls && hasStructuredResponse) return require_AgentNode.AGENT_NODE_NAME; if (!_langchain_core_messages.AIMessage.isInstance(lastMessage) || !lastMessage.tool_calls || lastMessage.tool_calls.length === 0) return exitNode; const hasOnlyStructuredResponseCalls = lastMessage.tool_calls.every((toolCall) => toolCall.name.startsWith("extract-")); const hasRegularToolCalls = lastMessage.tool_calls.some((toolCall) => !toolCall.name.startsWith("extract-")); if (hasOnlyStructuredResponseCalls || !hasRegularToolCalls) return exitNode; /** * v1: route the full AIMessage to a single ToolNode invocation so all * tool calls run concurrently via Promise.all. * * v2: dispatch each regular tool call as a separate Send task, matching * the behaviour of #createModelRouter when no afterModel middleware is * present. */ if (this.#toolBehaviorVersion === "v1") return require_ToolNode.TOOLS_NODE_NAME; const regularToolCalls = lastMessage.tool_calls.filter((toolCall) => !toolCall.name.startsWith("extract-")); if (regularToolCalls.length === 0) return exitNode; return regularToolCalls.map((toolCall) => new _langchain_langgraph.Send(require_ToolNode.TOOLS_NODE_NAME, { ...state, lg_tool_call: toolCall })); }; } /** * Router for afterModel sequence nodes (connecting later middlewares to earlier ones), * honoring allowed jump targets and defaulting to the next node. * @param toolClasses - Available tool classes for validation * @param allowed - List of allowed jump targets * @param nextDefault - Default node to route to * @param hasToolsAvailable - Whether tools are available (includes dynamic tools via middleware) */ #createAfterModelSequenceRouter(toolClasses, allowed, nextDefault, hasToolsAvailable = toolClasses.length > 0) { const allowedSet = new Set(allowed.map((t) => require_utils$1.parseJumpToTarget(t))); return (state) => { const builtInState = state; if (builtInState.jumpTo) { const dest = require_utils$1.parseJumpToTarget(builtInState.jumpTo); if (dest === _langchain_langgraph.END && allowedSet.has(_langchain_langgraph.END)) return _langchain_langgraph.END; if (dest === "tools" && allowedSet.has("tools")) { if (!hasToolsAvailable) return _langchain_langgraph.END; return new _langchain_langgraph.Send(require_ToolNode.TOOLS_NODE_NAME, { ...state, jumpTo: void 0 }); } if (dest === "model_request" && allowedSet.has("model_request")) return new _langchain_langgraph.Send(require_AgentNode.AGENT_NODE_NAME, { ...state, jumpTo: void 0 }); } return nextDefault; }; } /** * Create routing function for jumpTo functionality after beforeAgent hooks. * Falls back to the default next node if no jumpTo is present. * When jumping to END, routes to exitNode (which could be an afterAgent node). * @param toolClasses - Available tool classes for validation * @param nextDefault - Default node to route to * @param exitNode - Exit node to route to (could be after_agent or END) * @param hasToolsAvailable - Whether tools are available (includes dynamic tools via middleware) */ #createBeforeAgentRouter(toolClasses, nextDefault, exitNode, hasToolsAvailable = toolClasses.length > 0) { return (state) => { const builtInState = state; if (!builtInState.jumpTo) return nextDefault; const destination = require_utils$1.parseJumpToTarget(builtInState.jumpTo); if (destination === _langchain_langgraph.END) /** * When beforeAgent jumps to END, route to exitNode (first afterAgent node) */ return exitNode; if (destination === "tools") { if (!hasToolsAvailable) return exitNode; return new _langchain_langgraph.Send(require_ToolNode.TOOLS_NODE_NAME, { ...state, jumpTo: void 0 }); } return new _langchain_langgraph.Send(require_AgentNode.AGENT_NODE_NAME, { ...state, jumpTo: void 0 }); }; } /** * Create routing function for jumpTo functionality after beforeModel hooks. * Falls back to the default next node if no jumpTo is present. * @param toolClasses - Available tool classes for validation * @param nextDefault - Default node to route to * @param hasToolsAvailable - Whether tools are available (includes dynamic tools via middleware) */ #createBeforeModelRouter(toolClasses, nextDefault, hasToolsAvailable = toolClasses.length > 0) { return (state) => { const builtInState = state; if (!builtInState.jumpTo) return nextDefault; const destination = require_utils$1.parseJumpToTarget(builtInState.jumpTo); if (destination === _langchain_langgraph.END) return _langchain_langgraph.END; if (destination === "tools") { if (!hasToolsAvailable) return _langchain_langgraph.END; return new _langchain_langgraph.Send(require_ToolNode.TOOLS_NODE_NAME, { ...state, jumpTo: void 0 }); } return new _langchain_langgraph.Send(require_AgentNode.AGENT_NODE_NAME, { ...state, jumpTo: void 0 }); }; } /** * Initialize middleware states if not already present in the input state. */ async #initializeMiddlewareStates(state, config) { if (!this.options.middleware || this.options.middleware.length === 0 || state instanceof _langchain_langgraph.Command || !state) return state; const defaultStates = await require_utils$1.initializeMiddlewareStates(this.options.middleware, state); const updatedState = { ...(await this.#graph.getState(config).catch(() => ({ values: {} }))).values, ...state }; if (!updatedState) return updatedState; for (const [key, value] of Object.entries(defaultStates)) if (!(key in updatedState)) updatedState[key] = value; return updatedState; } /** * Executes the agent with the given state and returns the final state after all processing. * * This method runs the agent's entire workflow synchronously, including: * - Processing the input messages through any configured middleware * - Calling the language model to generate responses * - Executing any tool calls made by the model * - Running all middleware hooks (beforeModel, afterModel, etc.) * * @param state - The initial state for the agent execution. Can be: * - An object containing `messages` array and any middleware-specific state properties * - A Command object for more advanced control flow * * @param config - Optional runtime configuration including: * @param config.context - The context for the agent execution. * @param config.configurable - LangGraph configuration options like `thread_id`, `run_id`, etc. * @param config.store - The store for the agent execution for persisting state, see more in {@link https://docs.langchain.com/oss/javascript/langgraph/memory#memory-storage | Memory storage}. * @param config.signal - An optional {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal | `AbortSignal`} for the agent execution. * @param config.recursionLimit - The recursion limit for the agent execution. * * @returns A Promise that resolves to the final agent state after execution completes. * The returned state includes: * - a `messages` property containing an array with all messages (input, AI responses, tool calls/results) * - a `structuredResponse` property containing the structured response (if configured) * - all state values defined in the middleware * * @example * ```typescript * const agent = new ReactAgent({ * llm: myModel, * tools: [calculator, webSearch], * responseFormat: z.object({ * weather: z.string(), * }), * }); * * const result = await agent.invoke({ * messages: [{ role: "human", content: "What's the weather in Paris?" }] * }); * * console.log(result.structuredResponse.weather); // outputs: "It's sunny and 75°F." * ``` */ async invoke(state, config) { const mergedConfig = (0, _langchain_core_runnables.mergeConfigs)(this.#defaultConfig, config); const initializedState = await this.#initializeMiddlewareStates(state, mergedConfig); return this.#graph.invoke(initializedState, mergedConfig); } /** * Executes the agent with streaming, returning an async iterable of state updates as they occur. * * This method runs the agent's workflow similar to `invoke`, but instead of waiting for * completion, it streams high-level state updates in real-time. This allows you to: * - Display intermediate results to users as they're generated * - Monitor the agent's progress through each step * - React to state changes as nodes complete * * For more granular event-level streaming (like individual LLM tokens), use `streamEvents` instead. * * @param state - The initial state for the agent execution. Can be: * - An object containing `messages` array and any middleware-specific state properties * - A Command object for more advanced control flow * * @param config - Optional runtime configuration including: * @param config.context - The context for the agent execution. * @param config.configurable - LangGraph configuration options like `thread_id`, `run_id`, etc. * @param config.store - The store for the agent execution for persisting state, see more in {@link https://docs.langchain.com/oss/javascript/langgraph/memory#memory-storage | Memory storage}. * @param config.signal - An optional {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal | `AbortSignal`} for the agent execution. * @param config.streamMode - The streaming mode for the agent execution, see more in {@link https://docs.langchain.com/oss/javascript/langgraph/streaming#supported-stream-modes | Supported stream modes}. * @param config.recursionLimit - The recursion limit for the agent execution. * * @returns A Promise that resolves to an IterableReadableStream of state updates. * Each update contains the current state after a node completes. * * @example * ```typescript * const agent = new ReactAgent({ * llm: myModel, * tools: [calculator, webSearch] * }); * * const stream = await agent.stream({ * messages: [{ role: "human", content: "What's 2+2 and the weather in NYC?" }] * }); * * for await (const chunk of stream) { * console.log(chunk); // State update from each node * } * ``` */ async stream(state, config) { const mergedConfig = (0, _langchain_core_runnables.mergeConfigs)(this.#defaultConfig, config); const initializedState = await this.#initializeMiddlewareStates(state, mergedConfig); return this.#graph.stream(initializedState, mergedConfig); } streamEvents(state, config, streamOptions) { if (config?.version !== "v3" || streamOptions != null) { const mergedConfig = (0, _langchain_core_runnables.mergeConfigs)(this.#defaultConfig, config); const version = config?.version === "v1" || config?.version === "v2" ? config.version : "v2"; return this.#graph.streamEvents(state, { ...mergedConfig, version }, streamOptions); } return (async () => { const { transformers: callSiteTransformers, version: _version, ...restConfig } = config ?? {}; const mergedConfig = (0, _langchain_core_runnables.mergeConfigs)(this.#defaultConfig, restConfig); const initializedState = await this.#initializeMiddlewareStates(state, mergedConfig); return await this.#graph.streamEvents(initializedState, { ...mergedConfig, version: "v3", transformers: callSiteTransformers }); })(); } /** * Visualize the graph as a PNG image. * @param params - Parameters for the drawMermaidPng method. * @param params.withStyles - Whether to include styles in the graph. * @param params.curveStyle - The style of the graph's curves. * @param params.nodeColors - The colors of the graph's nodes. * @param params.wrapLabelNWords - The maximum number of words to wrap in a node's label. * @param params.backgroundColor - The background color of the graph. * @returns PNG image as a buffer */ async drawMermaidPng(params) { const arrayBuffer = await (await (await this.#graph.getGraphAsync()).drawMermaidPng(params)).arrayBuffer(); return new Uint8Array(arrayBuffer); } /** * Draw the graph as a Mermaid string. * @param params - Parameters for the drawMermaid method. * @param params.withStyles - Whether to include styles in the graph. * @param params.curveStyle - The style of the graph's curves. * @param params.nodeColors - The colors of the graph's nodes. * @param params.wrapLabelNWords - The maximum number of words to wrap in a node's label. * @param params.backgroundColor - The background color of the graph. * @returns Mermaid string */ async drawMermaid(params) { return (await this.#graph.getGraphAsync()).drawMermaid(params); } /** * The following are internal methods to enable support for LangGraph Platform. * They are not part of the createAgent public API. * * Note: we intentionally return as `never` to avoid type errors due to type inference. */ /** * @internal */ getGraphAsync(config) { return this.#graph.getGraphAsync(config); } /** * @internal */ getState(config, options) { return this.#graph.getState(config, options); } /** * @internal */ getStateHistory(config, options) { return this.#graph.getStateHistory(config, options); } /** * @internal */ getSubgraphs(namespace, recurse) { return this.#graph.getSubgraphs(namespace, recurse); } /** * @internal */ getSubgraphsAsync(namespace, recurse) { return this.#graph.getSubgraphsAsync(namespace, recurse); } /** * @internal */ updateState(inputConfig, values, asNode) { return this.#graph.updateState(inputConfig, values, asNode); } /** * @internal */ get builder() { return this.#graph.builder; } }; //#endregion exports.ReactAgent = ReactAgent; //# sourceMappingURL=ReactAgent.cjs.map