langchain 1.5.1-dev-1781824996270 → 1.5.2
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 +18 -0
- 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/nodes/AgentNode.cjs +6 -1
- package/dist/agents/nodes/AgentNode.cjs.map +1 -1
- package/dist/agents/nodes/AgentNode.js +6 -1
- package/dist/agents/nodes/AgentNode.js.map +1 -1
- package/dist/agents/transformers/tool-call.cjs +21 -10
- package/dist/agents/transformers/tool-call.cjs.map +1 -1
- package/dist/agents/transformers/tool-call.d.cts.map +1 -1
- package/dist/agents/transformers/tool-call.d.ts.map +1 -1
- package/dist/agents/transformers/tool-call.js +21 -10
- package/dist/agents/transformers/tool-call.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 +7 -7
- 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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# langchain
|
|
2
2
|
|
|
3
|
+
## 1.5.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#11100](https://github.com/langchain-ai/langchainjs/pull/11100) [`3205b35`](https://github.com/langchain-ai/langchainjs/commit/3205b35ac83037a2fff2998f16a66b5126b306f8) Thanks [@colifran](https://github.com/colifran)! - fix(langchain, openai): decouple strict tools from strict structured output response
|
|
8
|
+
|
|
9
|
+
## 1.5.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#11087](https://github.com/langchain-ai/langchainjs/pull/11087) [`534b43a`](https://github.com/langchain-ai/langchainjs/commit/534b43a0719c89e3fb4ab7052d3f39797660aa43) Thanks [@christian-bromann](https://github.com/christian-bromann)! - fix(langchain): keep tool call streams pending across any tool interrupt
|
|
14
|
+
|
|
15
|
+
A raw `interrupt()` raised from inside a tool surfaced as a `tool-error` and
|
|
16
|
+
rejected the call's un-awaited `output` promise, producing an unhandled
|
|
17
|
+
rejection that crashed HITL runs. The tool-call stream transformer now treats
|
|
18
|
+
any serialized graph interrupt as control flow (the call stays pending and
|
|
19
|
+
resumes), not just `humanInTheLoopMiddleware` interrupts.
|
|
20
|
+
|
|
3
21
|
## 1.5.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("../dist/chat_models/universal.cjs");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/chat_models/universal.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/chat_models/universal.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/chat_models/universal.js";
|
|
@@ -499,7 +499,12 @@ var AgentNode = class extends require_RunnableCallable.RunnableCallable {
|
|
|
499
499
|
kwargs: { method: "json_schema" },
|
|
500
500
|
schema: structuredResponseFormat.strategy.schema
|
|
501
501
|
},
|
|
502
|
-
|
|
502
|
+
/**
|
|
503
|
+
* Don't force strict on tools: it makes Anthropic's combined grammar
|
|
504
|
+
* "too complex for compilation", and only OpenAI Chat Completions needs
|
|
505
|
+
* it (re-applied there). Honor an explicit override; else leave unset.
|
|
506
|
+
*/
|
|
507
|
+
strict: preparedOptions?.modelSettings?.strict
|
|
503
508
|
});
|
|
504
509
|
}
|
|
505
510
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentNode.cjs","names":["AIMessage","RunnableCallable","#run","#options","#systemMessage","#getResponseFormat","isConfigurableModel","transformResponseFormat","ProviderStrategy","ToolStrategy","ToolMessage","Command","#invokeModel","#areMoreStepsNeeded","initChatModel","#deriveModel","#bindTools","mergeAbortSignals","StructuredOutputParsingError","#handleMultipleStructuredOutputs","#handleSingleStructuredOutput","toPartialZodObject","isClientTool","SystemMessage","MiddlewareError","MultipleStructuredOutputsError","#handleToolStrategyError","hasToolCalls","bindTools","withAgentName"],"sources":["../../../src/agents/nodes/AgentNode.ts"],"sourcesContent":["/* oxlint-disable no-instanceof/no-instanceof */\nimport { Runnable, RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseMessage,\n AIMessage,\n ToolMessage,\n SystemMessage,\n} from \"@langchain/core/messages\";\nimport {\n Command,\n isCommand,\n type LangGraphRunnableConfig,\n} from \"@langchain/langgraph\";\nimport { type BaseChatModelCallOptions } from \"@langchain/core/language_models/chat_models\";\nimport {\n InteropZodObject,\n getSchemaDescription,\n interopParse,\n} from \"@langchain/core/utils/types\";\nimport { raceWithSignal } from \"@langchain/core/runnables\";\nimport type { ToolCall } from \"@langchain/core/messages/tool\";\nimport type { ClientTool, ServerTool } from \"@langchain/core/tools\";\n\nimport { initChatModel } from \"../../chat_models/universal.js\";\nimport {\n MultipleStructuredOutputsError,\n MiddlewareError,\n StructuredOutputParsingError,\n} from \"../errors.js\";\nimport { RunnableCallable } from \"../RunnableCallable.js\";\nimport type { AgentLanguageModelLike as LanguageModelLike } from \"../model.js\";\nimport {\n bindTools,\n validateLLMHasNoBoundTools,\n hasToolCalls,\n isClientTool,\n} from \"../utils.js\";\nimport { isConfigurableModel } from \"../model.js\";\nimport { mergeAbortSignals, toPartialZodObject } from \"../nodes/utils.js\";\nimport { CreateAgentParams } from \"../types.js\";\nimport type { InternalAgentState, Runtime } from \"../runtime.js\";\nimport type {\n AgentMiddleware,\n AnyAnnotationRoot,\n WrapModelCallHandler,\n} from \"../middleware/types.js\";\nimport type { ModelRequest } from \"./types.js\";\nimport { withAgentName } from \"../withAgentName.js\";\nimport {\n ToolStrategy,\n ProviderStrategy,\n transformResponseFormat,\n ToolStrategyError,\n type ResponseFormatInput,\n} from \"../responses.js\";\n\ntype ResponseHandlerResult<StructuredResponseFormat> =\n | {\n structuredResponse: StructuredResponseFormat;\n messages: BaseMessage[];\n }\n | Promise<Command>;\n\n/**\n * Wrap the base handler with middleware wrapModelCall hooks\n * Middleware are composed so the first middleware is the outermost wrapper\n * Example: [auth, retry, cache] means auth wraps retry wraps cache wraps baseHandler\n */\ntype InternalModelResponse<StructuredResponseFormat> =\n | AIMessage\n | ResponseHandlerResult<StructuredResponseFormat>\n | Command;\n\n/**\n * Check if the response is an internal model response.\n * @param response - The response to check.\n * @returns True if the response is an internal model response, false otherwise.\n */\nfunction isInternalModelResponse<StructuredResponseFormat>(\n response: unknown\n): response is InternalModelResponse<StructuredResponseFormat> {\n return (\n AIMessage.isInstance(response) ||\n isCommand(response) ||\n (typeof response === \"object\" &&\n response !== null &&\n \"structuredResponse\" in response &&\n \"messages\" in response)\n );\n}\n\n/**\n * The name of the agent node in the state graph.\n */\nexport const AGENT_NODE_NAME = \"model_request\";\n\nexport interface AgentNodeOptions<\n StructuredResponseFormat extends Record<string, unknown> = Record<\n string,\n unknown\n >,\n StateSchema extends AnyAnnotationRoot | InteropZodObject = AnyAnnotationRoot,\n ContextSchema extends AnyAnnotationRoot | InteropZodObject =\n AnyAnnotationRoot,\n> extends Pick<\n CreateAgentParams<StructuredResponseFormat, StateSchema, ContextSchema>,\n \"model\" | \"includeAgentName\" | \"name\" | \"responseFormat\" | \"middleware\"\n> {\n toolClasses: (ClientTool | ServerTool)[];\n shouldReturnDirect: Set<string>;\n signal?: AbortSignal;\n systemMessage: SystemMessage;\n wrapModelCallHookMiddleware?: (\n | AgentMiddleware\n | [AgentMiddleware, (...args: unknown[]) => Record<string, unknown>]\n )[];\n}\n\ninterface NativeResponseFormat {\n type: \"native\";\n strategy: ProviderStrategy;\n}\n\ninterface ToolResponseFormat {\n type: \"tool\";\n tools: Record<string, ToolStrategy>;\n}\n\ntype ResponseFormat = NativeResponseFormat | ToolResponseFormat;\n\nexport class AgentNode<\n StructuredResponseFormat extends Record<string, unknown> = Record<\n string,\n unknown\n >,\n ContextSchema extends AnyAnnotationRoot | InteropZodObject =\n AnyAnnotationRoot,\n> extends RunnableCallable<\n InternalAgentState<StructuredResponseFormat>,\n | Command[]\n | {\n messages: BaseMessage[];\n structuredResponse: StructuredResponseFormat;\n }\n> {\n #options: AgentNodeOptions<StructuredResponseFormat, ContextSchema>;\n #systemMessage: SystemMessage;\n\n constructor(\n options: AgentNodeOptions<StructuredResponseFormat, ContextSchema>\n ) {\n super({\n name: options.name ?? \"model\",\n func: (input, config) => this.#run(input, config as RunnableConfig),\n });\n\n this.#options = options;\n this.#systemMessage = options.systemMessage;\n }\n\n /**\n * Returns response format primtivies based on given model and response format provided by the user.\n *\n * If the user selects a tool output:\n * - return a record of tools to extract structured output from the model's response\n *\n * if the user selects a native schema output or if the model supports JSON schema output:\n * - return a provider strategy to extract structured output from the model's response\n *\n * @param model - The model to get the response format for.\n * @returns The response format.\n */\n async #getResponseFormat(\n model: string | LanguageModelLike,\n responseFormat: ResponseFormatInput | undefined = this.#options\n .responseFormat\n ): Promise<ResponseFormat | undefined> {\n if (!responseFormat) {\n return undefined;\n }\n\n let resolvedModel: LanguageModelLike | undefined;\n if (isConfigurableModel(model)) {\n resolvedModel = await (\n model as unknown as {\n _getModelInstance: () => Promise<LanguageModelLike>;\n }\n )._getModelInstance();\n } else if (typeof model !== \"string\") {\n resolvedModel = model;\n }\n\n const strategies = transformResponseFormat(\n responseFormat,\n undefined,\n resolvedModel\n );\n\n if (strategies.length === 0) {\n return undefined;\n }\n\n /**\n * we either define a list of provider strategies or a list of tool strategies\n */\n const isProviderStrategy = strategies.every(\n (format) => format instanceof ProviderStrategy\n );\n\n /**\n * Populate a list of structured tool info.\n */\n if (!isProviderStrategy) {\n return {\n type: \"tool\",\n tools: (\n strategies.filter(\n (format) => format instanceof ToolStrategy\n ) as ToolStrategy[]\n ).reduce(\n (acc, format) => {\n acc[format.name] = format;\n return acc;\n },\n {} as Record<string, ToolStrategy>\n ),\n };\n }\n\n return {\n type: \"native\",\n /**\n * there can only be one provider strategy\n */\n strategy: strategies[0] as ProviderStrategy,\n };\n }\n\n async #run(\n state: InternalAgentState<StructuredResponseFormat>,\n config: RunnableConfig\n ) {\n /**\n * Check if we just executed a returnDirect tool\n * If so, we should generate structured response (if needed) and stop\n */\n const lastMessage = state.messages.at(-1);\n if (\n lastMessage &&\n ToolMessage.isInstance(lastMessage) &&\n lastMessage.name &&\n this.#options.shouldReturnDirect.has(lastMessage.name)\n ) {\n return [new Command({ update: { messages: [] } })];\n }\n\n const { response, lastAiMessage, collectedCommands } =\n await this.#invokeModel(state, config);\n\n /**\n * structuredResponse — return as a plain state update dict (not a Command)\n * because the structuredResponse channel uses UntrackedValue(guard=true)\n * which only allows a single write per step.\n */\n if (\n typeof response === \"object\" &&\n response !== null &&\n \"structuredResponse\" in response &&\n \"messages\" in response\n ) {\n const { structuredResponse, messages } = response as {\n structuredResponse: StructuredResponseFormat;\n messages: BaseMessage[];\n };\n return {\n messages: [...state.messages, ...messages],\n structuredResponse,\n };\n }\n\n const commands: Command[] = [];\n const aiMessage: AIMessage | null = AIMessage.isInstance(response)\n ? response\n : lastAiMessage;\n\n // messages\n if (aiMessage) {\n aiMessage.name = this.name;\n aiMessage.lc_kwargs.name = this.name;\n\n if (this.#areMoreStepsNeeded(state, aiMessage)) {\n commands.push(\n new Command({\n update: {\n messages: [\n new AIMessage({\n content: \"Sorry, need more steps to process this request.\",\n name: this.name,\n id: aiMessage.id,\n }),\n ],\n },\n })\n );\n } else {\n commands.push(new Command({ update: { messages: [aiMessage] } }));\n }\n }\n\n // Commands (from base handler retries or middleware)\n if (isCommand(response) && !collectedCommands.includes(response)) {\n commands.push(response);\n }\n commands.push(...collectedCommands);\n\n return commands;\n }\n\n /**\n * Derive the model from the options.\n * @param state - The state of the agent.\n * @param config - The config of the agent.\n * @returns The model.\n */\n #deriveModel() {\n if (typeof this.#options.model === \"string\") {\n return initChatModel(this.#options.model);\n }\n\n if (this.#options.model) {\n return this.#options.model;\n }\n\n throw new Error(\"No model option was provided, either via `model` option.\");\n }\n\n async #invokeModel(\n state: InternalAgentState<StructuredResponseFormat>,\n config: RunnableConfig,\n options: {\n lastMessage?: string;\n } = {}\n ): Promise<{\n response: InternalModelResponse<StructuredResponseFormat>;\n lastAiMessage: AIMessage | null;\n collectedCommands: Command[];\n }> {\n const model = await this.#deriveModel();\n const lgConfig = config as LangGraphRunnableConfig;\n\n /**\n * Create a local variable for current system message to avoid concurrency issues\n * Each invocation gets its own copy\n */\n let currentSystemMessage = this.#systemMessage;\n\n /**\n * Shared tracking state for AIMessage and Command collection.\n * lastAiMessage tracks the effective AIMessage through the middleware chain.\n * collectedCommands accumulates Commands returned by middleware (not base handler).\n */\n let lastAiMessage: AIMessage | null = null;\n const collectedCommands: Command[] = [];\n\n /**\n * Create the base handler that performs the actual model invocation\n */\n const baseHandler = async (\n request: ModelRequest\n ): Promise<AIMessage | ResponseHandlerResult<StructuredResponseFormat>> => {\n /**\n * Check if the LLM already has bound tools and throw if it does.\n */\n validateLLMHasNoBoundTools(request.model);\n\n const structuredResponseFormat = await this.#getResponseFormat(\n request.model,\n request.responseFormat\n );\n const modelWithTools = await this.#bindTools(\n request.model,\n request,\n structuredResponseFormat\n );\n\n /**\n * prepend the system message to the messages if it is not empty\n */\n const messages = [\n ...(currentSystemMessage.text === \"\" ? [] : [currentSystemMessage]),\n ...request.messages,\n ];\n\n const signal = mergeAbortSignals(this.#options.signal, config.signal);\n const response = (await raceWithSignal(\n modelWithTools.invoke(messages, {\n ...config,\n signal,\n }),\n signal\n )) as AIMessage;\n\n lastAiMessage = response;\n\n /**\n * if the user requests a native schema output, try to parse the response\n * and return the structured response if it is valid\n */\n if (structuredResponseFormat?.type === \"native\") {\n const structuredResponse =\n structuredResponseFormat.strategy.parse(response);\n if (structuredResponse) {\n return { structuredResponse, messages: [response] };\n }\n\n /**\n * If the model produced a terminal response (no tool calls) but the\n * output failed to satisfy the provider strategy's schema, throw an\n * informative error instead of silently exiting with\n * `structuredResponse: undefined`. If tool calls are present, the\n * agent loop continues and a subsequent terminal step will get\n * another chance to produce a valid structured response.\n */\n if (!response.tool_calls || response.tool_calls.length === 0) {\n const schemaTitle =\n typeof structuredResponseFormat.strategy.schema?.title === \"string\"\n ? structuredResponseFormat.strategy.schema.title\n : \"providerStrategy\";\n throw new StructuredOutputParsingError(schemaTitle, [\n \"Model output did not satisfy the provided response schema.\",\n ]);\n }\n\n return response;\n }\n\n if (!structuredResponseFormat || !response.tool_calls) {\n return response;\n }\n\n const toolCalls = response.tool_calls.filter(\n (call) => call.name in structuredResponseFormat.tools\n );\n\n /**\n * if there were not structured tool calls, we can return the response\n */\n if (toolCalls.length === 0) {\n return response;\n }\n\n /**\n * if there were multiple structured tool calls, we should throw an error as this\n * scenario is not defined/supported.\n */\n if (toolCalls.length > 1) {\n return this.#handleMultipleStructuredOutputs(\n response,\n toolCalls,\n structuredResponseFormat\n );\n }\n\n const toolStrategy = structuredResponseFormat.tools[toolCalls[0].name];\n const toolMessageContent = toolStrategy?.options?.toolMessageContent;\n return this.#handleSingleStructuredOutput(\n response,\n toolCalls[0],\n structuredResponseFormat,\n toolMessageContent ?? options.lastMessage\n );\n };\n\n const wrapperMiddleware = this.#options.wrapModelCallHookMiddleware ?? [];\n let wrappedHandler: (\n request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ) => Promise<InternalModelResponse<StructuredResponseFormat>> = baseHandler;\n\n /**\n * Build composed handler from last to first so first middleware becomes outermost\n */\n for (let i = wrapperMiddleware.length - 1; i >= 0; i--) {\n const middlewareEntry = wrapperMiddleware[i];\n const middleware = Array.isArray(middlewareEntry)\n ? middlewareEntry[0]\n : middlewareEntry;\n if (middleware.wrapModelCall) {\n const innerHandler = wrappedHandler;\n const currentMiddleware = middleware;\n\n wrappedHandler = async (\n request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ): Promise<InternalModelResponse<StructuredResponseFormat>> => {\n const baselineSystemMessage = currentSystemMessage;\n\n /**\n * Merge context with default context of middleware\n */\n const context = currentMiddleware.contextSchema\n ? interopParse(\n currentMiddleware.contextSchema,\n lgConfig?.context || {}\n )\n : lgConfig?.context;\n\n /**\n * Create runtime\n */\n const runtime: Runtime<unknown> = Object.freeze({\n context,\n store: lgConfig.store,\n configurable: lgConfig.configurable,\n writer: lgConfig.writer,\n interrupt: lgConfig.interrupt,\n signal: lgConfig.signal,\n });\n\n /**\n * Create the request with state and runtime\n */\n const requestWithStateAndRuntime: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n > = {\n ...request,\n state: {\n ...(middleware.stateSchema\n ? interopParse(\n toPartialZodObject(middleware.stateSchema),\n state\n )\n : {}),\n messages: state.messages,\n } as InternalAgentState<StructuredResponseFormat>,\n runtime,\n };\n\n /**\n * Create handler that validates tools and calls the inner handler\n */\n const handlerWithValidation = async (\n req: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ): Promise<InternalModelResponse<StructuredResponseFormat>> => {\n currentSystemMessage = baselineSystemMessage;\n\n /**\n * Validate tool modifications in wrapModelCall.\n *\n * Classify each client tool as either:\n * - \"added\": a genuinely new tool name not in the static toolClasses\n * - \"replaced\": same name as a registered tool but different instance\n *\n * Added tools are allowed when a wrapToolCall middleware exists to\n * handle their execution. Replaced tools are always rejected to\n * preserve ToolNode execution identity.\n */\n const modifiedTools = req.tools ?? [];\n const registeredToolsByName = new Map(\n this.#options.toolClasses\n .filter(isClientTool)\n .map((t) => [t.name, t] as const)\n );\n\n const addedClientTools = modifiedTools.filter(\n (tool) =>\n isClientTool(tool) && !registeredToolsByName.has(tool.name)\n );\n\n const replacedClientTools = modifiedTools.filter((tool) => {\n if (!isClientTool(tool)) return false;\n const original = registeredToolsByName.get(tool.name);\n return original != null && original !== tool;\n });\n\n if (addedClientTools.length > 0) {\n const hasWrapToolCallHandler = this.#options.middleware?.some(\n (m) => m.wrapToolCall != null\n );\n if (!hasWrapToolCallHandler) {\n throw new Error(\n `You have added a new tool in \"wrapModelCall\" hook of middleware \"${\n currentMiddleware.name\n }\": ${addedClientTools\n .map((tool) => tool.name)\n .join(\n \", \"\n )}. This is not supported unless a middleware provides a \"wrapToolCall\" handler to execute it.`\n );\n }\n }\n\n if (replacedClientTools.length > 0) {\n throw new Error(\n `You have modified a tool in \"wrapModelCall\" hook of middleware \"${\n currentMiddleware.name\n }\": ${replacedClientTools\n .map((tool) => tool.name)\n .join(\", \")}. This is not supported.`\n );\n }\n\n let normalizedReq = req;\n const hasSystemPromptChanged =\n req.systemPrompt !== currentSystemMessage.text;\n const hasSystemMessageChanged =\n req.systemMessage !== currentSystemMessage;\n if (hasSystemPromptChanged && hasSystemMessageChanged) {\n throw new Error(\n \"Cannot change both systemPrompt and systemMessage in the same request.\"\n );\n }\n\n /**\n * Check if systemPrompt is a string was changed, if so create a new SystemMessage\n */\n if (hasSystemPromptChanged) {\n currentSystemMessage = new SystemMessage({\n content: [{ type: \"text\", text: req.systemPrompt }],\n });\n normalizedReq = {\n ...req,\n systemPrompt: currentSystemMessage.text,\n systemMessage: currentSystemMessage,\n };\n }\n /**\n * If the systemMessage was changed, update the current system message\n */\n if (hasSystemMessageChanged) {\n currentSystemMessage = new SystemMessage({\n ...req.systemMessage,\n });\n normalizedReq = {\n ...req,\n systemPrompt: currentSystemMessage.text,\n systemMessage: currentSystemMessage,\n };\n }\n\n const innerHandlerResult = await innerHandler(normalizedReq);\n\n /**\n * Normalize Commands so middleware always sees AIMessage from handler().\n * When an inner handler (base handler or nested middleware) returns a\n * Command (e.g. structured-output retry), substitute the tracked\n * lastAiMessage so the middleware sees an AIMessage, and collect the\n * raw Command so the framework can still propagate it (e.g. for retries).\n *\n * Only collect if not already present: Commands from inner middleware\n * are already tracked via the middleware validation layer (line ~627).\n */\n if (isCommand(innerHandlerResult) && lastAiMessage) {\n if (!collectedCommands.includes(innerHandlerResult)) {\n collectedCommands.push(innerHandlerResult);\n }\n return lastAiMessage as InternalModelResponse<StructuredResponseFormat>;\n }\n\n return innerHandlerResult;\n };\n\n // Call middleware's wrapModelCall with the validation handler\n if (!currentMiddleware.wrapModelCall) {\n return handlerWithValidation(requestWithStateAndRuntime);\n }\n\n try {\n const middlewareResponse = await currentMiddleware.wrapModelCall(\n requestWithStateAndRuntime,\n handlerWithValidation as WrapModelCallHandler\n );\n\n /**\n * Validate that this specific middleware returned a valid response\n */\n if (!isInternalModelResponse(middlewareResponse)) {\n throw new Error(\n `Invalid response from \"wrapModelCall\" in middleware \"${\n currentMiddleware.name\n }\": expected AIMessage or Command, got ${typeof middlewareResponse}`\n );\n }\n\n if (AIMessage.isInstance(middlewareResponse)) {\n lastAiMessage = middlewareResponse;\n } else if (isCommand(middlewareResponse)) {\n collectedCommands.push(middlewareResponse);\n }\n\n return middlewareResponse;\n } catch (error) {\n throw MiddlewareError.wrap(error, currentMiddleware.name);\n }\n };\n }\n }\n\n /**\n * Execute the wrapped handler with the initial request\n * Reset current system prompt to initial state and convert to string using .text getter\n * for backwards compatibility with ModelRequest\n */\n currentSystemMessage = this.#systemMessage;\n const initialRequest: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n > = {\n model,\n responseFormat: this.#options.responseFormat,\n systemPrompt: currentSystemMessage?.text,\n systemMessage: currentSystemMessage,\n messages: state.messages,\n tools: this.#options.toolClasses,\n state,\n runtime: Object.freeze({\n context: lgConfig?.context,\n store: lgConfig.store,\n configurable: lgConfig.configurable,\n writer: lgConfig.writer,\n interrupt: lgConfig.interrupt,\n signal: lgConfig.signal,\n }) as Runtime<unknown>,\n };\n\n const response = await wrappedHandler(initialRequest);\n return { response, lastAiMessage, collectedCommands };\n }\n\n /**\n * If the model returns multiple structured outputs, we need to handle it.\n * @param response - The response from the model\n * @param toolCalls - The tool calls that were made\n * @returns The response from the model\n */\n #handleMultipleStructuredOutputs(\n response: AIMessage,\n toolCalls: ToolCall[],\n responseFormat: ToolResponseFormat\n ): Promise<Command> {\n const multipleStructuredOutputsError = new MultipleStructuredOutputsError(\n toolCalls.map((call) => call.name)\n );\n\n return this.#handleToolStrategyError(\n multipleStructuredOutputsError,\n response,\n toolCalls[0],\n responseFormat\n );\n }\n\n /**\n * If the model returns a single structured output, we need to handle it.\n * @param toolCall - The tool call that was made\n * @returns The structured response and a message to the LLM if needed\n */\n #handleSingleStructuredOutput(\n response: AIMessage,\n toolCall: ToolCall,\n responseFormat: ToolResponseFormat,\n lastMessage?: string\n ): ResponseHandlerResult<StructuredResponseFormat> {\n const tool = responseFormat.tools[toolCall.name];\n\n try {\n const structuredResponse = tool.parse(\n toolCall.args\n ) as StructuredResponseFormat;\n\n return {\n structuredResponse,\n messages: [\n response,\n new ToolMessage({\n tool_call_id: toolCall.id ?? \"\",\n content: JSON.stringify(structuredResponse),\n name: toolCall.name,\n }),\n new AIMessage(\n lastMessage ??\n `Returning structured response: ${JSON.stringify(\n structuredResponse\n )}`\n ),\n ],\n };\n } catch (error) {\n return this.#handleToolStrategyError(\n error as ToolStrategyError,\n response,\n toolCall,\n responseFormat\n );\n }\n }\n\n async #handleToolStrategyError(\n error: ToolStrategyError,\n response: AIMessage,\n toolCall: ToolCall,\n responseFormat: ToolResponseFormat\n ): Promise<Command> {\n /**\n * Using the `errorHandler` option of the first `ToolStrategy` entry is sufficient here.\n * There is technically only one `ToolStrategy` entry in `structuredToolInfo` if the user\n * uses `toolStrategy` to define the response format. If the user applies a list of json\n * schema objects, these will be transformed into multiple `ToolStrategy` entries but all\n * with the same `handleError` option.\n */\n const errorHandler = Object.values(responseFormat.tools).at(0)?.options\n ?.handleError;\n\n const toolCallId = toolCall.id;\n if (!toolCallId) {\n throw new Error(\n \"Tool call ID is required to handle tool output errors. Please provide a tool call ID.\"\n );\n }\n\n /**\n * Default behavior: retry if `errorHandler` is undefined or truthy.\n * Only throw if explicitly set to `false`.\n */\n if (errorHandler === false) {\n throw error;\n }\n\n /**\n * retry if:\n */\n if (\n /**\n * if the user has provided truthy value as the `errorHandler`, return a new AIMessage\n * with the error message and retry the tool call.\n */\n errorHandler === undefined ||\n (typeof errorHandler === \"boolean\" && errorHandler) ||\n /**\n * if `errorHandler` is an array and contains MultipleStructuredOutputsError\n */\n (Array.isArray(errorHandler) &&\n errorHandler.some((h) => h instanceof MultipleStructuredOutputsError))\n ) {\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content: error.message,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n /**\n * if `errorHandler` is a string, retry the tool call with given string\n */\n if (typeof errorHandler === \"string\") {\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content: errorHandler,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n /**\n * if `errorHandler` is a function, retry the tool call with the function\n */\n if (typeof errorHandler === \"function\") {\n const content = await errorHandler(error);\n if (typeof content !== \"string\") {\n throw new Error(\"Error handler must return a string.\");\n }\n\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n /**\n * Default: retry if we reach here\n */\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content: error.message,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n #areMoreStepsNeeded(\n state: InternalAgentState<StructuredResponseFormat>,\n response: BaseMessage\n ): boolean {\n const allToolsReturnDirect =\n AIMessage.isInstance(response) &&\n response.tool_calls?.every((call) =>\n this.#options.shouldReturnDirect.has(call.name)\n );\n const remainingSteps =\n \"remainingSteps\" in state ? (state.remainingSteps as number) : undefined;\n return Boolean(\n remainingSteps &&\n ((remainingSteps < 1 && allToolsReturnDirect) ||\n (remainingSteps < 2 && hasToolCalls(state.messages.at(-1))))\n );\n }\n\n async #bindTools(\n model: LanguageModelLike,\n preparedOptions: ModelRequest | undefined,\n structuredResponseFormat: ResponseFormat | undefined\n ): Promise<LanguageModelLike | Runnable> {\n const options: Partial<BaseChatModelCallOptions> = {};\n const structuredTools = Object.values(\n structuredResponseFormat && \"tools\" in structuredResponseFormat\n ? structuredResponseFormat.tools\n : {}\n );\n\n /**\n * Use tools from preparedOptions if provided, otherwise use default tools\n */\n const allTools = [\n ...(preparedOptions?.tools ?? this.#options.toolClasses),\n ...structuredTools.map((toolStrategy) => toolStrategy.tool),\n ];\n\n /**\n * If there are structured tools, we need to set the tool choice to \"any\"\n * so that the model can choose to use a structured tool or not.\n */\n const toolChoice =\n preparedOptions?.toolChoice ||\n (structuredTools.length > 0 ? \"any\" : undefined);\n\n /**\n * check if the user requests a native schema output\n */\n if (structuredResponseFormat?.type === \"native\") {\n const resolvedStrict =\n preparedOptions?.modelSettings?.strict ??\n structuredResponseFormat?.strategy?.strict ??\n true;\n\n const jsonSchemaParams = {\n name: structuredResponseFormat.strategy.schema?.name ?? \"extract\",\n description: getSchemaDescription(\n structuredResponseFormat.strategy.schema\n ),\n schema: structuredResponseFormat.strategy.schema,\n strict: resolvedStrict,\n };\n\n Object.assign(options, {\n /**\n * OpenAI-style options\n * Used by ChatOpenAI, ChatXAI, and other OpenAI-compatible providers.\n */\n response_format: {\n type: \"json_schema\",\n json_schema: jsonSchemaParams,\n },\n\n /**\n * Anthropic-style options\n */\n outputConfig: {\n format: {\n type: \"json_schema\",\n schema: structuredResponseFormat.strategy.schema,\n },\n },\n\n /**\n * Google-style options\n * Used by ChatGoogle and other Gemini-based providers.\n */\n responseSchema: structuredResponseFormat.strategy.schema,\n\n /**\n * for LangSmith structured output tracing\n */\n ls_structured_output_format: {\n kwargs: { method: \"json_schema\" },\n schema: structuredResponseFormat.strategy.schema,\n },\n strict: resolvedStrict,\n });\n }\n\n /**\n * Bind tools to the model if they are not already bound.\n */\n const modelWithTools = await bindTools(model, allTools, {\n ...options,\n ...preparedOptions?.modelSettings,\n tool_choice: toolChoice,\n });\n\n /**\n * Create a model runnable with the prompt and agent name\n * Use current SystemMessage state (which may have been modified by middleware)\n */\n const modelRunnable =\n this.#options.includeAgentName === \"inline\"\n ? withAgentName(modelWithTools, this.#options.includeAgentName)\n : modelWithTools;\n\n return modelRunnable;\n }\n\n /**\n * Returns internal bookkeeping state for StateManager, not graph output.\n * The return shape differs from the node's output type (Command).\n */\n // @ts-expect-error Internal state shape differs from graph output type\n getState(): { messages: BaseMessage[] } {\n const state = super.getState();\n const origState = state && !isCommand(state) ? state : {};\n\n return {\n messages: [],\n ...origState,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentNode.cjs","names":["AIMessage","RunnableCallable","#run","#options","#systemMessage","#getResponseFormat","isConfigurableModel","transformResponseFormat","ProviderStrategy","ToolStrategy","ToolMessage","Command","#invokeModel","#areMoreStepsNeeded","initChatModel","#deriveModel","#bindTools","mergeAbortSignals","StructuredOutputParsingError","#handleMultipleStructuredOutputs","#handleSingleStructuredOutput","toPartialZodObject","isClientTool","SystemMessage","MiddlewareError","MultipleStructuredOutputsError","#handleToolStrategyError","hasToolCalls","bindTools","withAgentName"],"sources":["../../../src/agents/nodes/AgentNode.ts"],"sourcesContent":["/* oxlint-disable no-instanceof/no-instanceof */\nimport { Runnable, RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseMessage,\n AIMessage,\n ToolMessage,\n SystemMessage,\n} from \"@langchain/core/messages\";\nimport {\n Command,\n isCommand,\n type LangGraphRunnableConfig,\n} from \"@langchain/langgraph\";\nimport { type BaseChatModelCallOptions } from \"@langchain/core/language_models/chat_models\";\nimport {\n InteropZodObject,\n getSchemaDescription,\n interopParse,\n} from \"@langchain/core/utils/types\";\nimport { raceWithSignal } from \"@langchain/core/runnables\";\nimport type { ToolCall } from \"@langchain/core/messages/tool\";\nimport type { ClientTool, ServerTool } from \"@langchain/core/tools\";\n\nimport { initChatModel } from \"../../chat_models/universal.js\";\nimport {\n MultipleStructuredOutputsError,\n MiddlewareError,\n StructuredOutputParsingError,\n} from \"../errors.js\";\nimport { RunnableCallable } from \"../RunnableCallable.js\";\nimport type { AgentLanguageModelLike as LanguageModelLike } from \"../model.js\";\nimport {\n bindTools,\n validateLLMHasNoBoundTools,\n hasToolCalls,\n isClientTool,\n} from \"../utils.js\";\nimport { isConfigurableModel } from \"../model.js\";\nimport { mergeAbortSignals, toPartialZodObject } from \"../nodes/utils.js\";\nimport { CreateAgentParams } from \"../types.js\";\nimport type { InternalAgentState, Runtime } from \"../runtime.js\";\nimport type {\n AgentMiddleware,\n AnyAnnotationRoot,\n WrapModelCallHandler,\n} from \"../middleware/types.js\";\nimport type { ModelRequest } from \"./types.js\";\nimport { withAgentName } from \"../withAgentName.js\";\nimport {\n ToolStrategy,\n ProviderStrategy,\n transformResponseFormat,\n ToolStrategyError,\n type ResponseFormatInput,\n} from \"../responses.js\";\n\ntype ResponseHandlerResult<StructuredResponseFormat> =\n | {\n structuredResponse: StructuredResponseFormat;\n messages: BaseMessage[];\n }\n | Promise<Command>;\n\n/**\n * Wrap the base handler with middleware wrapModelCall hooks\n * Middleware are composed so the first middleware is the outermost wrapper\n * Example: [auth, retry, cache] means auth wraps retry wraps cache wraps baseHandler\n */\ntype InternalModelResponse<StructuredResponseFormat> =\n | AIMessage\n | ResponseHandlerResult<StructuredResponseFormat>\n | Command;\n\n/**\n * Check if the response is an internal model response.\n * @param response - The response to check.\n * @returns True if the response is an internal model response, false otherwise.\n */\nfunction isInternalModelResponse<StructuredResponseFormat>(\n response: unknown\n): response is InternalModelResponse<StructuredResponseFormat> {\n return (\n AIMessage.isInstance(response) ||\n isCommand(response) ||\n (typeof response === \"object\" &&\n response !== null &&\n \"structuredResponse\" in response &&\n \"messages\" in response)\n );\n}\n\n/**\n * The name of the agent node in the state graph.\n */\nexport const AGENT_NODE_NAME = \"model_request\";\n\nexport interface AgentNodeOptions<\n StructuredResponseFormat extends Record<string, unknown> = Record<\n string,\n unknown\n >,\n StateSchema extends AnyAnnotationRoot | InteropZodObject = AnyAnnotationRoot,\n ContextSchema extends AnyAnnotationRoot | InteropZodObject =\n AnyAnnotationRoot,\n> extends Pick<\n CreateAgentParams<StructuredResponseFormat, StateSchema, ContextSchema>,\n \"model\" | \"includeAgentName\" | \"name\" | \"responseFormat\" | \"middleware\"\n> {\n toolClasses: (ClientTool | ServerTool)[];\n shouldReturnDirect: Set<string>;\n signal?: AbortSignal;\n systemMessage: SystemMessage;\n wrapModelCallHookMiddleware?: (\n | AgentMiddleware\n | [AgentMiddleware, (...args: unknown[]) => Record<string, unknown>]\n )[];\n}\n\ninterface NativeResponseFormat {\n type: \"native\";\n strategy: ProviderStrategy;\n}\n\ninterface ToolResponseFormat {\n type: \"tool\";\n tools: Record<string, ToolStrategy>;\n}\n\ntype ResponseFormat = NativeResponseFormat | ToolResponseFormat;\n\nexport class AgentNode<\n StructuredResponseFormat extends Record<string, unknown> = Record<\n string,\n unknown\n >,\n ContextSchema extends AnyAnnotationRoot | InteropZodObject =\n AnyAnnotationRoot,\n> extends RunnableCallable<\n InternalAgentState<StructuredResponseFormat>,\n | Command[]\n | {\n messages: BaseMessage[];\n structuredResponse: StructuredResponseFormat;\n }\n> {\n #options: AgentNodeOptions<StructuredResponseFormat, ContextSchema>;\n #systemMessage: SystemMessage;\n\n constructor(\n options: AgentNodeOptions<StructuredResponseFormat, ContextSchema>\n ) {\n super({\n name: options.name ?? \"model\",\n func: (input, config) => this.#run(input, config as RunnableConfig),\n });\n\n this.#options = options;\n this.#systemMessage = options.systemMessage;\n }\n\n /**\n * Returns response format primtivies based on given model and response format provided by the user.\n *\n * If the user selects a tool output:\n * - return a record of tools to extract structured output from the model's response\n *\n * if the user selects a native schema output or if the model supports JSON schema output:\n * - return a provider strategy to extract structured output from the model's response\n *\n * @param model - The model to get the response format for.\n * @returns The response format.\n */\n async #getResponseFormat(\n model: string | LanguageModelLike,\n responseFormat: ResponseFormatInput | undefined = this.#options\n .responseFormat\n ): Promise<ResponseFormat | undefined> {\n if (!responseFormat) {\n return undefined;\n }\n\n let resolvedModel: LanguageModelLike | undefined;\n if (isConfigurableModel(model)) {\n resolvedModel = await (\n model as unknown as {\n _getModelInstance: () => Promise<LanguageModelLike>;\n }\n )._getModelInstance();\n } else if (typeof model !== \"string\") {\n resolvedModel = model;\n }\n\n const strategies = transformResponseFormat(\n responseFormat,\n undefined,\n resolvedModel\n );\n\n if (strategies.length === 0) {\n return undefined;\n }\n\n /**\n * we either define a list of provider strategies or a list of tool strategies\n */\n const isProviderStrategy = strategies.every(\n (format) => format instanceof ProviderStrategy\n );\n\n /**\n * Populate a list of structured tool info.\n */\n if (!isProviderStrategy) {\n return {\n type: \"tool\",\n tools: (\n strategies.filter(\n (format) => format instanceof ToolStrategy\n ) as ToolStrategy[]\n ).reduce(\n (acc, format) => {\n acc[format.name] = format;\n return acc;\n },\n {} as Record<string, ToolStrategy>\n ),\n };\n }\n\n return {\n type: \"native\",\n /**\n * there can only be one provider strategy\n */\n strategy: strategies[0] as ProviderStrategy,\n };\n }\n\n async #run(\n state: InternalAgentState<StructuredResponseFormat>,\n config: RunnableConfig\n ) {\n /**\n * Check if we just executed a returnDirect tool\n * If so, we should generate structured response (if needed) and stop\n */\n const lastMessage = state.messages.at(-1);\n if (\n lastMessage &&\n ToolMessage.isInstance(lastMessage) &&\n lastMessage.name &&\n this.#options.shouldReturnDirect.has(lastMessage.name)\n ) {\n return [new Command({ update: { messages: [] } })];\n }\n\n const { response, lastAiMessage, collectedCommands } =\n await this.#invokeModel(state, config);\n\n /**\n * structuredResponse — return as a plain state update dict (not a Command)\n * because the structuredResponse channel uses UntrackedValue(guard=true)\n * which only allows a single write per step.\n */\n if (\n typeof response === \"object\" &&\n response !== null &&\n \"structuredResponse\" in response &&\n \"messages\" in response\n ) {\n const { structuredResponse, messages } = response as {\n structuredResponse: StructuredResponseFormat;\n messages: BaseMessage[];\n };\n return {\n messages: [...state.messages, ...messages],\n structuredResponse,\n };\n }\n\n const commands: Command[] = [];\n const aiMessage: AIMessage | null = AIMessage.isInstance(response)\n ? response\n : lastAiMessage;\n\n // messages\n if (aiMessage) {\n aiMessage.name = this.name;\n aiMessage.lc_kwargs.name = this.name;\n\n if (this.#areMoreStepsNeeded(state, aiMessage)) {\n commands.push(\n new Command({\n update: {\n messages: [\n new AIMessage({\n content: \"Sorry, need more steps to process this request.\",\n name: this.name,\n id: aiMessage.id,\n }),\n ],\n },\n })\n );\n } else {\n commands.push(new Command({ update: { messages: [aiMessage] } }));\n }\n }\n\n // Commands (from base handler retries or middleware)\n if (isCommand(response) && !collectedCommands.includes(response)) {\n commands.push(response);\n }\n commands.push(...collectedCommands);\n\n return commands;\n }\n\n /**\n * Derive the model from the options.\n * @param state - The state of the agent.\n * @param config - The config of the agent.\n * @returns The model.\n */\n #deriveModel() {\n if (typeof this.#options.model === \"string\") {\n return initChatModel(this.#options.model);\n }\n\n if (this.#options.model) {\n return this.#options.model;\n }\n\n throw new Error(\"No model option was provided, either via `model` option.\");\n }\n\n async #invokeModel(\n state: InternalAgentState<StructuredResponseFormat>,\n config: RunnableConfig,\n options: {\n lastMessage?: string;\n } = {}\n ): Promise<{\n response: InternalModelResponse<StructuredResponseFormat>;\n lastAiMessage: AIMessage | null;\n collectedCommands: Command[];\n }> {\n const model = await this.#deriveModel();\n const lgConfig = config as LangGraphRunnableConfig;\n\n /**\n * Create a local variable for current system message to avoid concurrency issues\n * Each invocation gets its own copy\n */\n let currentSystemMessage = this.#systemMessage;\n\n /**\n * Shared tracking state for AIMessage and Command collection.\n * lastAiMessage tracks the effective AIMessage through the middleware chain.\n * collectedCommands accumulates Commands returned by middleware (not base handler).\n */\n let lastAiMessage: AIMessage | null = null;\n const collectedCommands: Command[] = [];\n\n /**\n * Create the base handler that performs the actual model invocation\n */\n const baseHandler = async (\n request: ModelRequest\n ): Promise<AIMessage | ResponseHandlerResult<StructuredResponseFormat>> => {\n /**\n * Check if the LLM already has bound tools and throw if it does.\n */\n validateLLMHasNoBoundTools(request.model);\n\n const structuredResponseFormat = await this.#getResponseFormat(\n request.model,\n request.responseFormat\n );\n const modelWithTools = await this.#bindTools(\n request.model,\n request,\n structuredResponseFormat\n );\n\n /**\n * prepend the system message to the messages if it is not empty\n */\n const messages = [\n ...(currentSystemMessage.text === \"\" ? [] : [currentSystemMessage]),\n ...request.messages,\n ];\n\n const signal = mergeAbortSignals(this.#options.signal, config.signal);\n const response = (await raceWithSignal(\n modelWithTools.invoke(messages, {\n ...config,\n signal,\n }),\n signal\n )) as AIMessage;\n\n lastAiMessage = response;\n\n /**\n * if the user requests a native schema output, try to parse the response\n * and return the structured response if it is valid\n */\n if (structuredResponseFormat?.type === \"native\") {\n const structuredResponse =\n structuredResponseFormat.strategy.parse(response);\n if (structuredResponse) {\n return { structuredResponse, messages: [response] };\n }\n\n /**\n * If the model produced a terminal response (no tool calls) but the\n * output failed to satisfy the provider strategy's schema, throw an\n * informative error instead of silently exiting with\n * `structuredResponse: undefined`. If tool calls are present, the\n * agent loop continues and a subsequent terminal step will get\n * another chance to produce a valid structured response.\n */\n if (!response.tool_calls || response.tool_calls.length === 0) {\n const schemaTitle =\n typeof structuredResponseFormat.strategy.schema?.title === \"string\"\n ? structuredResponseFormat.strategy.schema.title\n : \"providerStrategy\";\n throw new StructuredOutputParsingError(schemaTitle, [\n \"Model output did not satisfy the provided response schema.\",\n ]);\n }\n\n return response;\n }\n\n if (!structuredResponseFormat || !response.tool_calls) {\n return response;\n }\n\n const toolCalls = response.tool_calls.filter(\n (call) => call.name in structuredResponseFormat.tools\n );\n\n /**\n * if there were not structured tool calls, we can return the response\n */\n if (toolCalls.length === 0) {\n return response;\n }\n\n /**\n * if there were multiple structured tool calls, we should throw an error as this\n * scenario is not defined/supported.\n */\n if (toolCalls.length > 1) {\n return this.#handleMultipleStructuredOutputs(\n response,\n toolCalls,\n structuredResponseFormat\n );\n }\n\n const toolStrategy = structuredResponseFormat.tools[toolCalls[0].name];\n const toolMessageContent = toolStrategy?.options?.toolMessageContent;\n return this.#handleSingleStructuredOutput(\n response,\n toolCalls[0],\n structuredResponseFormat,\n toolMessageContent ?? options.lastMessage\n );\n };\n\n const wrapperMiddleware = this.#options.wrapModelCallHookMiddleware ?? [];\n let wrappedHandler: (\n request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ) => Promise<InternalModelResponse<StructuredResponseFormat>> = baseHandler;\n\n /**\n * Build composed handler from last to first so first middleware becomes outermost\n */\n for (let i = wrapperMiddleware.length - 1; i >= 0; i--) {\n const middlewareEntry = wrapperMiddleware[i];\n const middleware = Array.isArray(middlewareEntry)\n ? middlewareEntry[0]\n : middlewareEntry;\n if (middleware.wrapModelCall) {\n const innerHandler = wrappedHandler;\n const currentMiddleware = middleware;\n\n wrappedHandler = async (\n request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ): Promise<InternalModelResponse<StructuredResponseFormat>> => {\n const baselineSystemMessage = currentSystemMessage;\n\n /**\n * Merge context with default context of middleware\n */\n const context = currentMiddleware.contextSchema\n ? interopParse(\n currentMiddleware.contextSchema,\n lgConfig?.context || {}\n )\n : lgConfig?.context;\n\n /**\n * Create runtime\n */\n const runtime: Runtime<unknown> = Object.freeze({\n context,\n store: lgConfig.store,\n configurable: lgConfig.configurable,\n writer: lgConfig.writer,\n interrupt: lgConfig.interrupt,\n signal: lgConfig.signal,\n });\n\n /**\n * Create the request with state and runtime\n */\n const requestWithStateAndRuntime: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n > = {\n ...request,\n state: {\n ...(middleware.stateSchema\n ? interopParse(\n toPartialZodObject(middleware.stateSchema),\n state\n )\n : {}),\n messages: state.messages,\n } as InternalAgentState<StructuredResponseFormat>,\n runtime,\n };\n\n /**\n * Create handler that validates tools and calls the inner handler\n */\n const handlerWithValidation = async (\n req: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ): Promise<InternalModelResponse<StructuredResponseFormat>> => {\n currentSystemMessage = baselineSystemMessage;\n\n /**\n * Validate tool modifications in wrapModelCall.\n *\n * Classify each client tool as either:\n * - \"added\": a genuinely new tool name not in the static toolClasses\n * - \"replaced\": same name as a registered tool but different instance\n *\n * Added tools are allowed when a wrapToolCall middleware exists to\n * handle their execution. Replaced tools are always rejected to\n * preserve ToolNode execution identity.\n */\n const modifiedTools = req.tools ?? [];\n const registeredToolsByName = new Map(\n this.#options.toolClasses\n .filter(isClientTool)\n .map((t) => [t.name, t] as const)\n );\n\n const addedClientTools = modifiedTools.filter(\n (tool) =>\n isClientTool(tool) && !registeredToolsByName.has(tool.name)\n );\n\n const replacedClientTools = modifiedTools.filter((tool) => {\n if (!isClientTool(tool)) return false;\n const original = registeredToolsByName.get(tool.name);\n return original != null && original !== tool;\n });\n\n if (addedClientTools.length > 0) {\n const hasWrapToolCallHandler = this.#options.middleware?.some(\n (m) => m.wrapToolCall != null\n );\n if (!hasWrapToolCallHandler) {\n throw new Error(\n `You have added a new tool in \"wrapModelCall\" hook of middleware \"${\n currentMiddleware.name\n }\": ${addedClientTools\n .map((tool) => tool.name)\n .join(\n \", \"\n )}. This is not supported unless a middleware provides a \"wrapToolCall\" handler to execute it.`\n );\n }\n }\n\n if (replacedClientTools.length > 0) {\n throw new Error(\n `You have modified a tool in \"wrapModelCall\" hook of middleware \"${\n currentMiddleware.name\n }\": ${replacedClientTools\n .map((tool) => tool.name)\n .join(\", \")}. This is not supported.`\n );\n }\n\n let normalizedReq = req;\n const hasSystemPromptChanged =\n req.systemPrompt !== currentSystemMessage.text;\n const hasSystemMessageChanged =\n req.systemMessage !== currentSystemMessage;\n if (hasSystemPromptChanged && hasSystemMessageChanged) {\n throw new Error(\n \"Cannot change both systemPrompt and systemMessage in the same request.\"\n );\n }\n\n /**\n * Check if systemPrompt is a string was changed, if so create a new SystemMessage\n */\n if (hasSystemPromptChanged) {\n currentSystemMessage = new SystemMessage({\n content: [{ type: \"text\", text: req.systemPrompt }],\n });\n normalizedReq = {\n ...req,\n systemPrompt: currentSystemMessage.text,\n systemMessage: currentSystemMessage,\n };\n }\n /**\n * If the systemMessage was changed, update the current system message\n */\n if (hasSystemMessageChanged) {\n currentSystemMessage = new SystemMessage({\n ...req.systemMessage,\n });\n normalizedReq = {\n ...req,\n systemPrompt: currentSystemMessage.text,\n systemMessage: currentSystemMessage,\n };\n }\n\n const innerHandlerResult = await innerHandler(normalizedReq);\n\n /**\n * Normalize Commands so middleware always sees AIMessage from handler().\n * When an inner handler (base handler or nested middleware) returns a\n * Command (e.g. structured-output retry), substitute the tracked\n * lastAiMessage so the middleware sees an AIMessage, and collect the\n * raw Command so the framework can still propagate it (e.g. for retries).\n *\n * Only collect if not already present: Commands from inner middleware\n * are already tracked via the middleware validation layer (line ~627).\n */\n if (isCommand(innerHandlerResult) && lastAiMessage) {\n if (!collectedCommands.includes(innerHandlerResult)) {\n collectedCommands.push(innerHandlerResult);\n }\n return lastAiMessage as InternalModelResponse<StructuredResponseFormat>;\n }\n\n return innerHandlerResult;\n };\n\n // Call middleware's wrapModelCall with the validation handler\n if (!currentMiddleware.wrapModelCall) {\n return handlerWithValidation(requestWithStateAndRuntime);\n }\n\n try {\n const middlewareResponse = await currentMiddleware.wrapModelCall(\n requestWithStateAndRuntime,\n handlerWithValidation as WrapModelCallHandler\n );\n\n /**\n * Validate that this specific middleware returned a valid response\n */\n if (!isInternalModelResponse(middlewareResponse)) {\n throw new Error(\n `Invalid response from \"wrapModelCall\" in middleware \"${\n currentMiddleware.name\n }\": expected AIMessage or Command, got ${typeof middlewareResponse}`\n );\n }\n\n if (AIMessage.isInstance(middlewareResponse)) {\n lastAiMessage = middlewareResponse;\n } else if (isCommand(middlewareResponse)) {\n collectedCommands.push(middlewareResponse);\n }\n\n return middlewareResponse;\n } catch (error) {\n throw MiddlewareError.wrap(error, currentMiddleware.name);\n }\n };\n }\n }\n\n /**\n * Execute the wrapped handler with the initial request\n * Reset current system prompt to initial state and convert to string using .text getter\n * for backwards compatibility with ModelRequest\n */\n currentSystemMessage = this.#systemMessage;\n const initialRequest: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n > = {\n model,\n responseFormat: this.#options.responseFormat,\n systemPrompt: currentSystemMessage?.text,\n systemMessage: currentSystemMessage,\n messages: state.messages,\n tools: this.#options.toolClasses,\n state,\n runtime: Object.freeze({\n context: lgConfig?.context,\n store: lgConfig.store,\n configurable: lgConfig.configurable,\n writer: lgConfig.writer,\n interrupt: lgConfig.interrupt,\n signal: lgConfig.signal,\n }) as Runtime<unknown>,\n };\n\n const response = await wrappedHandler(initialRequest);\n return { response, lastAiMessage, collectedCommands };\n }\n\n /**\n * If the model returns multiple structured outputs, we need to handle it.\n * @param response - The response from the model\n * @param toolCalls - The tool calls that were made\n * @returns The response from the model\n */\n #handleMultipleStructuredOutputs(\n response: AIMessage,\n toolCalls: ToolCall[],\n responseFormat: ToolResponseFormat\n ): Promise<Command> {\n const multipleStructuredOutputsError = new MultipleStructuredOutputsError(\n toolCalls.map((call) => call.name)\n );\n\n return this.#handleToolStrategyError(\n multipleStructuredOutputsError,\n response,\n toolCalls[0],\n responseFormat\n );\n }\n\n /**\n * If the model returns a single structured output, we need to handle it.\n * @param toolCall - The tool call that was made\n * @returns The structured response and a message to the LLM if needed\n */\n #handleSingleStructuredOutput(\n response: AIMessage,\n toolCall: ToolCall,\n responseFormat: ToolResponseFormat,\n lastMessage?: string\n ): ResponseHandlerResult<StructuredResponseFormat> {\n const tool = responseFormat.tools[toolCall.name];\n\n try {\n const structuredResponse = tool.parse(\n toolCall.args\n ) as StructuredResponseFormat;\n\n return {\n structuredResponse,\n messages: [\n response,\n new ToolMessage({\n tool_call_id: toolCall.id ?? \"\",\n content: JSON.stringify(structuredResponse),\n name: toolCall.name,\n }),\n new AIMessage(\n lastMessage ??\n `Returning structured response: ${JSON.stringify(\n structuredResponse\n )}`\n ),\n ],\n };\n } catch (error) {\n return this.#handleToolStrategyError(\n error as ToolStrategyError,\n response,\n toolCall,\n responseFormat\n );\n }\n }\n\n async #handleToolStrategyError(\n error: ToolStrategyError,\n response: AIMessage,\n toolCall: ToolCall,\n responseFormat: ToolResponseFormat\n ): Promise<Command> {\n /**\n * Using the `errorHandler` option of the first `ToolStrategy` entry is sufficient here.\n * There is technically only one `ToolStrategy` entry in `structuredToolInfo` if the user\n * uses `toolStrategy` to define the response format. If the user applies a list of json\n * schema objects, these will be transformed into multiple `ToolStrategy` entries but all\n * with the same `handleError` option.\n */\n const errorHandler = Object.values(responseFormat.tools).at(0)?.options\n ?.handleError;\n\n const toolCallId = toolCall.id;\n if (!toolCallId) {\n throw new Error(\n \"Tool call ID is required to handle tool output errors. Please provide a tool call ID.\"\n );\n }\n\n /**\n * Default behavior: retry if `errorHandler` is undefined or truthy.\n * Only throw if explicitly set to `false`.\n */\n if (errorHandler === false) {\n throw error;\n }\n\n /**\n * retry if:\n */\n if (\n /**\n * if the user has provided truthy value as the `errorHandler`, return a new AIMessage\n * with the error message and retry the tool call.\n */\n errorHandler === undefined ||\n (typeof errorHandler === \"boolean\" && errorHandler) ||\n /**\n * if `errorHandler` is an array and contains MultipleStructuredOutputsError\n */\n (Array.isArray(errorHandler) &&\n errorHandler.some((h) => h instanceof MultipleStructuredOutputsError))\n ) {\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content: error.message,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n /**\n * if `errorHandler` is a string, retry the tool call with given string\n */\n if (typeof errorHandler === \"string\") {\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content: errorHandler,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n /**\n * if `errorHandler` is a function, retry the tool call with the function\n */\n if (typeof errorHandler === \"function\") {\n const content = await errorHandler(error);\n if (typeof content !== \"string\") {\n throw new Error(\"Error handler must return a string.\");\n }\n\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n /**\n * Default: retry if we reach here\n */\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content: error.message,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n #areMoreStepsNeeded(\n state: InternalAgentState<StructuredResponseFormat>,\n response: BaseMessage\n ): boolean {\n const allToolsReturnDirect =\n AIMessage.isInstance(response) &&\n response.tool_calls?.every((call) =>\n this.#options.shouldReturnDirect.has(call.name)\n );\n const remainingSteps =\n \"remainingSteps\" in state ? (state.remainingSteps as number) : undefined;\n return Boolean(\n remainingSteps &&\n ((remainingSteps < 1 && allToolsReturnDirect) ||\n (remainingSteps < 2 && hasToolCalls(state.messages.at(-1))))\n );\n }\n\n async #bindTools(\n model: LanguageModelLike,\n preparedOptions: ModelRequest | undefined,\n structuredResponseFormat: ResponseFormat | undefined\n ): Promise<LanguageModelLike | Runnable> {\n const options: Partial<BaseChatModelCallOptions> = {};\n const structuredTools = Object.values(\n structuredResponseFormat && \"tools\" in structuredResponseFormat\n ? structuredResponseFormat.tools\n : {}\n );\n\n /**\n * Use tools from preparedOptions if provided, otherwise use default tools\n */\n const allTools = [\n ...(preparedOptions?.tools ?? this.#options.toolClasses),\n ...structuredTools.map((toolStrategy) => toolStrategy.tool),\n ];\n\n /**\n * If there are structured tools, we need to set the tool choice to \"any\"\n * so that the model can choose to use a structured tool or not.\n */\n const toolChoice =\n preparedOptions?.toolChoice ||\n (structuredTools.length > 0 ? \"any\" : undefined);\n\n /**\n * check if the user requests a native schema output\n */\n if (structuredResponseFormat?.type === \"native\") {\n const resolvedStrict =\n preparedOptions?.modelSettings?.strict ??\n structuredResponseFormat?.strategy?.strict ??\n true;\n\n const jsonSchemaParams = {\n name: structuredResponseFormat.strategy.schema?.name ?? \"extract\",\n description: getSchemaDescription(\n structuredResponseFormat.strategy.schema\n ),\n schema: structuredResponseFormat.strategy.schema,\n strict: resolvedStrict,\n };\n\n Object.assign(options, {\n /**\n * OpenAI-style options\n * Used by ChatOpenAI, ChatXAI, and other OpenAI-compatible providers.\n */\n response_format: {\n type: \"json_schema\",\n json_schema: jsonSchemaParams,\n },\n\n /**\n * Anthropic-style options\n */\n outputConfig: {\n format: {\n type: \"json_schema\",\n schema: structuredResponseFormat.strategy.schema,\n },\n },\n\n /**\n * Google-style options\n * Used by ChatGoogle and other Gemini-based providers.\n */\n responseSchema: structuredResponseFormat.strategy.schema,\n\n /**\n * for LangSmith structured output tracing\n */\n ls_structured_output_format: {\n kwargs: { method: \"json_schema\" },\n schema: structuredResponseFormat.strategy.schema,\n },\n\n /**\n * Don't force strict on tools: it makes Anthropic's combined grammar\n * \"too complex for compilation\", and only OpenAI Chat Completions needs\n * it (re-applied there). Honor an explicit override; else leave unset.\n */\n strict: preparedOptions?.modelSettings?.strict,\n });\n }\n\n /**\n * Bind tools to the model if they are not already bound.\n */\n const modelWithTools = await bindTools(model, allTools, {\n ...options,\n ...preparedOptions?.modelSettings,\n tool_choice: toolChoice,\n });\n\n /**\n * Create a model runnable with the prompt and agent name\n * Use current SystemMessage state (which may have been modified by middleware)\n */\n const modelRunnable =\n this.#options.includeAgentName === \"inline\"\n ? withAgentName(modelWithTools, this.#options.includeAgentName)\n : modelWithTools;\n\n return modelRunnable;\n }\n\n /**\n * Returns internal bookkeeping state for StateManager, not graph output.\n * The return shape differs from the node's output type (Command).\n */\n // @ts-expect-error Internal state shape differs from graph output type\n getState(): { messages: BaseMessage[] } {\n const state = super.getState();\n const origState = state && !isCommand(state) ? state : {};\n\n return {\n messages: [],\n ...origState,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -498,7 +498,12 @@ var AgentNode = class extends RunnableCallable {
|
|
|
498
498
|
kwargs: { method: "json_schema" },
|
|
499
499
|
schema: structuredResponseFormat.strategy.schema
|
|
500
500
|
},
|
|
501
|
-
|
|
501
|
+
/**
|
|
502
|
+
* Don't force strict on tools: it makes Anthropic's combined grammar
|
|
503
|
+
* "too complex for compilation", and only OpenAI Chat Completions needs
|
|
504
|
+
* it (re-applied there). Honor an explicit override; else leave unset.
|
|
505
|
+
*/
|
|
506
|
+
strict: preparedOptions?.modelSettings?.strict
|
|
502
507
|
});
|
|
503
508
|
}
|
|
504
509
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentNode.js","names":["#run","#options","#systemMessage","#getResponseFormat","#invokeModel","#areMoreStepsNeeded","#deriveModel","#bindTools","#handleMultipleStructuredOutputs","#handleSingleStructuredOutput","#handleToolStrategyError"],"sources":["../../../src/agents/nodes/AgentNode.ts"],"sourcesContent":["/* oxlint-disable no-instanceof/no-instanceof */\nimport { Runnable, RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseMessage,\n AIMessage,\n ToolMessage,\n SystemMessage,\n} from \"@langchain/core/messages\";\nimport {\n Command,\n isCommand,\n type LangGraphRunnableConfig,\n} from \"@langchain/langgraph\";\nimport { type BaseChatModelCallOptions } from \"@langchain/core/language_models/chat_models\";\nimport {\n InteropZodObject,\n getSchemaDescription,\n interopParse,\n} from \"@langchain/core/utils/types\";\nimport { raceWithSignal } from \"@langchain/core/runnables\";\nimport type { ToolCall } from \"@langchain/core/messages/tool\";\nimport type { ClientTool, ServerTool } from \"@langchain/core/tools\";\n\nimport { initChatModel } from \"../../chat_models/universal.js\";\nimport {\n MultipleStructuredOutputsError,\n MiddlewareError,\n StructuredOutputParsingError,\n} from \"../errors.js\";\nimport { RunnableCallable } from \"../RunnableCallable.js\";\nimport type { AgentLanguageModelLike as LanguageModelLike } from \"../model.js\";\nimport {\n bindTools,\n validateLLMHasNoBoundTools,\n hasToolCalls,\n isClientTool,\n} from \"../utils.js\";\nimport { isConfigurableModel } from \"../model.js\";\nimport { mergeAbortSignals, toPartialZodObject } from \"../nodes/utils.js\";\nimport { CreateAgentParams } from \"../types.js\";\nimport type { InternalAgentState, Runtime } from \"../runtime.js\";\nimport type {\n AgentMiddleware,\n AnyAnnotationRoot,\n WrapModelCallHandler,\n} from \"../middleware/types.js\";\nimport type { ModelRequest } from \"./types.js\";\nimport { withAgentName } from \"../withAgentName.js\";\nimport {\n ToolStrategy,\n ProviderStrategy,\n transformResponseFormat,\n ToolStrategyError,\n type ResponseFormatInput,\n} from \"../responses.js\";\n\ntype ResponseHandlerResult<StructuredResponseFormat> =\n | {\n structuredResponse: StructuredResponseFormat;\n messages: BaseMessage[];\n }\n | Promise<Command>;\n\n/**\n * Wrap the base handler with middleware wrapModelCall hooks\n * Middleware are composed so the first middleware is the outermost wrapper\n * Example: [auth, retry, cache] means auth wraps retry wraps cache wraps baseHandler\n */\ntype InternalModelResponse<StructuredResponseFormat> =\n | AIMessage\n | ResponseHandlerResult<StructuredResponseFormat>\n | Command;\n\n/**\n * Check if the response is an internal model response.\n * @param response - The response to check.\n * @returns True if the response is an internal model response, false otherwise.\n */\nfunction isInternalModelResponse<StructuredResponseFormat>(\n response: unknown\n): response is InternalModelResponse<StructuredResponseFormat> {\n return (\n AIMessage.isInstance(response) ||\n isCommand(response) ||\n (typeof response === \"object\" &&\n response !== null &&\n \"structuredResponse\" in response &&\n \"messages\" in response)\n );\n}\n\n/**\n * The name of the agent node in the state graph.\n */\nexport const AGENT_NODE_NAME = \"model_request\";\n\nexport interface AgentNodeOptions<\n StructuredResponseFormat extends Record<string, unknown> = Record<\n string,\n unknown\n >,\n StateSchema extends AnyAnnotationRoot | InteropZodObject = AnyAnnotationRoot,\n ContextSchema extends AnyAnnotationRoot | InteropZodObject =\n AnyAnnotationRoot,\n> extends Pick<\n CreateAgentParams<StructuredResponseFormat, StateSchema, ContextSchema>,\n \"model\" | \"includeAgentName\" | \"name\" | \"responseFormat\" | \"middleware\"\n> {\n toolClasses: (ClientTool | ServerTool)[];\n shouldReturnDirect: Set<string>;\n signal?: AbortSignal;\n systemMessage: SystemMessage;\n wrapModelCallHookMiddleware?: (\n | AgentMiddleware\n | [AgentMiddleware, (...args: unknown[]) => Record<string, unknown>]\n )[];\n}\n\ninterface NativeResponseFormat {\n type: \"native\";\n strategy: ProviderStrategy;\n}\n\ninterface ToolResponseFormat {\n type: \"tool\";\n tools: Record<string, ToolStrategy>;\n}\n\ntype ResponseFormat = NativeResponseFormat | ToolResponseFormat;\n\nexport class AgentNode<\n StructuredResponseFormat extends Record<string, unknown> = Record<\n string,\n unknown\n >,\n ContextSchema extends AnyAnnotationRoot | InteropZodObject =\n AnyAnnotationRoot,\n> extends RunnableCallable<\n InternalAgentState<StructuredResponseFormat>,\n | Command[]\n | {\n messages: BaseMessage[];\n structuredResponse: StructuredResponseFormat;\n }\n> {\n #options: AgentNodeOptions<StructuredResponseFormat, ContextSchema>;\n #systemMessage: SystemMessage;\n\n constructor(\n options: AgentNodeOptions<StructuredResponseFormat, ContextSchema>\n ) {\n super({\n name: options.name ?? \"model\",\n func: (input, config) => this.#run(input, config as RunnableConfig),\n });\n\n this.#options = options;\n this.#systemMessage = options.systemMessage;\n }\n\n /**\n * Returns response format primtivies based on given model and response format provided by the user.\n *\n * If the user selects a tool output:\n * - return a record of tools to extract structured output from the model's response\n *\n * if the user selects a native schema output or if the model supports JSON schema output:\n * - return a provider strategy to extract structured output from the model's response\n *\n * @param model - The model to get the response format for.\n * @returns The response format.\n */\n async #getResponseFormat(\n model: string | LanguageModelLike,\n responseFormat: ResponseFormatInput | undefined = this.#options\n .responseFormat\n ): Promise<ResponseFormat | undefined> {\n if (!responseFormat) {\n return undefined;\n }\n\n let resolvedModel: LanguageModelLike | undefined;\n if (isConfigurableModel(model)) {\n resolvedModel = await (\n model as unknown as {\n _getModelInstance: () => Promise<LanguageModelLike>;\n }\n )._getModelInstance();\n } else if (typeof model !== \"string\") {\n resolvedModel = model;\n }\n\n const strategies = transformResponseFormat(\n responseFormat,\n undefined,\n resolvedModel\n );\n\n if (strategies.length === 0) {\n return undefined;\n }\n\n /**\n * we either define a list of provider strategies or a list of tool strategies\n */\n const isProviderStrategy = strategies.every(\n (format) => format instanceof ProviderStrategy\n );\n\n /**\n * Populate a list of structured tool info.\n */\n if (!isProviderStrategy) {\n return {\n type: \"tool\",\n tools: (\n strategies.filter(\n (format) => format instanceof ToolStrategy\n ) as ToolStrategy[]\n ).reduce(\n (acc, format) => {\n acc[format.name] = format;\n return acc;\n },\n {} as Record<string, ToolStrategy>\n ),\n };\n }\n\n return {\n type: \"native\",\n /**\n * there can only be one provider strategy\n */\n strategy: strategies[0] as ProviderStrategy,\n };\n }\n\n async #run(\n state: InternalAgentState<StructuredResponseFormat>,\n config: RunnableConfig\n ) {\n /**\n * Check if we just executed a returnDirect tool\n * If so, we should generate structured response (if needed) and stop\n */\n const lastMessage = state.messages.at(-1);\n if (\n lastMessage &&\n ToolMessage.isInstance(lastMessage) &&\n lastMessage.name &&\n this.#options.shouldReturnDirect.has(lastMessage.name)\n ) {\n return [new Command({ update: { messages: [] } })];\n }\n\n const { response, lastAiMessage, collectedCommands } =\n await this.#invokeModel(state, config);\n\n /**\n * structuredResponse — return as a plain state update dict (not a Command)\n * because the structuredResponse channel uses UntrackedValue(guard=true)\n * which only allows a single write per step.\n */\n if (\n typeof response === \"object\" &&\n response !== null &&\n \"structuredResponse\" in response &&\n \"messages\" in response\n ) {\n const { structuredResponse, messages } = response as {\n structuredResponse: StructuredResponseFormat;\n messages: BaseMessage[];\n };\n return {\n messages: [...state.messages, ...messages],\n structuredResponse,\n };\n }\n\n const commands: Command[] = [];\n const aiMessage: AIMessage | null = AIMessage.isInstance(response)\n ? response\n : lastAiMessage;\n\n // messages\n if (aiMessage) {\n aiMessage.name = this.name;\n aiMessage.lc_kwargs.name = this.name;\n\n if (this.#areMoreStepsNeeded(state, aiMessage)) {\n commands.push(\n new Command({\n update: {\n messages: [\n new AIMessage({\n content: \"Sorry, need more steps to process this request.\",\n name: this.name,\n id: aiMessage.id,\n }),\n ],\n },\n })\n );\n } else {\n commands.push(new Command({ update: { messages: [aiMessage] } }));\n }\n }\n\n // Commands (from base handler retries or middleware)\n if (isCommand(response) && !collectedCommands.includes(response)) {\n commands.push(response);\n }\n commands.push(...collectedCommands);\n\n return commands;\n }\n\n /**\n * Derive the model from the options.\n * @param state - The state of the agent.\n * @param config - The config of the agent.\n * @returns The model.\n */\n #deriveModel() {\n if (typeof this.#options.model === \"string\") {\n return initChatModel(this.#options.model);\n }\n\n if (this.#options.model) {\n return this.#options.model;\n }\n\n throw new Error(\"No model option was provided, either via `model` option.\");\n }\n\n async #invokeModel(\n state: InternalAgentState<StructuredResponseFormat>,\n config: RunnableConfig,\n options: {\n lastMessage?: string;\n } = {}\n ): Promise<{\n response: InternalModelResponse<StructuredResponseFormat>;\n lastAiMessage: AIMessage | null;\n collectedCommands: Command[];\n }> {\n const model = await this.#deriveModel();\n const lgConfig = config as LangGraphRunnableConfig;\n\n /**\n * Create a local variable for current system message to avoid concurrency issues\n * Each invocation gets its own copy\n */\n let currentSystemMessage = this.#systemMessage;\n\n /**\n * Shared tracking state for AIMessage and Command collection.\n * lastAiMessage tracks the effective AIMessage through the middleware chain.\n * collectedCommands accumulates Commands returned by middleware (not base handler).\n */\n let lastAiMessage: AIMessage | null = null;\n const collectedCommands: Command[] = [];\n\n /**\n * Create the base handler that performs the actual model invocation\n */\n const baseHandler = async (\n request: ModelRequest\n ): Promise<AIMessage | ResponseHandlerResult<StructuredResponseFormat>> => {\n /**\n * Check if the LLM already has bound tools and throw if it does.\n */\n validateLLMHasNoBoundTools(request.model);\n\n const structuredResponseFormat = await this.#getResponseFormat(\n request.model,\n request.responseFormat\n );\n const modelWithTools = await this.#bindTools(\n request.model,\n request,\n structuredResponseFormat\n );\n\n /**\n * prepend the system message to the messages if it is not empty\n */\n const messages = [\n ...(currentSystemMessage.text === \"\" ? [] : [currentSystemMessage]),\n ...request.messages,\n ];\n\n const signal = mergeAbortSignals(this.#options.signal, config.signal);\n const response = (await raceWithSignal(\n modelWithTools.invoke(messages, {\n ...config,\n signal,\n }),\n signal\n )) as AIMessage;\n\n lastAiMessage = response;\n\n /**\n * if the user requests a native schema output, try to parse the response\n * and return the structured response if it is valid\n */\n if (structuredResponseFormat?.type === \"native\") {\n const structuredResponse =\n structuredResponseFormat.strategy.parse(response);\n if (structuredResponse) {\n return { structuredResponse, messages: [response] };\n }\n\n /**\n * If the model produced a terminal response (no tool calls) but the\n * output failed to satisfy the provider strategy's schema, throw an\n * informative error instead of silently exiting with\n * `structuredResponse: undefined`. If tool calls are present, the\n * agent loop continues and a subsequent terminal step will get\n * another chance to produce a valid structured response.\n */\n if (!response.tool_calls || response.tool_calls.length === 0) {\n const schemaTitle =\n typeof structuredResponseFormat.strategy.schema?.title === \"string\"\n ? structuredResponseFormat.strategy.schema.title\n : \"providerStrategy\";\n throw new StructuredOutputParsingError(schemaTitle, [\n \"Model output did not satisfy the provided response schema.\",\n ]);\n }\n\n return response;\n }\n\n if (!structuredResponseFormat || !response.tool_calls) {\n return response;\n }\n\n const toolCalls = response.tool_calls.filter(\n (call) => call.name in structuredResponseFormat.tools\n );\n\n /**\n * if there were not structured tool calls, we can return the response\n */\n if (toolCalls.length === 0) {\n return response;\n }\n\n /**\n * if there were multiple structured tool calls, we should throw an error as this\n * scenario is not defined/supported.\n */\n if (toolCalls.length > 1) {\n return this.#handleMultipleStructuredOutputs(\n response,\n toolCalls,\n structuredResponseFormat\n );\n }\n\n const toolStrategy = structuredResponseFormat.tools[toolCalls[0].name];\n const toolMessageContent = toolStrategy?.options?.toolMessageContent;\n return this.#handleSingleStructuredOutput(\n response,\n toolCalls[0],\n structuredResponseFormat,\n toolMessageContent ?? options.lastMessage\n );\n };\n\n const wrapperMiddleware = this.#options.wrapModelCallHookMiddleware ?? [];\n let wrappedHandler: (\n request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ) => Promise<InternalModelResponse<StructuredResponseFormat>> = baseHandler;\n\n /**\n * Build composed handler from last to first so first middleware becomes outermost\n */\n for (let i = wrapperMiddleware.length - 1; i >= 0; i--) {\n const middlewareEntry = wrapperMiddleware[i];\n const middleware = Array.isArray(middlewareEntry)\n ? middlewareEntry[0]\n : middlewareEntry;\n if (middleware.wrapModelCall) {\n const innerHandler = wrappedHandler;\n const currentMiddleware = middleware;\n\n wrappedHandler = async (\n request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ): Promise<InternalModelResponse<StructuredResponseFormat>> => {\n const baselineSystemMessage = currentSystemMessage;\n\n /**\n * Merge context with default context of middleware\n */\n const context = currentMiddleware.contextSchema\n ? interopParse(\n currentMiddleware.contextSchema,\n lgConfig?.context || {}\n )\n : lgConfig?.context;\n\n /**\n * Create runtime\n */\n const runtime: Runtime<unknown> = Object.freeze({\n context,\n store: lgConfig.store,\n configurable: lgConfig.configurable,\n writer: lgConfig.writer,\n interrupt: lgConfig.interrupt,\n signal: lgConfig.signal,\n });\n\n /**\n * Create the request with state and runtime\n */\n const requestWithStateAndRuntime: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n > = {\n ...request,\n state: {\n ...(middleware.stateSchema\n ? interopParse(\n toPartialZodObject(middleware.stateSchema),\n state\n )\n : {}),\n messages: state.messages,\n } as InternalAgentState<StructuredResponseFormat>,\n runtime,\n };\n\n /**\n * Create handler that validates tools and calls the inner handler\n */\n const handlerWithValidation = async (\n req: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ): Promise<InternalModelResponse<StructuredResponseFormat>> => {\n currentSystemMessage = baselineSystemMessage;\n\n /**\n * Validate tool modifications in wrapModelCall.\n *\n * Classify each client tool as either:\n * - \"added\": a genuinely new tool name not in the static toolClasses\n * - \"replaced\": same name as a registered tool but different instance\n *\n * Added tools are allowed when a wrapToolCall middleware exists to\n * handle their execution. Replaced tools are always rejected to\n * preserve ToolNode execution identity.\n */\n const modifiedTools = req.tools ?? [];\n const registeredToolsByName = new Map(\n this.#options.toolClasses\n .filter(isClientTool)\n .map((t) => [t.name, t] as const)\n );\n\n const addedClientTools = modifiedTools.filter(\n (tool) =>\n isClientTool(tool) && !registeredToolsByName.has(tool.name)\n );\n\n const replacedClientTools = modifiedTools.filter((tool) => {\n if (!isClientTool(tool)) return false;\n const original = registeredToolsByName.get(tool.name);\n return original != null && original !== tool;\n });\n\n if (addedClientTools.length > 0) {\n const hasWrapToolCallHandler = this.#options.middleware?.some(\n (m) => m.wrapToolCall != null\n );\n if (!hasWrapToolCallHandler) {\n throw new Error(\n `You have added a new tool in \"wrapModelCall\" hook of middleware \"${\n currentMiddleware.name\n }\": ${addedClientTools\n .map((tool) => tool.name)\n .join(\n \", \"\n )}. This is not supported unless a middleware provides a \"wrapToolCall\" handler to execute it.`\n );\n }\n }\n\n if (replacedClientTools.length > 0) {\n throw new Error(\n `You have modified a tool in \"wrapModelCall\" hook of middleware \"${\n currentMiddleware.name\n }\": ${replacedClientTools\n .map((tool) => tool.name)\n .join(\", \")}. This is not supported.`\n );\n }\n\n let normalizedReq = req;\n const hasSystemPromptChanged =\n req.systemPrompt !== currentSystemMessage.text;\n const hasSystemMessageChanged =\n req.systemMessage !== currentSystemMessage;\n if (hasSystemPromptChanged && hasSystemMessageChanged) {\n throw new Error(\n \"Cannot change both systemPrompt and systemMessage in the same request.\"\n );\n }\n\n /**\n * Check if systemPrompt is a string was changed, if so create a new SystemMessage\n */\n if (hasSystemPromptChanged) {\n currentSystemMessage = new SystemMessage({\n content: [{ type: \"text\", text: req.systemPrompt }],\n });\n normalizedReq = {\n ...req,\n systemPrompt: currentSystemMessage.text,\n systemMessage: currentSystemMessage,\n };\n }\n /**\n * If the systemMessage was changed, update the current system message\n */\n if (hasSystemMessageChanged) {\n currentSystemMessage = new SystemMessage({\n ...req.systemMessage,\n });\n normalizedReq = {\n ...req,\n systemPrompt: currentSystemMessage.text,\n systemMessage: currentSystemMessage,\n };\n }\n\n const innerHandlerResult = await innerHandler(normalizedReq);\n\n /**\n * Normalize Commands so middleware always sees AIMessage from handler().\n * When an inner handler (base handler or nested middleware) returns a\n * Command (e.g. structured-output retry), substitute the tracked\n * lastAiMessage so the middleware sees an AIMessage, and collect the\n * raw Command so the framework can still propagate it (e.g. for retries).\n *\n * Only collect if not already present: Commands from inner middleware\n * are already tracked via the middleware validation layer (line ~627).\n */\n if (isCommand(innerHandlerResult) && lastAiMessage) {\n if (!collectedCommands.includes(innerHandlerResult)) {\n collectedCommands.push(innerHandlerResult);\n }\n return lastAiMessage as InternalModelResponse<StructuredResponseFormat>;\n }\n\n return innerHandlerResult;\n };\n\n // Call middleware's wrapModelCall with the validation handler\n if (!currentMiddleware.wrapModelCall) {\n return handlerWithValidation(requestWithStateAndRuntime);\n }\n\n try {\n const middlewareResponse = await currentMiddleware.wrapModelCall(\n requestWithStateAndRuntime,\n handlerWithValidation as WrapModelCallHandler\n );\n\n /**\n * Validate that this specific middleware returned a valid response\n */\n if (!isInternalModelResponse(middlewareResponse)) {\n throw new Error(\n `Invalid response from \"wrapModelCall\" in middleware \"${\n currentMiddleware.name\n }\": expected AIMessage or Command, got ${typeof middlewareResponse}`\n );\n }\n\n if (AIMessage.isInstance(middlewareResponse)) {\n lastAiMessage = middlewareResponse;\n } else if (isCommand(middlewareResponse)) {\n collectedCommands.push(middlewareResponse);\n }\n\n return middlewareResponse;\n } catch (error) {\n throw MiddlewareError.wrap(error, currentMiddleware.name);\n }\n };\n }\n }\n\n /**\n * Execute the wrapped handler with the initial request\n * Reset current system prompt to initial state and convert to string using .text getter\n * for backwards compatibility with ModelRequest\n */\n currentSystemMessage = this.#systemMessage;\n const initialRequest: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n > = {\n model,\n responseFormat: this.#options.responseFormat,\n systemPrompt: currentSystemMessage?.text,\n systemMessage: currentSystemMessage,\n messages: state.messages,\n tools: this.#options.toolClasses,\n state,\n runtime: Object.freeze({\n context: lgConfig?.context,\n store: lgConfig.store,\n configurable: lgConfig.configurable,\n writer: lgConfig.writer,\n interrupt: lgConfig.interrupt,\n signal: lgConfig.signal,\n }) as Runtime<unknown>,\n };\n\n const response = await wrappedHandler(initialRequest);\n return { response, lastAiMessage, collectedCommands };\n }\n\n /**\n * If the model returns multiple structured outputs, we need to handle it.\n * @param response - The response from the model\n * @param toolCalls - The tool calls that were made\n * @returns The response from the model\n */\n #handleMultipleStructuredOutputs(\n response: AIMessage,\n toolCalls: ToolCall[],\n responseFormat: ToolResponseFormat\n ): Promise<Command> {\n const multipleStructuredOutputsError = new MultipleStructuredOutputsError(\n toolCalls.map((call) => call.name)\n );\n\n return this.#handleToolStrategyError(\n multipleStructuredOutputsError,\n response,\n toolCalls[0],\n responseFormat\n );\n }\n\n /**\n * If the model returns a single structured output, we need to handle it.\n * @param toolCall - The tool call that was made\n * @returns The structured response and a message to the LLM if needed\n */\n #handleSingleStructuredOutput(\n response: AIMessage,\n toolCall: ToolCall,\n responseFormat: ToolResponseFormat,\n lastMessage?: string\n ): ResponseHandlerResult<StructuredResponseFormat> {\n const tool = responseFormat.tools[toolCall.name];\n\n try {\n const structuredResponse = tool.parse(\n toolCall.args\n ) as StructuredResponseFormat;\n\n return {\n structuredResponse,\n messages: [\n response,\n new ToolMessage({\n tool_call_id: toolCall.id ?? \"\",\n content: JSON.stringify(structuredResponse),\n name: toolCall.name,\n }),\n new AIMessage(\n lastMessage ??\n `Returning structured response: ${JSON.stringify(\n structuredResponse\n )}`\n ),\n ],\n };\n } catch (error) {\n return this.#handleToolStrategyError(\n error as ToolStrategyError,\n response,\n toolCall,\n responseFormat\n );\n }\n }\n\n async #handleToolStrategyError(\n error: ToolStrategyError,\n response: AIMessage,\n toolCall: ToolCall,\n responseFormat: ToolResponseFormat\n ): Promise<Command> {\n /**\n * Using the `errorHandler` option of the first `ToolStrategy` entry is sufficient here.\n * There is technically only one `ToolStrategy` entry in `structuredToolInfo` if the user\n * uses `toolStrategy` to define the response format. If the user applies a list of json\n * schema objects, these will be transformed into multiple `ToolStrategy` entries but all\n * with the same `handleError` option.\n */\n const errorHandler = Object.values(responseFormat.tools).at(0)?.options\n ?.handleError;\n\n const toolCallId = toolCall.id;\n if (!toolCallId) {\n throw new Error(\n \"Tool call ID is required to handle tool output errors. Please provide a tool call ID.\"\n );\n }\n\n /**\n * Default behavior: retry if `errorHandler` is undefined or truthy.\n * Only throw if explicitly set to `false`.\n */\n if (errorHandler === false) {\n throw error;\n }\n\n /**\n * retry if:\n */\n if (\n /**\n * if the user has provided truthy value as the `errorHandler`, return a new AIMessage\n * with the error message and retry the tool call.\n */\n errorHandler === undefined ||\n (typeof errorHandler === \"boolean\" && errorHandler) ||\n /**\n * if `errorHandler` is an array and contains MultipleStructuredOutputsError\n */\n (Array.isArray(errorHandler) &&\n errorHandler.some((h) => h instanceof MultipleStructuredOutputsError))\n ) {\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content: error.message,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n /**\n * if `errorHandler` is a string, retry the tool call with given string\n */\n if (typeof errorHandler === \"string\") {\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content: errorHandler,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n /**\n * if `errorHandler` is a function, retry the tool call with the function\n */\n if (typeof errorHandler === \"function\") {\n const content = await errorHandler(error);\n if (typeof content !== \"string\") {\n throw new Error(\"Error handler must return a string.\");\n }\n\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n /**\n * Default: retry if we reach here\n */\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content: error.message,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n #areMoreStepsNeeded(\n state: InternalAgentState<StructuredResponseFormat>,\n response: BaseMessage\n ): boolean {\n const allToolsReturnDirect =\n AIMessage.isInstance(response) &&\n response.tool_calls?.every((call) =>\n this.#options.shouldReturnDirect.has(call.name)\n );\n const remainingSteps =\n \"remainingSteps\" in state ? (state.remainingSteps as number) : undefined;\n return Boolean(\n remainingSteps &&\n ((remainingSteps < 1 && allToolsReturnDirect) ||\n (remainingSteps < 2 && hasToolCalls(state.messages.at(-1))))\n );\n }\n\n async #bindTools(\n model: LanguageModelLike,\n preparedOptions: ModelRequest | undefined,\n structuredResponseFormat: ResponseFormat | undefined\n ): Promise<LanguageModelLike | Runnable> {\n const options: Partial<BaseChatModelCallOptions> = {};\n const structuredTools = Object.values(\n structuredResponseFormat && \"tools\" in structuredResponseFormat\n ? structuredResponseFormat.tools\n : {}\n );\n\n /**\n * Use tools from preparedOptions if provided, otherwise use default tools\n */\n const allTools = [\n ...(preparedOptions?.tools ?? this.#options.toolClasses),\n ...structuredTools.map((toolStrategy) => toolStrategy.tool),\n ];\n\n /**\n * If there are structured tools, we need to set the tool choice to \"any\"\n * so that the model can choose to use a structured tool or not.\n */\n const toolChoice =\n preparedOptions?.toolChoice ||\n (structuredTools.length > 0 ? \"any\" : undefined);\n\n /**\n * check if the user requests a native schema output\n */\n if (structuredResponseFormat?.type === \"native\") {\n const resolvedStrict =\n preparedOptions?.modelSettings?.strict ??\n structuredResponseFormat?.strategy?.strict ??\n true;\n\n const jsonSchemaParams = {\n name: structuredResponseFormat.strategy.schema?.name ?? \"extract\",\n description: getSchemaDescription(\n structuredResponseFormat.strategy.schema\n ),\n schema: structuredResponseFormat.strategy.schema,\n strict: resolvedStrict,\n };\n\n Object.assign(options, {\n /**\n * OpenAI-style options\n * Used by ChatOpenAI, ChatXAI, and other OpenAI-compatible providers.\n */\n response_format: {\n type: \"json_schema\",\n json_schema: jsonSchemaParams,\n },\n\n /**\n * Anthropic-style options\n */\n outputConfig: {\n format: {\n type: \"json_schema\",\n schema: structuredResponseFormat.strategy.schema,\n },\n },\n\n /**\n * Google-style options\n * Used by ChatGoogle and other Gemini-based providers.\n */\n responseSchema: structuredResponseFormat.strategy.schema,\n\n /**\n * for LangSmith structured output tracing\n */\n ls_structured_output_format: {\n kwargs: { method: \"json_schema\" },\n schema: structuredResponseFormat.strategy.schema,\n },\n strict: resolvedStrict,\n });\n }\n\n /**\n * Bind tools to the model if they are not already bound.\n */\n const modelWithTools = await bindTools(model, allTools, {\n ...options,\n ...preparedOptions?.modelSettings,\n tool_choice: toolChoice,\n });\n\n /**\n * Create a model runnable with the prompt and agent name\n * Use current SystemMessage state (which may have been modified by middleware)\n */\n const modelRunnable =\n this.#options.includeAgentName === \"inline\"\n ? withAgentName(modelWithTools, this.#options.includeAgentName)\n : modelWithTools;\n\n return modelRunnable;\n }\n\n /**\n * Returns internal bookkeeping state for StateManager, not graph output.\n * The return shape differs from the node's output type (Command).\n */\n // @ts-expect-error Internal state shape differs from graph output type\n getState(): { messages: BaseMessage[] } {\n const state = super.getState();\n const origState = state && !isCommand(state) ? state : {};\n\n return {\n messages: [],\n ...origState,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentNode.js","names":["#run","#options","#systemMessage","#getResponseFormat","#invokeModel","#areMoreStepsNeeded","#deriveModel","#bindTools","#handleMultipleStructuredOutputs","#handleSingleStructuredOutput","#handleToolStrategyError"],"sources":["../../../src/agents/nodes/AgentNode.ts"],"sourcesContent":["/* oxlint-disable no-instanceof/no-instanceof */\nimport { Runnable, RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseMessage,\n AIMessage,\n ToolMessage,\n SystemMessage,\n} from \"@langchain/core/messages\";\nimport {\n Command,\n isCommand,\n type LangGraphRunnableConfig,\n} from \"@langchain/langgraph\";\nimport { type BaseChatModelCallOptions } from \"@langchain/core/language_models/chat_models\";\nimport {\n InteropZodObject,\n getSchemaDescription,\n interopParse,\n} from \"@langchain/core/utils/types\";\nimport { raceWithSignal } from \"@langchain/core/runnables\";\nimport type { ToolCall } from \"@langchain/core/messages/tool\";\nimport type { ClientTool, ServerTool } from \"@langchain/core/tools\";\n\nimport { initChatModel } from \"../../chat_models/universal.js\";\nimport {\n MultipleStructuredOutputsError,\n MiddlewareError,\n StructuredOutputParsingError,\n} from \"../errors.js\";\nimport { RunnableCallable } from \"../RunnableCallable.js\";\nimport type { AgentLanguageModelLike as LanguageModelLike } from \"../model.js\";\nimport {\n bindTools,\n validateLLMHasNoBoundTools,\n hasToolCalls,\n isClientTool,\n} from \"../utils.js\";\nimport { isConfigurableModel } from \"../model.js\";\nimport { mergeAbortSignals, toPartialZodObject } from \"../nodes/utils.js\";\nimport { CreateAgentParams } from \"../types.js\";\nimport type { InternalAgentState, Runtime } from \"../runtime.js\";\nimport type {\n AgentMiddleware,\n AnyAnnotationRoot,\n WrapModelCallHandler,\n} from \"../middleware/types.js\";\nimport type { ModelRequest } from \"./types.js\";\nimport { withAgentName } from \"../withAgentName.js\";\nimport {\n ToolStrategy,\n ProviderStrategy,\n transformResponseFormat,\n ToolStrategyError,\n type ResponseFormatInput,\n} from \"../responses.js\";\n\ntype ResponseHandlerResult<StructuredResponseFormat> =\n | {\n structuredResponse: StructuredResponseFormat;\n messages: BaseMessage[];\n }\n | Promise<Command>;\n\n/**\n * Wrap the base handler with middleware wrapModelCall hooks\n * Middleware are composed so the first middleware is the outermost wrapper\n * Example: [auth, retry, cache] means auth wraps retry wraps cache wraps baseHandler\n */\ntype InternalModelResponse<StructuredResponseFormat> =\n | AIMessage\n | ResponseHandlerResult<StructuredResponseFormat>\n | Command;\n\n/**\n * Check if the response is an internal model response.\n * @param response - The response to check.\n * @returns True if the response is an internal model response, false otherwise.\n */\nfunction isInternalModelResponse<StructuredResponseFormat>(\n response: unknown\n): response is InternalModelResponse<StructuredResponseFormat> {\n return (\n AIMessage.isInstance(response) ||\n isCommand(response) ||\n (typeof response === \"object\" &&\n response !== null &&\n \"structuredResponse\" in response &&\n \"messages\" in response)\n );\n}\n\n/**\n * The name of the agent node in the state graph.\n */\nexport const AGENT_NODE_NAME = \"model_request\";\n\nexport interface AgentNodeOptions<\n StructuredResponseFormat extends Record<string, unknown> = Record<\n string,\n unknown\n >,\n StateSchema extends AnyAnnotationRoot | InteropZodObject = AnyAnnotationRoot,\n ContextSchema extends AnyAnnotationRoot | InteropZodObject =\n AnyAnnotationRoot,\n> extends Pick<\n CreateAgentParams<StructuredResponseFormat, StateSchema, ContextSchema>,\n \"model\" | \"includeAgentName\" | \"name\" | \"responseFormat\" | \"middleware\"\n> {\n toolClasses: (ClientTool | ServerTool)[];\n shouldReturnDirect: Set<string>;\n signal?: AbortSignal;\n systemMessage: SystemMessage;\n wrapModelCallHookMiddleware?: (\n | AgentMiddleware\n | [AgentMiddleware, (...args: unknown[]) => Record<string, unknown>]\n )[];\n}\n\ninterface NativeResponseFormat {\n type: \"native\";\n strategy: ProviderStrategy;\n}\n\ninterface ToolResponseFormat {\n type: \"tool\";\n tools: Record<string, ToolStrategy>;\n}\n\ntype ResponseFormat = NativeResponseFormat | ToolResponseFormat;\n\nexport class AgentNode<\n StructuredResponseFormat extends Record<string, unknown> = Record<\n string,\n unknown\n >,\n ContextSchema extends AnyAnnotationRoot | InteropZodObject =\n AnyAnnotationRoot,\n> extends RunnableCallable<\n InternalAgentState<StructuredResponseFormat>,\n | Command[]\n | {\n messages: BaseMessage[];\n structuredResponse: StructuredResponseFormat;\n }\n> {\n #options: AgentNodeOptions<StructuredResponseFormat, ContextSchema>;\n #systemMessage: SystemMessage;\n\n constructor(\n options: AgentNodeOptions<StructuredResponseFormat, ContextSchema>\n ) {\n super({\n name: options.name ?? \"model\",\n func: (input, config) => this.#run(input, config as RunnableConfig),\n });\n\n this.#options = options;\n this.#systemMessage = options.systemMessage;\n }\n\n /**\n * Returns response format primtivies based on given model and response format provided by the user.\n *\n * If the user selects a tool output:\n * - return a record of tools to extract structured output from the model's response\n *\n * if the user selects a native schema output or if the model supports JSON schema output:\n * - return a provider strategy to extract structured output from the model's response\n *\n * @param model - The model to get the response format for.\n * @returns The response format.\n */\n async #getResponseFormat(\n model: string | LanguageModelLike,\n responseFormat: ResponseFormatInput | undefined = this.#options\n .responseFormat\n ): Promise<ResponseFormat | undefined> {\n if (!responseFormat) {\n return undefined;\n }\n\n let resolvedModel: LanguageModelLike | undefined;\n if (isConfigurableModel(model)) {\n resolvedModel = await (\n model as unknown as {\n _getModelInstance: () => Promise<LanguageModelLike>;\n }\n )._getModelInstance();\n } else if (typeof model !== \"string\") {\n resolvedModel = model;\n }\n\n const strategies = transformResponseFormat(\n responseFormat,\n undefined,\n resolvedModel\n );\n\n if (strategies.length === 0) {\n return undefined;\n }\n\n /**\n * we either define a list of provider strategies or a list of tool strategies\n */\n const isProviderStrategy = strategies.every(\n (format) => format instanceof ProviderStrategy\n );\n\n /**\n * Populate a list of structured tool info.\n */\n if (!isProviderStrategy) {\n return {\n type: \"tool\",\n tools: (\n strategies.filter(\n (format) => format instanceof ToolStrategy\n ) as ToolStrategy[]\n ).reduce(\n (acc, format) => {\n acc[format.name] = format;\n return acc;\n },\n {} as Record<string, ToolStrategy>\n ),\n };\n }\n\n return {\n type: \"native\",\n /**\n * there can only be one provider strategy\n */\n strategy: strategies[0] as ProviderStrategy,\n };\n }\n\n async #run(\n state: InternalAgentState<StructuredResponseFormat>,\n config: RunnableConfig\n ) {\n /**\n * Check if we just executed a returnDirect tool\n * If so, we should generate structured response (if needed) and stop\n */\n const lastMessage = state.messages.at(-1);\n if (\n lastMessage &&\n ToolMessage.isInstance(lastMessage) &&\n lastMessage.name &&\n this.#options.shouldReturnDirect.has(lastMessage.name)\n ) {\n return [new Command({ update: { messages: [] } })];\n }\n\n const { response, lastAiMessage, collectedCommands } =\n await this.#invokeModel(state, config);\n\n /**\n * structuredResponse — return as a plain state update dict (not a Command)\n * because the structuredResponse channel uses UntrackedValue(guard=true)\n * which only allows a single write per step.\n */\n if (\n typeof response === \"object\" &&\n response !== null &&\n \"structuredResponse\" in response &&\n \"messages\" in response\n ) {\n const { structuredResponse, messages } = response as {\n structuredResponse: StructuredResponseFormat;\n messages: BaseMessage[];\n };\n return {\n messages: [...state.messages, ...messages],\n structuredResponse,\n };\n }\n\n const commands: Command[] = [];\n const aiMessage: AIMessage | null = AIMessage.isInstance(response)\n ? response\n : lastAiMessage;\n\n // messages\n if (aiMessage) {\n aiMessage.name = this.name;\n aiMessage.lc_kwargs.name = this.name;\n\n if (this.#areMoreStepsNeeded(state, aiMessage)) {\n commands.push(\n new Command({\n update: {\n messages: [\n new AIMessage({\n content: \"Sorry, need more steps to process this request.\",\n name: this.name,\n id: aiMessage.id,\n }),\n ],\n },\n })\n );\n } else {\n commands.push(new Command({ update: { messages: [aiMessage] } }));\n }\n }\n\n // Commands (from base handler retries or middleware)\n if (isCommand(response) && !collectedCommands.includes(response)) {\n commands.push(response);\n }\n commands.push(...collectedCommands);\n\n return commands;\n }\n\n /**\n * Derive the model from the options.\n * @param state - The state of the agent.\n * @param config - The config of the agent.\n * @returns The model.\n */\n #deriveModel() {\n if (typeof this.#options.model === \"string\") {\n return initChatModel(this.#options.model);\n }\n\n if (this.#options.model) {\n return this.#options.model;\n }\n\n throw new Error(\"No model option was provided, either via `model` option.\");\n }\n\n async #invokeModel(\n state: InternalAgentState<StructuredResponseFormat>,\n config: RunnableConfig,\n options: {\n lastMessage?: string;\n } = {}\n ): Promise<{\n response: InternalModelResponse<StructuredResponseFormat>;\n lastAiMessage: AIMessage | null;\n collectedCommands: Command[];\n }> {\n const model = await this.#deriveModel();\n const lgConfig = config as LangGraphRunnableConfig;\n\n /**\n * Create a local variable for current system message to avoid concurrency issues\n * Each invocation gets its own copy\n */\n let currentSystemMessage = this.#systemMessage;\n\n /**\n * Shared tracking state for AIMessage and Command collection.\n * lastAiMessage tracks the effective AIMessage through the middleware chain.\n * collectedCommands accumulates Commands returned by middleware (not base handler).\n */\n let lastAiMessage: AIMessage | null = null;\n const collectedCommands: Command[] = [];\n\n /**\n * Create the base handler that performs the actual model invocation\n */\n const baseHandler = async (\n request: ModelRequest\n ): Promise<AIMessage | ResponseHandlerResult<StructuredResponseFormat>> => {\n /**\n * Check if the LLM already has bound tools and throw if it does.\n */\n validateLLMHasNoBoundTools(request.model);\n\n const structuredResponseFormat = await this.#getResponseFormat(\n request.model,\n request.responseFormat\n );\n const modelWithTools = await this.#bindTools(\n request.model,\n request,\n structuredResponseFormat\n );\n\n /**\n * prepend the system message to the messages if it is not empty\n */\n const messages = [\n ...(currentSystemMessage.text === \"\" ? [] : [currentSystemMessage]),\n ...request.messages,\n ];\n\n const signal = mergeAbortSignals(this.#options.signal, config.signal);\n const response = (await raceWithSignal(\n modelWithTools.invoke(messages, {\n ...config,\n signal,\n }),\n signal\n )) as AIMessage;\n\n lastAiMessage = response;\n\n /**\n * if the user requests a native schema output, try to parse the response\n * and return the structured response if it is valid\n */\n if (structuredResponseFormat?.type === \"native\") {\n const structuredResponse =\n structuredResponseFormat.strategy.parse(response);\n if (structuredResponse) {\n return { structuredResponse, messages: [response] };\n }\n\n /**\n * If the model produced a terminal response (no tool calls) but the\n * output failed to satisfy the provider strategy's schema, throw an\n * informative error instead of silently exiting with\n * `structuredResponse: undefined`. If tool calls are present, the\n * agent loop continues and a subsequent terminal step will get\n * another chance to produce a valid structured response.\n */\n if (!response.tool_calls || response.tool_calls.length === 0) {\n const schemaTitle =\n typeof structuredResponseFormat.strategy.schema?.title === \"string\"\n ? structuredResponseFormat.strategy.schema.title\n : \"providerStrategy\";\n throw new StructuredOutputParsingError(schemaTitle, [\n \"Model output did not satisfy the provided response schema.\",\n ]);\n }\n\n return response;\n }\n\n if (!structuredResponseFormat || !response.tool_calls) {\n return response;\n }\n\n const toolCalls = response.tool_calls.filter(\n (call) => call.name in structuredResponseFormat.tools\n );\n\n /**\n * if there were not structured tool calls, we can return the response\n */\n if (toolCalls.length === 0) {\n return response;\n }\n\n /**\n * if there were multiple structured tool calls, we should throw an error as this\n * scenario is not defined/supported.\n */\n if (toolCalls.length > 1) {\n return this.#handleMultipleStructuredOutputs(\n response,\n toolCalls,\n structuredResponseFormat\n );\n }\n\n const toolStrategy = structuredResponseFormat.tools[toolCalls[0].name];\n const toolMessageContent = toolStrategy?.options?.toolMessageContent;\n return this.#handleSingleStructuredOutput(\n response,\n toolCalls[0],\n structuredResponseFormat,\n toolMessageContent ?? options.lastMessage\n );\n };\n\n const wrapperMiddleware = this.#options.wrapModelCallHookMiddleware ?? [];\n let wrappedHandler: (\n request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ) => Promise<InternalModelResponse<StructuredResponseFormat>> = baseHandler;\n\n /**\n * Build composed handler from last to first so first middleware becomes outermost\n */\n for (let i = wrapperMiddleware.length - 1; i >= 0; i--) {\n const middlewareEntry = wrapperMiddleware[i];\n const middleware = Array.isArray(middlewareEntry)\n ? middlewareEntry[0]\n : middlewareEntry;\n if (middleware.wrapModelCall) {\n const innerHandler = wrappedHandler;\n const currentMiddleware = middleware;\n\n wrappedHandler = async (\n request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ): Promise<InternalModelResponse<StructuredResponseFormat>> => {\n const baselineSystemMessage = currentSystemMessage;\n\n /**\n * Merge context with default context of middleware\n */\n const context = currentMiddleware.contextSchema\n ? interopParse(\n currentMiddleware.contextSchema,\n lgConfig?.context || {}\n )\n : lgConfig?.context;\n\n /**\n * Create runtime\n */\n const runtime: Runtime<unknown> = Object.freeze({\n context,\n store: lgConfig.store,\n configurable: lgConfig.configurable,\n writer: lgConfig.writer,\n interrupt: lgConfig.interrupt,\n signal: lgConfig.signal,\n });\n\n /**\n * Create the request with state and runtime\n */\n const requestWithStateAndRuntime: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n > = {\n ...request,\n state: {\n ...(middleware.stateSchema\n ? interopParse(\n toPartialZodObject(middleware.stateSchema),\n state\n )\n : {}),\n messages: state.messages,\n } as InternalAgentState<StructuredResponseFormat>,\n runtime,\n };\n\n /**\n * Create handler that validates tools and calls the inner handler\n */\n const handlerWithValidation = async (\n req: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ): Promise<InternalModelResponse<StructuredResponseFormat>> => {\n currentSystemMessage = baselineSystemMessage;\n\n /**\n * Validate tool modifications in wrapModelCall.\n *\n * Classify each client tool as either:\n * - \"added\": a genuinely new tool name not in the static toolClasses\n * - \"replaced\": same name as a registered tool but different instance\n *\n * Added tools are allowed when a wrapToolCall middleware exists to\n * handle their execution. Replaced tools are always rejected to\n * preserve ToolNode execution identity.\n */\n const modifiedTools = req.tools ?? [];\n const registeredToolsByName = new Map(\n this.#options.toolClasses\n .filter(isClientTool)\n .map((t) => [t.name, t] as const)\n );\n\n const addedClientTools = modifiedTools.filter(\n (tool) =>\n isClientTool(tool) && !registeredToolsByName.has(tool.name)\n );\n\n const replacedClientTools = modifiedTools.filter((tool) => {\n if (!isClientTool(tool)) return false;\n const original = registeredToolsByName.get(tool.name);\n return original != null && original !== tool;\n });\n\n if (addedClientTools.length > 0) {\n const hasWrapToolCallHandler = this.#options.middleware?.some(\n (m) => m.wrapToolCall != null\n );\n if (!hasWrapToolCallHandler) {\n throw new Error(\n `You have added a new tool in \"wrapModelCall\" hook of middleware \"${\n currentMiddleware.name\n }\": ${addedClientTools\n .map((tool) => tool.name)\n .join(\n \", \"\n )}. This is not supported unless a middleware provides a \"wrapToolCall\" handler to execute it.`\n );\n }\n }\n\n if (replacedClientTools.length > 0) {\n throw new Error(\n `You have modified a tool in \"wrapModelCall\" hook of middleware \"${\n currentMiddleware.name\n }\": ${replacedClientTools\n .map((tool) => tool.name)\n .join(\", \")}. This is not supported.`\n );\n }\n\n let normalizedReq = req;\n const hasSystemPromptChanged =\n req.systemPrompt !== currentSystemMessage.text;\n const hasSystemMessageChanged =\n req.systemMessage !== currentSystemMessage;\n if (hasSystemPromptChanged && hasSystemMessageChanged) {\n throw new Error(\n \"Cannot change both systemPrompt and systemMessage in the same request.\"\n );\n }\n\n /**\n * Check if systemPrompt is a string was changed, if so create a new SystemMessage\n */\n if (hasSystemPromptChanged) {\n currentSystemMessage = new SystemMessage({\n content: [{ type: \"text\", text: req.systemPrompt }],\n });\n normalizedReq = {\n ...req,\n systemPrompt: currentSystemMessage.text,\n systemMessage: currentSystemMessage,\n };\n }\n /**\n * If the systemMessage was changed, update the current system message\n */\n if (hasSystemMessageChanged) {\n currentSystemMessage = new SystemMessage({\n ...req.systemMessage,\n });\n normalizedReq = {\n ...req,\n systemPrompt: currentSystemMessage.text,\n systemMessage: currentSystemMessage,\n };\n }\n\n const innerHandlerResult = await innerHandler(normalizedReq);\n\n /**\n * Normalize Commands so middleware always sees AIMessage from handler().\n * When an inner handler (base handler or nested middleware) returns a\n * Command (e.g. structured-output retry), substitute the tracked\n * lastAiMessage so the middleware sees an AIMessage, and collect the\n * raw Command so the framework can still propagate it (e.g. for retries).\n *\n * Only collect if not already present: Commands from inner middleware\n * are already tracked via the middleware validation layer (line ~627).\n */\n if (isCommand(innerHandlerResult) && lastAiMessage) {\n if (!collectedCommands.includes(innerHandlerResult)) {\n collectedCommands.push(innerHandlerResult);\n }\n return lastAiMessage as InternalModelResponse<StructuredResponseFormat>;\n }\n\n return innerHandlerResult;\n };\n\n // Call middleware's wrapModelCall with the validation handler\n if (!currentMiddleware.wrapModelCall) {\n return handlerWithValidation(requestWithStateAndRuntime);\n }\n\n try {\n const middlewareResponse = await currentMiddleware.wrapModelCall(\n requestWithStateAndRuntime,\n handlerWithValidation as WrapModelCallHandler\n );\n\n /**\n * Validate that this specific middleware returned a valid response\n */\n if (!isInternalModelResponse(middlewareResponse)) {\n throw new Error(\n `Invalid response from \"wrapModelCall\" in middleware \"${\n currentMiddleware.name\n }\": expected AIMessage or Command, got ${typeof middlewareResponse}`\n );\n }\n\n if (AIMessage.isInstance(middlewareResponse)) {\n lastAiMessage = middlewareResponse;\n } else if (isCommand(middlewareResponse)) {\n collectedCommands.push(middlewareResponse);\n }\n\n return middlewareResponse;\n } catch (error) {\n throw MiddlewareError.wrap(error, currentMiddleware.name);\n }\n };\n }\n }\n\n /**\n * Execute the wrapped handler with the initial request\n * Reset current system prompt to initial state and convert to string using .text getter\n * for backwards compatibility with ModelRequest\n */\n currentSystemMessage = this.#systemMessage;\n const initialRequest: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n > = {\n model,\n responseFormat: this.#options.responseFormat,\n systemPrompt: currentSystemMessage?.text,\n systemMessage: currentSystemMessage,\n messages: state.messages,\n tools: this.#options.toolClasses,\n state,\n runtime: Object.freeze({\n context: lgConfig?.context,\n store: lgConfig.store,\n configurable: lgConfig.configurable,\n writer: lgConfig.writer,\n interrupt: lgConfig.interrupt,\n signal: lgConfig.signal,\n }) as Runtime<unknown>,\n };\n\n const response = await wrappedHandler(initialRequest);\n return { response, lastAiMessage, collectedCommands };\n }\n\n /**\n * If the model returns multiple structured outputs, we need to handle it.\n * @param response - The response from the model\n * @param toolCalls - The tool calls that were made\n * @returns The response from the model\n */\n #handleMultipleStructuredOutputs(\n response: AIMessage,\n toolCalls: ToolCall[],\n responseFormat: ToolResponseFormat\n ): Promise<Command> {\n const multipleStructuredOutputsError = new MultipleStructuredOutputsError(\n toolCalls.map((call) => call.name)\n );\n\n return this.#handleToolStrategyError(\n multipleStructuredOutputsError,\n response,\n toolCalls[0],\n responseFormat\n );\n }\n\n /**\n * If the model returns a single structured output, we need to handle it.\n * @param toolCall - The tool call that was made\n * @returns The structured response and a message to the LLM if needed\n */\n #handleSingleStructuredOutput(\n response: AIMessage,\n toolCall: ToolCall,\n responseFormat: ToolResponseFormat,\n lastMessage?: string\n ): ResponseHandlerResult<StructuredResponseFormat> {\n const tool = responseFormat.tools[toolCall.name];\n\n try {\n const structuredResponse = tool.parse(\n toolCall.args\n ) as StructuredResponseFormat;\n\n return {\n structuredResponse,\n messages: [\n response,\n new ToolMessage({\n tool_call_id: toolCall.id ?? \"\",\n content: JSON.stringify(structuredResponse),\n name: toolCall.name,\n }),\n new AIMessage(\n lastMessage ??\n `Returning structured response: ${JSON.stringify(\n structuredResponse\n )}`\n ),\n ],\n };\n } catch (error) {\n return this.#handleToolStrategyError(\n error as ToolStrategyError,\n response,\n toolCall,\n responseFormat\n );\n }\n }\n\n async #handleToolStrategyError(\n error: ToolStrategyError,\n response: AIMessage,\n toolCall: ToolCall,\n responseFormat: ToolResponseFormat\n ): Promise<Command> {\n /**\n * Using the `errorHandler` option of the first `ToolStrategy` entry is sufficient here.\n * There is technically only one `ToolStrategy` entry in `structuredToolInfo` if the user\n * uses `toolStrategy` to define the response format. If the user applies a list of json\n * schema objects, these will be transformed into multiple `ToolStrategy` entries but all\n * with the same `handleError` option.\n */\n const errorHandler = Object.values(responseFormat.tools).at(0)?.options\n ?.handleError;\n\n const toolCallId = toolCall.id;\n if (!toolCallId) {\n throw new Error(\n \"Tool call ID is required to handle tool output errors. Please provide a tool call ID.\"\n );\n }\n\n /**\n * Default behavior: retry if `errorHandler` is undefined or truthy.\n * Only throw if explicitly set to `false`.\n */\n if (errorHandler === false) {\n throw error;\n }\n\n /**\n * retry if:\n */\n if (\n /**\n * if the user has provided truthy value as the `errorHandler`, return a new AIMessage\n * with the error message and retry the tool call.\n */\n errorHandler === undefined ||\n (typeof errorHandler === \"boolean\" && errorHandler) ||\n /**\n * if `errorHandler` is an array and contains MultipleStructuredOutputsError\n */\n (Array.isArray(errorHandler) &&\n errorHandler.some((h) => h instanceof MultipleStructuredOutputsError))\n ) {\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content: error.message,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n /**\n * if `errorHandler` is a string, retry the tool call with given string\n */\n if (typeof errorHandler === \"string\") {\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content: errorHandler,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n /**\n * if `errorHandler` is a function, retry the tool call with the function\n */\n if (typeof errorHandler === \"function\") {\n const content = await errorHandler(error);\n if (typeof content !== \"string\") {\n throw new Error(\"Error handler must return a string.\");\n }\n\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n /**\n * Default: retry if we reach here\n */\n return new Command({\n update: {\n messages: [\n response,\n new ToolMessage({\n content: error.message,\n tool_call_id: toolCallId,\n }),\n ],\n },\n goto: AGENT_NODE_NAME,\n });\n }\n\n #areMoreStepsNeeded(\n state: InternalAgentState<StructuredResponseFormat>,\n response: BaseMessage\n ): boolean {\n const allToolsReturnDirect =\n AIMessage.isInstance(response) &&\n response.tool_calls?.every((call) =>\n this.#options.shouldReturnDirect.has(call.name)\n );\n const remainingSteps =\n \"remainingSteps\" in state ? (state.remainingSteps as number) : undefined;\n return Boolean(\n remainingSteps &&\n ((remainingSteps < 1 && allToolsReturnDirect) ||\n (remainingSteps < 2 && hasToolCalls(state.messages.at(-1))))\n );\n }\n\n async #bindTools(\n model: LanguageModelLike,\n preparedOptions: ModelRequest | undefined,\n structuredResponseFormat: ResponseFormat | undefined\n ): Promise<LanguageModelLike | Runnable> {\n const options: Partial<BaseChatModelCallOptions> = {};\n const structuredTools = Object.values(\n structuredResponseFormat && \"tools\" in structuredResponseFormat\n ? structuredResponseFormat.tools\n : {}\n );\n\n /**\n * Use tools from preparedOptions if provided, otherwise use default tools\n */\n const allTools = [\n ...(preparedOptions?.tools ?? this.#options.toolClasses),\n ...structuredTools.map((toolStrategy) => toolStrategy.tool),\n ];\n\n /**\n * If there are structured tools, we need to set the tool choice to \"any\"\n * so that the model can choose to use a structured tool or not.\n */\n const toolChoice =\n preparedOptions?.toolChoice ||\n (structuredTools.length > 0 ? \"any\" : undefined);\n\n /**\n * check if the user requests a native schema output\n */\n if (structuredResponseFormat?.type === \"native\") {\n const resolvedStrict =\n preparedOptions?.modelSettings?.strict ??\n structuredResponseFormat?.strategy?.strict ??\n true;\n\n const jsonSchemaParams = {\n name: structuredResponseFormat.strategy.schema?.name ?? \"extract\",\n description: getSchemaDescription(\n structuredResponseFormat.strategy.schema\n ),\n schema: structuredResponseFormat.strategy.schema,\n strict: resolvedStrict,\n };\n\n Object.assign(options, {\n /**\n * OpenAI-style options\n * Used by ChatOpenAI, ChatXAI, and other OpenAI-compatible providers.\n */\n response_format: {\n type: \"json_schema\",\n json_schema: jsonSchemaParams,\n },\n\n /**\n * Anthropic-style options\n */\n outputConfig: {\n format: {\n type: \"json_schema\",\n schema: structuredResponseFormat.strategy.schema,\n },\n },\n\n /**\n * Google-style options\n * Used by ChatGoogle and other Gemini-based providers.\n */\n responseSchema: structuredResponseFormat.strategy.schema,\n\n /**\n * for LangSmith structured output tracing\n */\n ls_structured_output_format: {\n kwargs: { method: \"json_schema\" },\n schema: structuredResponseFormat.strategy.schema,\n },\n\n /**\n * Don't force strict on tools: it makes Anthropic's combined grammar\n * \"too complex for compilation\", and only OpenAI Chat Completions needs\n * it (re-applied there). Honor an explicit override; else leave unset.\n */\n strict: preparedOptions?.modelSettings?.strict,\n });\n }\n\n /**\n * Bind tools to the model if they are not already bound.\n */\n const modelWithTools = await bindTools(model, allTools, {\n ...options,\n ...preparedOptions?.modelSettings,\n tool_choice: toolChoice,\n });\n\n /**\n * Create a model runnable with the prompt and agent name\n * Use current SystemMessage state (which may have been modified by middleware)\n */\n const modelRunnable =\n this.#options.includeAgentName === \"inline\"\n ? withAgentName(modelWithTools, this.#options.includeAgentName)\n : modelWithTools;\n\n return modelRunnable;\n }\n\n /**\n * Returns internal bookkeeping state for StateManager, not graph output.\n * The return shape differs from the node's output type (Command).\n */\n // @ts-expect-error Internal state shape differs from graph output type\n getState(): { messages: BaseMessage[] } {\n const state = super.getState();\n const origState = state && !isCommand(state) ? state : {};\n\n return {\n messages: [],\n ...origState,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -21,16 +21,23 @@ function isOwnEvent(ns, path) {
|
|
|
21
21
|
* than a genuine tool failure.
|
|
22
22
|
*
|
|
23
23
|
* A tool that calls `interrupt()` throws a `GraphInterrupt`, whose message is
|
|
24
|
-
* the JSON-serialized `Interrupt[]` array
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* the JSON-serialized `Interrupt[]` array. Each entry has the LangGraph
|
|
25
|
+
* `Interrupt` shape `{ id, value }`: a stable `id` (a hash of the checkpoint
|
|
26
|
+
* namespace, generated by `interrupt()` and always present during graph
|
|
27
|
+
* execution) plus the `value` passed to `interrupt(...)`. We require BOTH a
|
|
28
|
+
* string `id` and a `value` on every entry — a bare `value` is not a reliable
|
|
29
|
+
* discriminator, since a genuine tool error message can also be a JSON array
|
|
30
|
+
* of `{ value }` records (e.g. a validator emitting
|
|
31
|
+
* `[{ "value": "bad input", "message": "invalid" }]`). Keying off the
|
|
32
|
+
* interrupt `id` keeps real tool failures on the error path.
|
|
28
33
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
+
* An interrupt is control flow that *suspends* the run (the tool re-runs on
|
|
35
|
+
* resume); it is not an error, so the tool call must stay pending rather than
|
|
36
|
+
* have its `output` promise rejected. Any interrupt qualifies regardless of
|
|
37
|
+
* its `value` shape: HITL middleware interrupts (`value.type === "tool"`) and
|
|
38
|
+
* raw `interrupt(...)` calls from inside a tool are treated identically —
|
|
39
|
+
* raising an interrupt in a tool must work whether or not
|
|
40
|
+
* `humanInTheLoopMiddleware` is involved.
|
|
34
41
|
*/
|
|
35
42
|
function isToolInterrupt(message) {
|
|
36
43
|
let parsed;
|
|
@@ -40,7 +47,11 @@ function isToolInterrupt(message) {
|
|
|
40
47
|
return false;
|
|
41
48
|
}
|
|
42
49
|
if (!Array.isArray(parsed) || parsed.length === 0) return false;
|
|
43
|
-
return parsed.every((entry) =>
|
|
50
|
+
return parsed.every((entry) => {
|
|
51
|
+
if (entry == null || typeof entry !== "object") return false;
|
|
52
|
+
const record = entry;
|
|
53
|
+
return typeof record.id === "string" && "value" in record;
|
|
54
|
+
});
|
|
44
55
|
}
|
|
45
56
|
/**
|
|
46
57
|
* Detects serialized LangChain `ToolMessage` values that can appear on
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-call.cjs","names":["ToolMessage","StreamChannel"],"sources":["../../../src/agents/transformers/tool-call.ts"],"sourcesContent":["import {\n StreamChannel,\n type NativeStreamTransformer,\n type ProtocolEvent,\n type ToolCallStream,\n type ToolCallStatus,\n type ToolsEventData,\n type Namespace,\n} from \"@langchain/langgraph\";\nimport { ToolMessage } from \"@langchain/core/messages\";\n\ninterface ToolCallProjection {\n toolCalls: AsyncIterable<ToolCallStream>;\n}\n\n/**\n * Returns true when `ns` belongs to the agent's own graph — i.e. it\n * starts with `path` and is at most one level deeper (the agent's\n * internal nodes like `tools`, `model_request`, etc.).\n *\n * Events from subagent subgraphs (two or more levels deeper) are\n * excluded, so `run.toolCalls` / `run.middleware` only show events\n * from the agent itself, not from its subagents.\n */\nfunction isOwnEvent(ns: Namespace, path: Namespace): boolean {\n if (ns.length < path.length || ns.length > path.length + 1) return false;\n for (let i = 0; i < path.length; i += 1) {\n if (ns[i] !== path[i]) return false;\n }\n return true;\n}\n\n/**\n * Detects when a `tool-error` payload is actually a graph interrupt rather\n * than a genuine tool failure.\n *\n * A tool that calls `interrupt()` throws a `GraphInterrupt`, whose message is\n * the JSON-serialized `Interrupt[]` array — each entry carrying the `value`\n * passed to `interrupt(...)`. An interrupt is control flow that *suspends* the\n * run (the tool re-runs on resume); it is not an error, so the tool call must\n * stay pending rather than have its `output` promise rejected.\n *\n * Any interrupt qualifies, regardless of payload shape: HITL middleware\n * interrupts (`value.type === \"tool\"`) and raw `interrupt(...)` calls from\n * inside a tool (arbitrary `value`) are treated identically — raising an\n * interrupt in a tool must work whether or not `humanInTheLoopMiddleware`\n * is involved.\n */\nfunction isToolInterrupt(message: string): boolean {\n let parsed: unknown;\n try {\n parsed = JSON.parse(message);\n } catch {\n return false;\n }\n if (!Array.isArray(parsed) || parsed.length === 0) return false;\n return parsed.every(\n (entry) =>\n entry != null &&\n typeof entry === \"object\" &&\n \"value\" in (entry as Record<string, unknown>)\n );\n}\n\n/**\n * Detects serialized LangChain `ToolMessage` values that can appear on\n * `tool-finished.output` after crossing a protocol or serialization boundary.\n *\n * @example\n * ```ts\n * {\n * lc: 1,\n * type: \"constructor\",\n * id: [\"langchain_core\", \"messages\", \"ToolMessage\"],\n * kwargs: { content: \"raw tool result\", tool_call_id: \"call_1\" }\n * }\n * ```\n */\nfunction isSerializedToolMessage(\n value: unknown\n): value is { kwargs?: { content?: unknown } } {\n if (value == null || typeof value !== \"object\") return false;\n const record = value as Record<string, unknown>;\n if (record.type !== \"constructor\" || !Array.isArray(record.id)) return false;\n return record.id[record.id.length - 1] === \"ToolMessage\";\n}\n\nfunction normalizeToolOutput(output: unknown): unknown {\n if (ToolMessage.isInstance(output)) {\n return output.content;\n }\n if (isSerializedToolMessage(output)) {\n return output.kwargs?.content;\n }\n return output;\n}\n\n/**\n * Creates a native transformer that correlates `tools` channel events\n * into per-call {@link ToolCallStream} objects.\n *\n * Marked `__native: true` — projection keys land directly on the\n * `GraphRunStream` instance as `run.toolCalls`.\n */\nexport function createToolCallTransformer(\n path: Namespace\n): () => NativeStreamTransformer<ToolCallProjection> {\n return () => {\n const toolCallsLog = StreamChannel.local<ToolCallStream>();\n\n const pendingCalls = new Map<\n string,\n {\n resolveOutput: (v: unknown) => void;\n rejectOutput: (e: unknown) => void;\n resolveStatus: (v: ToolCallStatus) => void;\n resolveError: (v: string | undefined) => void;\n }\n >();\n\n function createToolCallEntry(\n callId: string,\n name: string,\n rawInput: unknown\n ): void {\n if (pendingCalls.has(callId)) return;\n const input =\n typeof rawInput === \"string\" ? JSON.parse(rawInput) : rawInput;\n\n let resolveOutput!: (v: unknown) => void;\n let rejectOutput!: (e: unknown) => void;\n let resolveStatus!: (v: ToolCallStatus) => void;\n let resolveError!: (v: string | undefined) => void;\n\n const output = new Promise<unknown>((res, rej) => {\n resolveOutput = res;\n rejectOutput = rej;\n });\n const status = new Promise<ToolCallStatus>((res) => {\n resolveStatus = res;\n });\n const error = new Promise<string | undefined>((res) => {\n resolveError = res;\n });\n\n pendingCalls.set(callId, {\n resolveOutput,\n rejectOutput,\n resolveStatus,\n resolveError,\n });\n\n toolCallsLog.push({\n name,\n callId,\n input,\n output,\n status,\n error,\n } as ToolCallStream);\n }\n\n return {\n __native: true as const,\n\n init: () => ({\n toolCalls: toolCallsLog,\n }),\n\n process(event: ProtocolEvent): boolean {\n /**\n * Only process events that are at the same depth as the agent's graph.\n */\n if (!isOwnEvent(event.params.namespace, path)) return true;\n\n if (event.method === \"messages\") {\n const data = event.params.data as Record<string, unknown>;\n if (data.event === \"content-block-finish\") {\n const cb = (data.contentBlock ?? data.content_block) as\n | Record<string, unknown>\n | undefined;\n if (cb?.type === \"tool_call\") {\n createToolCallEntry(\n String(cb.id ?? \"\"),\n String(cb.name ?? \"\"),\n cb.args ?? cb.input\n );\n }\n }\n }\n\n if (event.method === \"tools\") {\n const data = event.params.data as ToolsEventData;\n const toolCallId = (data as Record<string, unknown>)\n .tool_call_id as string;\n\n if (data.event === \"tool-started\") {\n createToolCallEntry(\n toolCallId,\n ((data as Record<string, unknown>).tool_name as string) ??\n \"unknown\",\n (data as Record<string, unknown>).input\n );\n }\n\n const pending = toolCallId ? pendingCalls.get(toolCallId) : undefined;\n\n if (pending) {\n if (data.event === \"tool-finished\") {\n pending.resolveOutput(\n normalizeToolOutput((data as Record<string, unknown>).output)\n );\n pending.resolveStatus(\"finished\");\n pending.resolveError(undefined);\n pendingCalls.delete(toolCallId);\n } else if (data.event === \"tool-error\") {\n const message =\n ((data as Record<string, unknown>).message as string) ??\n \"unknown error\";\n // An interrupt raised inside a tool (HITL middleware *or* a\n // raw `interrupt()`) surfaces here as a `tool-error` whose\n // message is the serialized interrupt. It is control flow,\n // not a failure: keep the call pending (it re-runs on resume)\n // and never reject `output`, which would otherwise become an\n // unhandled rejection and crash the run.\n if (isToolInterrupt(message)) {\n return true;\n }\n pending.rejectOutput(new Error(message));\n pending.resolveStatus(\"error\");\n pending.resolveError(message);\n pendingCalls.delete(toolCallId);\n }\n }\n }\n\n return true;\n },\n\n finalize(): void {\n for (const pending of pendingCalls.values()) {\n pending.resolveStatus(\"finished\");\n pending.resolveError(undefined);\n pending.resolveOutput(undefined);\n }\n pendingCalls.clear();\n toolCallsLog.close();\n },\n\n fail(err: unknown): void {\n for (const pending of pendingCalls.values()) {\n pending.resolveStatus(\"error\");\n pending.resolveError(\n err instanceof Error ? err.message : String(err)\n );\n pending.rejectOutput(err);\n }\n pendingCalls.clear();\n toolCallsLog.fail(err);\n },\n };\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tool-call.cjs","names":["ToolMessage","StreamChannel"],"sources":["../../../src/agents/transformers/tool-call.ts"],"sourcesContent":["import {\n StreamChannel,\n type NativeStreamTransformer,\n type ProtocolEvent,\n type ToolCallStream,\n type ToolCallStatus,\n type ToolsEventData,\n type Namespace,\n} from \"@langchain/langgraph\";\nimport { ToolMessage } from \"@langchain/core/messages\";\n\ninterface ToolCallProjection {\n toolCalls: AsyncIterable<ToolCallStream>;\n}\n\n/**\n * Returns true when `ns` belongs to the agent's own graph — i.e. it\n * starts with `path` and is at most one level deeper (the agent's\n * internal nodes like `tools`, `model_request`, etc.).\n *\n * Events from subagent subgraphs (two or more levels deeper) are\n * excluded, so `run.toolCalls` / `run.middleware` only show events\n * from the agent itself, not from its subagents.\n */\nfunction isOwnEvent(ns: Namespace, path: Namespace): boolean {\n if (ns.length < path.length || ns.length > path.length + 1) return false;\n for (let i = 0; i < path.length; i += 1) {\n if (ns[i] !== path[i]) return false;\n }\n return true;\n}\n\n/**\n * Detects when a `tool-error` payload is actually a graph interrupt rather\n * than a genuine tool failure.\n *\n * A tool that calls `interrupt()` throws a `GraphInterrupt`, whose message is\n * the JSON-serialized `Interrupt[]` array. Each entry has the LangGraph\n * `Interrupt` shape `{ id, value }`: a stable `id` (a hash of the checkpoint\n * namespace, generated by `interrupt()` and always present during graph\n * execution) plus the `value` passed to `interrupt(...)`. We require BOTH a\n * string `id` and a `value` on every entry — a bare `value` is not a reliable\n * discriminator, since a genuine tool error message can also be a JSON array\n * of `{ value }` records (e.g. a validator emitting\n * `[{ \"value\": \"bad input\", \"message\": \"invalid\" }]`). Keying off the\n * interrupt `id` keeps real tool failures on the error path.\n *\n * An interrupt is control flow that *suspends* the run (the tool re-runs on\n * resume); it is not an error, so the tool call must stay pending rather than\n * have its `output` promise rejected. Any interrupt qualifies regardless of\n * its `value` shape: HITL middleware interrupts (`value.type === \"tool\"`) and\n * raw `interrupt(...)` calls from inside a tool are treated identically —\n * raising an interrupt in a tool must work whether or not\n * `humanInTheLoopMiddleware` is involved.\n */\nfunction isToolInterrupt(message: string): boolean {\n let parsed: unknown;\n try {\n parsed = JSON.parse(message);\n } catch {\n return false;\n }\n if (!Array.isArray(parsed) || parsed.length === 0) return false;\n return parsed.every((entry) => {\n if (entry == null || typeof entry !== \"object\") return false;\n const record = entry as Record<string, unknown>;\n return typeof record.id === \"string\" && \"value\" in record;\n });\n}\n\n/**\n * Detects serialized LangChain `ToolMessage` values that can appear on\n * `tool-finished.output` after crossing a protocol or serialization boundary.\n *\n * @example\n * ```ts\n * {\n * lc: 1,\n * type: \"constructor\",\n * id: [\"langchain_core\", \"messages\", \"ToolMessage\"],\n * kwargs: { content: \"raw tool result\", tool_call_id: \"call_1\" }\n * }\n * ```\n */\nfunction isSerializedToolMessage(\n value: unknown\n): value is { kwargs?: { content?: unknown } } {\n if (value == null || typeof value !== \"object\") return false;\n const record = value as Record<string, unknown>;\n if (record.type !== \"constructor\" || !Array.isArray(record.id)) return false;\n return record.id[record.id.length - 1] === \"ToolMessage\";\n}\n\nfunction normalizeToolOutput(output: unknown): unknown {\n if (ToolMessage.isInstance(output)) {\n return output.content;\n }\n if (isSerializedToolMessage(output)) {\n return output.kwargs?.content;\n }\n return output;\n}\n\n/**\n * Creates a native transformer that correlates `tools` channel events\n * into per-call {@link ToolCallStream} objects.\n *\n * Marked `__native: true` — projection keys land directly on the\n * `GraphRunStream` instance as `run.toolCalls`.\n */\nexport function createToolCallTransformer(\n path: Namespace\n): () => NativeStreamTransformer<ToolCallProjection> {\n return () => {\n const toolCallsLog = StreamChannel.local<ToolCallStream>();\n\n const pendingCalls = new Map<\n string,\n {\n resolveOutput: (v: unknown) => void;\n rejectOutput: (e: unknown) => void;\n resolveStatus: (v: ToolCallStatus) => void;\n resolveError: (v: string | undefined) => void;\n }\n >();\n\n function createToolCallEntry(\n callId: string,\n name: string,\n rawInput: unknown\n ): void {\n if (pendingCalls.has(callId)) return;\n const input =\n typeof rawInput === \"string\" ? JSON.parse(rawInput) : rawInput;\n\n let resolveOutput!: (v: unknown) => void;\n let rejectOutput!: (e: unknown) => void;\n let resolveStatus!: (v: ToolCallStatus) => void;\n let resolveError!: (v: string | undefined) => void;\n\n const output = new Promise<unknown>((res, rej) => {\n resolveOutput = res;\n rejectOutput = rej;\n });\n const status = new Promise<ToolCallStatus>((res) => {\n resolveStatus = res;\n });\n const error = new Promise<string | undefined>((res) => {\n resolveError = res;\n });\n\n pendingCalls.set(callId, {\n resolveOutput,\n rejectOutput,\n resolveStatus,\n resolveError,\n });\n\n toolCallsLog.push({\n name,\n callId,\n input,\n output,\n status,\n error,\n } as ToolCallStream);\n }\n\n return {\n __native: true as const,\n\n init: () => ({\n toolCalls: toolCallsLog,\n }),\n\n process(event: ProtocolEvent): boolean {\n /**\n * Only process events that are at the same depth as the agent's graph.\n */\n if (!isOwnEvent(event.params.namespace, path)) return true;\n\n if (event.method === \"messages\") {\n const data = event.params.data as Record<string, unknown>;\n if (data.event === \"content-block-finish\") {\n const cb = (data.contentBlock ?? data.content_block) as\n | Record<string, unknown>\n | undefined;\n if (cb?.type === \"tool_call\") {\n createToolCallEntry(\n String(cb.id ?? \"\"),\n String(cb.name ?? \"\"),\n cb.args ?? cb.input\n );\n }\n }\n }\n\n if (event.method === \"tools\") {\n const data = event.params.data as ToolsEventData;\n const toolCallId = (data as Record<string, unknown>)\n .tool_call_id as string;\n\n if (data.event === \"tool-started\") {\n createToolCallEntry(\n toolCallId,\n ((data as Record<string, unknown>).tool_name as string) ??\n \"unknown\",\n (data as Record<string, unknown>).input\n );\n }\n\n const pending = toolCallId ? pendingCalls.get(toolCallId) : undefined;\n\n if (pending) {\n if (data.event === \"tool-finished\") {\n pending.resolveOutput(\n normalizeToolOutput((data as Record<string, unknown>).output)\n );\n pending.resolveStatus(\"finished\");\n pending.resolveError(undefined);\n pendingCalls.delete(toolCallId);\n } else if (data.event === \"tool-error\") {\n const message =\n ((data as Record<string, unknown>).message as string) ??\n \"unknown error\";\n // An interrupt raised inside a tool (HITL middleware *or* a\n // raw `interrupt()`) surfaces here as a `tool-error` whose\n // message is the serialized interrupt. It is control flow,\n // not a failure: keep the call pending (it re-runs on resume)\n // and never reject `output`, which would otherwise become an\n // unhandled rejection and crash the run.\n if (isToolInterrupt(message)) {\n return true;\n }\n pending.rejectOutput(new Error(message));\n pending.resolveStatus(\"error\");\n pending.resolveError(message);\n pendingCalls.delete(toolCallId);\n }\n }\n }\n\n return true;\n },\n\n finalize(): void {\n for (const pending of pendingCalls.values()) {\n pending.resolveStatus(\"finished\");\n pending.resolveError(undefined);\n pending.resolveOutput(undefined);\n }\n pendingCalls.clear();\n toolCallsLog.close();\n },\n\n fail(err: unknown): void {\n for (const pending of pendingCalls.values()) {\n pending.resolveStatus(\"error\");\n pending.resolveError(\n err instanceof Error ? err.message : String(err)\n );\n pending.rejectOutput(err);\n }\n pendingCalls.clear();\n toolCallsLog.fail(err);\n },\n };\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-call.d.cts","names":[],"sources":["../../../src/agents/transformers/tool-call.ts"],"mappings":";;;UAWU,kBAAA;EACR,SAAA,EAAW,aAAA,CAAc,cAAA;AAAA;;;;;;;;
|
|
1
|
+
{"version":3,"file":"tool-call.d.cts","names":[],"sources":["../../../src/agents/transformers/tool-call.ts"],"mappings":";;;UAWU,kBAAA;EACR,SAAA,EAAW,aAAA,CAAc,cAAA;AAAA;;;;;;;;iBAkGX,yBAAA,CACd,IAAA,EAAM,SAAA,SACC,uBAAA,CAAwB,kBAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-call.d.ts","names":[],"sources":["../../../src/agents/transformers/tool-call.ts"],"mappings":";;;UAWU,kBAAA;EACR,SAAA,EAAW,aAAA,CAAc,cAAA;AAAA;;;;;;;;
|
|
1
|
+
{"version":3,"file":"tool-call.d.ts","names":[],"sources":["../../../src/agents/transformers/tool-call.ts"],"mappings":";;;UAWU,kBAAA;EACR,SAAA,EAAW,aAAA,CAAc,cAAA;AAAA;;;;;;;;iBAkGX,yBAAA,CACd,IAAA,EAAM,SAAA,SACC,uBAAA,CAAwB,kBAAA"}
|
|
@@ -20,16 +20,23 @@ function isOwnEvent(ns, path) {
|
|
|
20
20
|
* than a genuine tool failure.
|
|
21
21
|
*
|
|
22
22
|
* A tool that calls `interrupt()` throws a `GraphInterrupt`, whose message is
|
|
23
|
-
* the JSON-serialized `Interrupt[]` array
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* the JSON-serialized `Interrupt[]` array. Each entry has the LangGraph
|
|
24
|
+
* `Interrupt` shape `{ id, value }`: a stable `id` (a hash of the checkpoint
|
|
25
|
+
* namespace, generated by `interrupt()` and always present during graph
|
|
26
|
+
* execution) plus the `value` passed to `interrupt(...)`. We require BOTH a
|
|
27
|
+
* string `id` and a `value` on every entry — a bare `value` is not a reliable
|
|
28
|
+
* discriminator, since a genuine tool error message can also be a JSON array
|
|
29
|
+
* of `{ value }` records (e.g. a validator emitting
|
|
30
|
+
* `[{ "value": "bad input", "message": "invalid" }]`). Keying off the
|
|
31
|
+
* interrupt `id` keeps real tool failures on the error path.
|
|
27
32
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
+
* An interrupt is control flow that *suspends* the run (the tool re-runs on
|
|
34
|
+
* resume); it is not an error, so the tool call must stay pending rather than
|
|
35
|
+
* have its `output` promise rejected. Any interrupt qualifies regardless of
|
|
36
|
+
* its `value` shape: HITL middleware interrupts (`value.type === "tool"`) and
|
|
37
|
+
* raw `interrupt(...)` calls from inside a tool are treated identically —
|
|
38
|
+
* raising an interrupt in a tool must work whether or not
|
|
39
|
+
* `humanInTheLoopMiddleware` is involved.
|
|
33
40
|
*/
|
|
34
41
|
function isToolInterrupt(message) {
|
|
35
42
|
let parsed;
|
|
@@ -39,7 +46,11 @@ function isToolInterrupt(message) {
|
|
|
39
46
|
return false;
|
|
40
47
|
}
|
|
41
48
|
if (!Array.isArray(parsed) || parsed.length === 0) return false;
|
|
42
|
-
return parsed.every((entry) =>
|
|
49
|
+
return parsed.every((entry) => {
|
|
50
|
+
if (entry == null || typeof entry !== "object") return false;
|
|
51
|
+
const record = entry;
|
|
52
|
+
return typeof record.id === "string" && "value" in record;
|
|
53
|
+
});
|
|
43
54
|
}
|
|
44
55
|
/**
|
|
45
56
|
* Detects serialized LangChain `ToolMessage` values that can appear on
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-call.js","names":[],"sources":["../../../src/agents/transformers/tool-call.ts"],"sourcesContent":["import {\n StreamChannel,\n type NativeStreamTransformer,\n type ProtocolEvent,\n type ToolCallStream,\n type ToolCallStatus,\n type ToolsEventData,\n type Namespace,\n} from \"@langchain/langgraph\";\nimport { ToolMessage } from \"@langchain/core/messages\";\n\ninterface ToolCallProjection {\n toolCalls: AsyncIterable<ToolCallStream>;\n}\n\n/**\n * Returns true when `ns` belongs to the agent's own graph — i.e. it\n * starts with `path` and is at most one level deeper (the agent's\n * internal nodes like `tools`, `model_request`, etc.).\n *\n * Events from subagent subgraphs (two or more levels deeper) are\n * excluded, so `run.toolCalls` / `run.middleware` only show events\n * from the agent itself, not from its subagents.\n */\nfunction isOwnEvent(ns: Namespace, path: Namespace): boolean {\n if (ns.length < path.length || ns.length > path.length + 1) return false;\n for (let i = 0; i < path.length; i += 1) {\n if (ns[i] !== path[i]) return false;\n }\n return true;\n}\n\n/**\n * Detects when a `tool-error` payload is actually a graph interrupt rather\n * than a genuine tool failure.\n *\n * A tool that calls `interrupt()` throws a `GraphInterrupt`, whose message is\n * the JSON-serialized `Interrupt[]` array — each entry carrying the `value`\n * passed to `interrupt(...)`. An interrupt is control flow that *suspends* the\n * run (the tool re-runs on resume); it is not an error, so the tool call must\n * stay pending rather than have its `output` promise rejected.\n *\n * Any interrupt qualifies, regardless of payload shape: HITL middleware\n * interrupts (`value.type === \"tool\"`) and raw `interrupt(...)` calls from\n * inside a tool (arbitrary `value`) are treated identically — raising an\n * interrupt in a tool must work whether or not `humanInTheLoopMiddleware`\n * is involved.\n */\nfunction isToolInterrupt(message: string): boolean {\n let parsed: unknown;\n try {\n parsed = JSON.parse(message);\n } catch {\n return false;\n }\n if (!Array.isArray(parsed) || parsed.length === 0) return false;\n return parsed.every(\n (entry) =>\n entry != null &&\n typeof entry === \"object\" &&\n \"value\" in (entry as Record<string, unknown>)\n );\n}\n\n/**\n * Detects serialized LangChain `ToolMessage` values that can appear on\n * `tool-finished.output` after crossing a protocol or serialization boundary.\n *\n * @example\n * ```ts\n * {\n * lc: 1,\n * type: \"constructor\",\n * id: [\"langchain_core\", \"messages\", \"ToolMessage\"],\n * kwargs: { content: \"raw tool result\", tool_call_id: \"call_1\" }\n * }\n * ```\n */\nfunction isSerializedToolMessage(\n value: unknown\n): value is { kwargs?: { content?: unknown } } {\n if (value == null || typeof value !== \"object\") return false;\n const record = value as Record<string, unknown>;\n if (record.type !== \"constructor\" || !Array.isArray(record.id)) return false;\n return record.id[record.id.length - 1] === \"ToolMessage\";\n}\n\nfunction normalizeToolOutput(output: unknown): unknown {\n if (ToolMessage.isInstance(output)) {\n return output.content;\n }\n if (isSerializedToolMessage(output)) {\n return output.kwargs?.content;\n }\n return output;\n}\n\n/**\n * Creates a native transformer that correlates `tools` channel events\n * into per-call {@link ToolCallStream} objects.\n *\n * Marked `__native: true` — projection keys land directly on the\n * `GraphRunStream` instance as `run.toolCalls`.\n */\nexport function createToolCallTransformer(\n path: Namespace\n): () => NativeStreamTransformer<ToolCallProjection> {\n return () => {\n const toolCallsLog = StreamChannel.local<ToolCallStream>();\n\n const pendingCalls = new Map<\n string,\n {\n resolveOutput: (v: unknown) => void;\n rejectOutput: (e: unknown) => void;\n resolveStatus: (v: ToolCallStatus) => void;\n resolveError: (v: string | undefined) => void;\n }\n >();\n\n function createToolCallEntry(\n callId: string,\n name: string,\n rawInput: unknown\n ): void {\n if (pendingCalls.has(callId)) return;\n const input =\n typeof rawInput === \"string\" ? JSON.parse(rawInput) : rawInput;\n\n let resolveOutput!: (v: unknown) => void;\n let rejectOutput!: (e: unknown) => void;\n let resolveStatus!: (v: ToolCallStatus) => void;\n let resolveError!: (v: string | undefined) => void;\n\n const output = new Promise<unknown>((res, rej) => {\n resolveOutput = res;\n rejectOutput = rej;\n });\n const status = new Promise<ToolCallStatus>((res) => {\n resolveStatus = res;\n });\n const error = new Promise<string | undefined>((res) => {\n resolveError = res;\n });\n\n pendingCalls.set(callId, {\n resolveOutput,\n rejectOutput,\n resolveStatus,\n resolveError,\n });\n\n toolCallsLog.push({\n name,\n callId,\n input,\n output,\n status,\n error,\n } as ToolCallStream);\n }\n\n return {\n __native: true as const,\n\n init: () => ({\n toolCalls: toolCallsLog,\n }),\n\n process(event: ProtocolEvent): boolean {\n /**\n * Only process events that are at the same depth as the agent's graph.\n */\n if (!isOwnEvent(event.params.namespace, path)) return true;\n\n if (event.method === \"messages\") {\n const data = event.params.data as Record<string, unknown>;\n if (data.event === \"content-block-finish\") {\n const cb = (data.contentBlock ?? data.content_block) as\n | Record<string, unknown>\n | undefined;\n if (cb?.type === \"tool_call\") {\n createToolCallEntry(\n String(cb.id ?? \"\"),\n String(cb.name ?? \"\"),\n cb.args ?? cb.input\n );\n }\n }\n }\n\n if (event.method === \"tools\") {\n const data = event.params.data as ToolsEventData;\n const toolCallId = (data as Record<string, unknown>)\n .tool_call_id as string;\n\n if (data.event === \"tool-started\") {\n createToolCallEntry(\n toolCallId,\n ((data as Record<string, unknown>).tool_name as string) ??\n \"unknown\",\n (data as Record<string, unknown>).input\n );\n }\n\n const pending = toolCallId ? pendingCalls.get(toolCallId) : undefined;\n\n if (pending) {\n if (data.event === \"tool-finished\") {\n pending.resolveOutput(\n normalizeToolOutput((data as Record<string, unknown>).output)\n );\n pending.resolveStatus(\"finished\");\n pending.resolveError(undefined);\n pendingCalls.delete(toolCallId);\n } else if (data.event === \"tool-error\") {\n const message =\n ((data as Record<string, unknown>).message as string) ??\n \"unknown error\";\n // An interrupt raised inside a tool (HITL middleware *or* a\n // raw `interrupt()`) surfaces here as a `tool-error` whose\n // message is the serialized interrupt. It is control flow,\n // not a failure: keep the call pending (it re-runs on resume)\n // and never reject `output`, which would otherwise become an\n // unhandled rejection and crash the run.\n if (isToolInterrupt(message)) {\n return true;\n }\n pending.rejectOutput(new Error(message));\n pending.resolveStatus(\"error\");\n pending.resolveError(message);\n pendingCalls.delete(toolCallId);\n }\n }\n }\n\n return true;\n },\n\n finalize(): void {\n for (const pending of pendingCalls.values()) {\n pending.resolveStatus(\"finished\");\n pending.resolveError(undefined);\n pending.resolveOutput(undefined);\n }\n pendingCalls.clear();\n toolCallsLog.close();\n },\n\n fail(err: unknown): void {\n for (const pending of pendingCalls.values()) {\n pending.resolveStatus(\"error\");\n pending.resolveError(\n err instanceof Error ? err.message : String(err)\n );\n pending.rejectOutput(err);\n }\n pendingCalls.clear();\n toolCallsLog.fail(err);\n },\n };\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tool-call.js","names":[],"sources":["../../../src/agents/transformers/tool-call.ts"],"sourcesContent":["import {\n StreamChannel,\n type NativeStreamTransformer,\n type ProtocolEvent,\n type ToolCallStream,\n type ToolCallStatus,\n type ToolsEventData,\n type Namespace,\n} from \"@langchain/langgraph\";\nimport { ToolMessage } from \"@langchain/core/messages\";\n\ninterface ToolCallProjection {\n toolCalls: AsyncIterable<ToolCallStream>;\n}\n\n/**\n * Returns true when `ns` belongs to the agent's own graph — i.e. it\n * starts with `path` and is at most one level deeper (the agent's\n * internal nodes like `tools`, `model_request`, etc.).\n *\n * Events from subagent subgraphs (two or more levels deeper) are\n * excluded, so `run.toolCalls` / `run.middleware` only show events\n * from the agent itself, not from its subagents.\n */\nfunction isOwnEvent(ns: Namespace, path: Namespace): boolean {\n if (ns.length < path.length || ns.length > path.length + 1) return false;\n for (let i = 0; i < path.length; i += 1) {\n if (ns[i] !== path[i]) return false;\n }\n return true;\n}\n\n/**\n * Detects when a `tool-error` payload is actually a graph interrupt rather\n * than a genuine tool failure.\n *\n * A tool that calls `interrupt()` throws a `GraphInterrupt`, whose message is\n * the JSON-serialized `Interrupt[]` array. Each entry has the LangGraph\n * `Interrupt` shape `{ id, value }`: a stable `id` (a hash of the checkpoint\n * namespace, generated by `interrupt()` and always present during graph\n * execution) plus the `value` passed to `interrupt(...)`. We require BOTH a\n * string `id` and a `value` on every entry — a bare `value` is not a reliable\n * discriminator, since a genuine tool error message can also be a JSON array\n * of `{ value }` records (e.g. a validator emitting\n * `[{ \"value\": \"bad input\", \"message\": \"invalid\" }]`). Keying off the\n * interrupt `id` keeps real tool failures on the error path.\n *\n * An interrupt is control flow that *suspends* the run (the tool re-runs on\n * resume); it is not an error, so the tool call must stay pending rather than\n * have its `output` promise rejected. Any interrupt qualifies regardless of\n * its `value` shape: HITL middleware interrupts (`value.type === \"tool\"`) and\n * raw `interrupt(...)` calls from inside a tool are treated identically —\n * raising an interrupt in a tool must work whether or not\n * `humanInTheLoopMiddleware` is involved.\n */\nfunction isToolInterrupt(message: string): boolean {\n let parsed: unknown;\n try {\n parsed = JSON.parse(message);\n } catch {\n return false;\n }\n if (!Array.isArray(parsed) || parsed.length === 0) return false;\n return parsed.every((entry) => {\n if (entry == null || typeof entry !== \"object\") return false;\n const record = entry as Record<string, unknown>;\n return typeof record.id === \"string\" && \"value\" in record;\n });\n}\n\n/**\n * Detects serialized LangChain `ToolMessage` values that can appear on\n * `tool-finished.output` after crossing a protocol or serialization boundary.\n *\n * @example\n * ```ts\n * {\n * lc: 1,\n * type: \"constructor\",\n * id: [\"langchain_core\", \"messages\", \"ToolMessage\"],\n * kwargs: { content: \"raw tool result\", tool_call_id: \"call_1\" }\n * }\n * ```\n */\nfunction isSerializedToolMessage(\n value: unknown\n): value is { kwargs?: { content?: unknown } } {\n if (value == null || typeof value !== \"object\") return false;\n const record = value as Record<string, unknown>;\n if (record.type !== \"constructor\" || !Array.isArray(record.id)) return false;\n return record.id[record.id.length - 1] === \"ToolMessage\";\n}\n\nfunction normalizeToolOutput(output: unknown): unknown {\n if (ToolMessage.isInstance(output)) {\n return output.content;\n }\n if (isSerializedToolMessage(output)) {\n return output.kwargs?.content;\n }\n return output;\n}\n\n/**\n * Creates a native transformer that correlates `tools` channel events\n * into per-call {@link ToolCallStream} objects.\n *\n * Marked `__native: true` — projection keys land directly on the\n * `GraphRunStream` instance as `run.toolCalls`.\n */\nexport function createToolCallTransformer(\n path: Namespace\n): () => NativeStreamTransformer<ToolCallProjection> {\n return () => {\n const toolCallsLog = StreamChannel.local<ToolCallStream>();\n\n const pendingCalls = new Map<\n string,\n {\n resolveOutput: (v: unknown) => void;\n rejectOutput: (e: unknown) => void;\n resolveStatus: (v: ToolCallStatus) => void;\n resolveError: (v: string | undefined) => void;\n }\n >();\n\n function createToolCallEntry(\n callId: string,\n name: string,\n rawInput: unknown\n ): void {\n if (pendingCalls.has(callId)) return;\n const input =\n typeof rawInput === \"string\" ? JSON.parse(rawInput) : rawInput;\n\n let resolveOutput!: (v: unknown) => void;\n let rejectOutput!: (e: unknown) => void;\n let resolveStatus!: (v: ToolCallStatus) => void;\n let resolveError!: (v: string | undefined) => void;\n\n const output = new Promise<unknown>((res, rej) => {\n resolveOutput = res;\n rejectOutput = rej;\n });\n const status = new Promise<ToolCallStatus>((res) => {\n resolveStatus = res;\n });\n const error = new Promise<string | undefined>((res) => {\n resolveError = res;\n });\n\n pendingCalls.set(callId, {\n resolveOutput,\n rejectOutput,\n resolveStatus,\n resolveError,\n });\n\n toolCallsLog.push({\n name,\n callId,\n input,\n output,\n status,\n error,\n } as ToolCallStream);\n }\n\n return {\n __native: true as const,\n\n init: () => ({\n toolCalls: toolCallsLog,\n }),\n\n process(event: ProtocolEvent): boolean {\n /**\n * Only process events that are at the same depth as the agent's graph.\n */\n if (!isOwnEvent(event.params.namespace, path)) return true;\n\n if (event.method === \"messages\") {\n const data = event.params.data as Record<string, unknown>;\n if (data.event === \"content-block-finish\") {\n const cb = (data.contentBlock ?? data.content_block) as\n | Record<string, unknown>\n | undefined;\n if (cb?.type === \"tool_call\") {\n createToolCallEntry(\n String(cb.id ?? \"\"),\n String(cb.name ?? \"\"),\n cb.args ?? cb.input\n );\n }\n }\n }\n\n if (event.method === \"tools\") {\n const data = event.params.data as ToolsEventData;\n const toolCallId = (data as Record<string, unknown>)\n .tool_call_id as string;\n\n if (data.event === \"tool-started\") {\n createToolCallEntry(\n toolCallId,\n ((data as Record<string, unknown>).tool_name as string) ??\n \"unknown\",\n (data as Record<string, unknown>).input\n );\n }\n\n const pending = toolCallId ? pendingCalls.get(toolCallId) : undefined;\n\n if (pending) {\n if (data.event === \"tool-finished\") {\n pending.resolveOutput(\n normalizeToolOutput((data as Record<string, unknown>).output)\n );\n pending.resolveStatus(\"finished\");\n pending.resolveError(undefined);\n pendingCalls.delete(toolCallId);\n } else if (data.event === \"tool-error\") {\n const message =\n ((data as Record<string, unknown>).message as string) ??\n \"unknown error\";\n // An interrupt raised inside a tool (HITL middleware *or* a\n // raw `interrupt()`) surfaces here as a `tool-error` whose\n // message is the serialized interrupt. It is control flow,\n // not a failure: keep the call pending (it re-runs on resume)\n // and never reject `output`, which would otherwise become an\n // unhandled rejection and crash the run.\n if (isToolInterrupt(message)) {\n return true;\n }\n pending.rejectOutput(new Error(message));\n pending.resolveStatus(\"error\");\n pending.resolveError(message);\n pendingCalls.delete(toolCallId);\n }\n }\n }\n\n return true;\n },\n\n finalize(): void {\n for (const pending of pendingCalls.values()) {\n pending.resolveStatus(\"finished\");\n pending.resolveError(undefined);\n pending.resolveOutput(undefined);\n }\n pendingCalls.clear();\n toolCallsLog.close();\n },\n\n fail(err: unknown): void {\n for (const pending of pendingCalls.values()) {\n pending.resolveStatus(\"error\");\n pending.resolveError(\n err instanceof Error ? err.message : String(err)\n );\n pending.rejectOutput(err);\n }\n pendingCalls.clear();\n toolCallsLog.fail(err);\n },\n };\n };\n}\n"],"mappings":"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"}
|
package/hub/node.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("../dist/hub/node.cjs");
|
package/hub/node.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/hub/node.js";
|
package/hub/node.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/hub/node.js";
|
package/hub/node.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/hub/node.js";
|
package/hub.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("./dist/hub/index.cjs");
|
package/hub.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dist/hub/index.js";
|
package/hub.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dist/hub/index.js";
|
package/hub.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dist/hub/index.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("../dist/load/serializable.cjs");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/load/serializable.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/load/serializable.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/load/serializable.js";
|
package/load.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("./dist/load/index.cjs");
|
package/load.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dist/load/index.js";
|
package/load.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dist/load/index.js";
|
package/load.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dist/load/index.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "langchain",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "Typescript bindings for langchain",
|
|
5
5
|
"author": "LangChain",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,15 +53,15 @@
|
|
|
53
53
|
"typescript": "~6.0.3",
|
|
54
54
|
"vitest": "^4.1.8",
|
|
55
55
|
"yaml": "^2.9.0",
|
|
56
|
-
"@langchain/anthropic": "1.5.
|
|
57
|
-
"@langchain/aws": "1.4.
|
|
58
|
-
"@langchain/
|
|
59
|
-
"@langchain/
|
|
60
|
-
"@langchain/
|
|
56
|
+
"@langchain/anthropic": "1.5.1",
|
|
57
|
+
"@langchain/aws": "1.4.2",
|
|
58
|
+
"@langchain/core": "^1.2.1",
|
|
59
|
+
"@langchain/fireworks": "0.2.3",
|
|
60
|
+
"@langchain/openai": "1.5.3",
|
|
61
61
|
"@langchain/tsconfig": "0.0.1"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@langchain/core": "^1.2.
|
|
64
|
+
"@langchain/core": "^1.2.1"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@langchain/langgraph": "^1.4.4",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("../dist/storage/encoder_backed.cjs");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/storage/encoder_backed.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/storage/encoder_backed.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/storage/encoder_backed.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("../dist/storage/file_system.cjs");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/storage/file_system.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/storage/file_system.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/storage/file_system.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("../dist/storage/in_memory.cjs");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/storage/in_memory.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/storage/in_memory.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/storage/in_memory.js";
|