langchain 1.2.18 → 1.2.20
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 +30 -0
- package/dist/agents/index.cjs.map +1 -1
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/middleware/types.cjs.map +1 -1
- package/dist/agents/middleware/types.d.cts +1 -1
- package/dist/agents/middleware/types.d.cts.map +1 -1
- package/dist/agents/middleware/types.d.ts +1 -1
- package/dist/agents/middleware/types.d.ts.map +1 -1
- package/dist/agents/middleware/types.js.map +1 -1
- package/dist/agents/middleware/utils.cjs +20 -3
- package/dist/agents/middleware/utils.cjs.map +1 -1
- package/dist/agents/middleware/utils.d.cts +8 -2
- package/dist/agents/middleware/utils.d.cts.map +1 -1
- package/dist/agents/middleware/utils.d.ts +8 -2
- package/dist/agents/middleware/utils.d.ts.map +1 -1
- package/dist/agents/middleware/utils.js +20 -3
- package/dist/agents/middleware/utils.js.map +1 -1
- package/dist/agents/nodes/AgentNode.cjs +86 -51
- package/dist/agents/nodes/AgentNode.cjs.map +1 -1
- package/dist/agents/nodes/AgentNode.js +87 -52
- package/dist/agents/nodes/AgentNode.js.map +1 -1
- package/dist/agents/nodes/middleware.cjs +4 -5
- package/dist/agents/nodes/middleware.cjs.map +1 -1
- package/dist/agents/nodes/middleware.js +4 -5
- package/dist/agents/nodes/middleware.js.map +1 -1
- package/dist/agents/responses.cjs.map +1 -1
- package/dist/agents/responses.d.cts +9 -0
- package/dist/agents/responses.d.cts.map +1 -1
- package/dist/agents/responses.d.ts +9 -0
- package/dist/agents/responses.d.ts.map +1 -1
- package/dist/agents/responses.js.map +1 -1
- package/dist/agents/utils.cjs.map +1 -1
- package/dist/agents/utils.js.map +1 -1
- package/dist/chat_models/universal.cjs.map +1 -1
- package/dist/chat_models/universal.js.map +1 -1
- package/dist/hub/base.cjs +6 -3
- package/dist/hub/base.cjs.map +1 -1
- package/dist/hub/base.d.cts +2 -0
- package/dist/hub/base.d.cts.map +1 -1
- package/dist/hub/base.d.ts +2 -0
- package/dist/hub/base.d.ts.map +1 -1
- package/dist/hub/base.js +6 -3
- package/dist/hub/base.js.map +1 -1
- package/dist/hub/index.cjs +2 -0
- package/dist/hub/index.cjs.map +1 -1
- package/dist/hub/index.d.cts +5 -0
- package/dist/hub/index.d.cts.map +1 -1
- package/dist/hub/index.d.ts +5 -0
- package/dist/hub/index.d.ts.map +1 -1
- package/dist/hub/index.js +2 -0
- package/dist/hub/index.js.map +1 -1
- package/dist/hub/node.cjs +2 -0
- package/dist/hub/node.cjs.map +1 -1
- package/dist/hub/node.d.cts +5 -0
- package/dist/hub/node.d.cts.map +1 -1
- package/dist/hub/node.d.ts +5 -0
- package/dist/hub/node.d.ts.map +1 -1
- package/dist/hub/node.js +2 -0
- package/dist/hub/node.js.map +1 -1
- package/package.json +7 -7
|
@@ -7,7 +7,7 @@ import { withAgentName } from "../withAgentName.js";
|
|
|
7
7
|
import { ProviderStrategy, ToolStrategy, transformResponseFormat } from "../responses.js";
|
|
8
8
|
import { AIMessage, SystemMessage, ToolMessage } from "@langchain/core/messages";
|
|
9
9
|
import { raceWithSignal } from "@langchain/core/runnables";
|
|
10
|
-
import { Command } from "@langchain/langgraph";
|
|
10
|
+
import { Command, isCommand } from "@langchain/langgraph";
|
|
11
11
|
import { getSchemaDescription, interopParse } from "@langchain/core/utils/types";
|
|
12
12
|
|
|
13
13
|
//#region src/agents/nodes/AgentNode.ts
|
|
@@ -17,7 +17,7 @@ import { getSchemaDescription, interopParse } from "@langchain/core/utils/types"
|
|
|
17
17
|
* @returns True if the response is an internal model response, false otherwise.
|
|
18
18
|
*/
|
|
19
19
|
function isInternalModelResponse(response) {
|
|
20
|
-
return AIMessage.isInstance(response) || typeof response === "object" && response !== null && "structuredResponse" in response && "messages" in response;
|
|
20
|
+
return AIMessage.isInstance(response) || isCommand(response) || typeof response === "object" && response !== null && "structuredResponse" in response && "messages" in response;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* The name of the agent node in the state graph.
|
|
@@ -26,7 +26,6 @@ const AGENT_NODE_NAME = "model_request";
|
|
|
26
26
|
var AgentNode = class extends RunnableCallable {
|
|
27
27
|
#options;
|
|
28
28
|
#systemMessage;
|
|
29
|
-
#currentSystemMessage;
|
|
30
29
|
constructor(options) {
|
|
31
30
|
super({
|
|
32
31
|
name: options.name ?? "model",
|
|
@@ -75,31 +74,35 @@ var AgentNode = class extends RunnableCallable {
|
|
|
75
74
|
* If so, we should generate structured response (if needed) and stop
|
|
76
75
|
*/
|
|
77
76
|
const lastMessage = state.messages.at(-1);
|
|
78
|
-
if (lastMessage && ToolMessage.isInstance(lastMessage) && lastMessage.name && this.#options.shouldReturnDirect.has(lastMessage.name))
|
|
79
|
-
|
|
80
|
-
* return directly without invoking the model again
|
|
81
|
-
*/
|
|
82
|
-
return { messages: [] };
|
|
83
|
-
const response = await this.#invokeModel(state, config);
|
|
77
|
+
if (lastMessage && ToolMessage.isInstance(lastMessage) && lastMessage.name && this.#options.shouldReturnDirect.has(lastMessage.name)) return [new Command({ update: { messages: [] } })];
|
|
78
|
+
const { response, lastAiMessage, collectedCommands } = await this.#invokeModel(state, config);
|
|
84
79
|
/**
|
|
85
|
-
*
|
|
80
|
+
* structuredResponse — return as a plain state update dict (not a Command)
|
|
81
|
+
* because the structuredResponse channel uses UntrackedValue(guard=true)
|
|
82
|
+
* which only allows a single write per step.
|
|
86
83
|
*/
|
|
87
|
-
if ("structuredResponse" in response)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
84
|
+
if (typeof response === "object" && response !== null && "structuredResponse" in response && "messages" in response) {
|
|
85
|
+
const { structuredResponse, messages } = response;
|
|
86
|
+
return {
|
|
87
|
+
messages: [...state.messages, ...messages],
|
|
88
|
+
structuredResponse
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const commands = [];
|
|
92
|
+
const aiMessage = AIMessage.isInstance(response) ? response : lastAiMessage;
|
|
93
|
+
if (aiMessage) {
|
|
94
|
+
aiMessage.name = this.name;
|
|
95
|
+
aiMessage.lc_kwargs.name = this.name;
|
|
96
|
+
if (this.#areMoreStepsNeeded(state, aiMessage)) commands.push(new Command({ update: { messages: [new AIMessage({
|
|
97
|
+
content: "Sorry, need more steps to process this request.",
|
|
98
|
+
name: this.name,
|
|
99
|
+
id: aiMessage.id
|
|
100
|
+
})] } }));
|
|
101
|
+
else commands.push(new Command({ update: { messages: [aiMessage] } }));
|
|
102
|
+
}
|
|
103
|
+
if (isCommand(response) && !collectedCommands.includes(response)) commands.push(response);
|
|
104
|
+
commands.push(...collectedCommands);
|
|
105
|
+
return commands;
|
|
103
106
|
}
|
|
104
107
|
/**
|
|
105
108
|
* Derive the model from the options.
|
|
@@ -116,6 +119,18 @@ var AgentNode = class extends RunnableCallable {
|
|
|
116
119
|
const model = await this.#deriveModel();
|
|
117
120
|
const lgConfig = config;
|
|
118
121
|
/**
|
|
122
|
+
* Create a local variable for current system message to avoid concurrency issues
|
|
123
|
+
* Each invocation gets its own copy
|
|
124
|
+
*/
|
|
125
|
+
let currentSystemMessage = this.#systemMessage;
|
|
126
|
+
/**
|
|
127
|
+
* Shared tracking state for AIMessage and Command collection.
|
|
128
|
+
* lastAiMessage tracks the effective AIMessage through the middleware chain.
|
|
129
|
+
* collectedCommands accumulates Commands returned by middleware (not base handler).
|
|
130
|
+
*/
|
|
131
|
+
let lastAiMessage = null;
|
|
132
|
+
const collectedCommands = [];
|
|
133
|
+
/**
|
|
119
134
|
* Create the base handler that performs the actual model invocation
|
|
120
135
|
*/
|
|
121
136
|
const baseHandler = async (request) => {
|
|
@@ -128,38 +143,39 @@ var AgentNode = class extends RunnableCallable {
|
|
|
128
143
|
/**
|
|
129
144
|
* prepend the system message to the messages if it is not empty
|
|
130
145
|
*/
|
|
131
|
-
const messages = [...
|
|
146
|
+
const messages = [...currentSystemMessage.text === "" ? [] : [currentSystemMessage], ...request.messages];
|
|
132
147
|
const signal = mergeAbortSignals(this.#options.signal, config.signal);
|
|
133
|
-
const response = await raceWithSignal(modelWithTools.invoke(messages, {
|
|
148
|
+
const response$1 = await raceWithSignal(modelWithTools.invoke(messages, {
|
|
134
149
|
...config,
|
|
135
150
|
signal
|
|
136
151
|
}), signal);
|
|
152
|
+
lastAiMessage = response$1;
|
|
137
153
|
/**
|
|
138
154
|
* if the user requests a native schema output, try to parse the response
|
|
139
155
|
* and return the structured response if it is valid
|
|
140
156
|
*/
|
|
141
157
|
if (structuredResponseFormat?.type === "native") {
|
|
142
|
-
const structuredResponse = structuredResponseFormat.strategy.parse(response);
|
|
158
|
+
const structuredResponse = structuredResponseFormat.strategy.parse(response$1);
|
|
143
159
|
if (structuredResponse) return {
|
|
144
160
|
structuredResponse,
|
|
145
|
-
messages: [response]
|
|
161
|
+
messages: [response$1]
|
|
146
162
|
};
|
|
147
|
-
return response;
|
|
163
|
+
return response$1;
|
|
148
164
|
}
|
|
149
|
-
if (!structuredResponseFormat || !response.tool_calls) return response;
|
|
150
|
-
const toolCalls = response.tool_calls.filter((call) => call.name in structuredResponseFormat.tools);
|
|
165
|
+
if (!structuredResponseFormat || !response$1.tool_calls) return response$1;
|
|
166
|
+
const toolCalls = response$1.tool_calls.filter((call) => call.name in structuredResponseFormat.tools);
|
|
151
167
|
/**
|
|
152
168
|
* if there were not structured tool calls, we can return the response
|
|
153
169
|
*/
|
|
154
|
-
if (toolCalls.length === 0) return response;
|
|
170
|
+
if (toolCalls.length === 0) return response$1;
|
|
155
171
|
/**
|
|
156
172
|
* if there were multiple structured tool calls, we should throw an error as this
|
|
157
173
|
* scenario is not defined/supported.
|
|
158
174
|
*/
|
|
159
|
-
if (toolCalls.length > 1) return this.#handleMultipleStructuredOutputs(response, toolCalls, structuredResponseFormat);
|
|
175
|
+
if (toolCalls.length > 1) return this.#handleMultipleStructuredOutputs(response$1, toolCalls, structuredResponseFormat);
|
|
160
176
|
const toolStrategy = structuredResponseFormat.tools[toolCalls[0].name];
|
|
161
177
|
const toolMessageContent = toolStrategy?.options?.toolMessageContent;
|
|
162
|
-
return this.#handleSingleStructuredOutput(response, toolCalls[0], structuredResponseFormat, toolMessageContent ?? options.lastMessage);
|
|
178
|
+
return this.#handleSingleStructuredOutput(response$1, toolCalls[0], structuredResponseFormat, toolMessageContent ?? options.lastMessage);
|
|
163
179
|
};
|
|
164
180
|
const wrapperMiddleware = this.#options.wrapModelCallHookMiddleware ?? [];
|
|
165
181
|
let wrappedHandler = baseHandler;
|
|
@@ -216,43 +232,53 @@ var AgentNode = class extends RunnableCallable {
|
|
|
216
232
|
const invalidTools = modifiedTools.filter((tool) => isClientTool(tool) && this.#options.toolClasses.every((t) => t !== tool));
|
|
217
233
|
if (invalidTools.length > 0) throw new Error(`You have modified a tool in "wrapModelCall" hook of middleware "${currentMiddleware.name}": ${invalidTools.map((tool) => tool.name).join(", ")}. This is not supported.`);
|
|
218
234
|
let normalizedReq = req;
|
|
219
|
-
const hasSystemPromptChanged = req.systemPrompt !==
|
|
220
|
-
const hasSystemMessageChanged = req.systemMessage !==
|
|
235
|
+
const hasSystemPromptChanged = req.systemPrompt !== currentSystemMessage.text;
|
|
236
|
+
const hasSystemMessageChanged = req.systemMessage !== currentSystemMessage;
|
|
221
237
|
if (hasSystemPromptChanged && hasSystemMessageChanged) throw new Error("Cannot change both systemPrompt and systemMessage in the same request.");
|
|
222
238
|
/**
|
|
223
239
|
* Check if systemPrompt is a string was changed, if so create a new SystemMessage
|
|
224
240
|
*/
|
|
225
241
|
if (hasSystemPromptChanged) {
|
|
226
|
-
|
|
242
|
+
currentSystemMessage = new SystemMessage({ content: [{
|
|
227
243
|
type: "text",
|
|
228
244
|
text: req.systemPrompt
|
|
229
245
|
}] });
|
|
230
246
|
normalizedReq = {
|
|
231
247
|
...req,
|
|
232
|
-
systemPrompt:
|
|
233
|
-
systemMessage:
|
|
248
|
+
systemPrompt: currentSystemMessage.text,
|
|
249
|
+
systemMessage: currentSystemMessage
|
|
234
250
|
};
|
|
235
251
|
}
|
|
236
252
|
/**
|
|
237
253
|
* If the systemMessage was changed, update the current system message
|
|
238
254
|
*/
|
|
239
255
|
if (hasSystemMessageChanged) {
|
|
240
|
-
|
|
256
|
+
currentSystemMessage = new SystemMessage({ ...req.systemMessage });
|
|
241
257
|
normalizedReq = {
|
|
242
258
|
...req,
|
|
243
|
-
systemPrompt:
|
|
244
|
-
systemMessage:
|
|
259
|
+
systemPrompt: currentSystemMessage.text,
|
|
260
|
+
systemMessage: currentSystemMessage
|
|
245
261
|
};
|
|
246
262
|
}
|
|
247
|
-
|
|
263
|
+
const innerHandlerResult = await innerHandler(normalizedReq);
|
|
264
|
+
/**
|
|
265
|
+
* Normalize Commands so middleware always sees AIMessage from handler().
|
|
266
|
+
* When an inner middleware returns a Command, substitute the tracked
|
|
267
|
+
* lastAiMessage. The raw Command is still captured in innerHandlerResult
|
|
268
|
+
* for the framework's Command collection.
|
|
269
|
+
*/
|
|
270
|
+
if (isCommand(innerHandlerResult) && lastAiMessage) return lastAiMessage;
|
|
271
|
+
return innerHandlerResult;
|
|
248
272
|
};
|
|
249
273
|
if (!currentMiddleware.wrapModelCall) return handlerWithValidation(requestWithStateAndRuntime);
|
|
250
274
|
try {
|
|
251
275
|
const middlewareResponse = await currentMiddleware.wrapModelCall(requestWithStateAndRuntime, handlerWithValidation);
|
|
252
276
|
/**
|
|
253
|
-
* Validate that this specific middleware returned a valid
|
|
277
|
+
* Validate that this specific middleware returned a valid response
|
|
254
278
|
*/
|
|
255
|
-
if (!isInternalModelResponse(middlewareResponse)) throw new Error(`Invalid response from "wrapModelCall" in middleware "${currentMiddleware.name}": expected AIMessage, got ${typeof middlewareResponse}`);
|
|
279
|
+
if (!isInternalModelResponse(middlewareResponse)) throw new Error(`Invalid response from "wrapModelCall" in middleware "${currentMiddleware.name}": expected AIMessage or Command, got ${typeof middlewareResponse}`);
|
|
280
|
+
if (AIMessage.isInstance(middlewareResponse)) lastAiMessage = middlewareResponse;
|
|
281
|
+
else if (isCommand(middlewareResponse)) collectedCommands.push(middlewareResponse);
|
|
256
282
|
return middlewareResponse;
|
|
257
283
|
} catch (error) {
|
|
258
284
|
throw MiddlewareError.wrap(error, currentMiddleware.name);
|
|
@@ -265,11 +291,11 @@ var AgentNode = class extends RunnableCallable {
|
|
|
265
291
|
* Reset current system prompt to initial state and convert to string using .text getter
|
|
266
292
|
* for backwards compatibility with ModelRequest
|
|
267
293
|
*/
|
|
268
|
-
|
|
294
|
+
currentSystemMessage = this.#systemMessage;
|
|
269
295
|
const initialRequest = {
|
|
270
296
|
model,
|
|
271
|
-
systemPrompt:
|
|
272
|
-
systemMessage:
|
|
297
|
+
systemPrompt: currentSystemMessage?.text,
|
|
298
|
+
systemMessage: currentSystemMessage,
|
|
273
299
|
messages: state.messages,
|
|
274
300
|
tools: this.#options.toolClasses,
|
|
275
301
|
state,
|
|
@@ -280,7 +306,12 @@ var AgentNode = class extends RunnableCallable {
|
|
|
280
306
|
signal: lgConfig.signal
|
|
281
307
|
})
|
|
282
308
|
};
|
|
283
|
-
|
|
309
|
+
const response = await wrappedHandler(initialRequest);
|
|
310
|
+
return {
|
|
311
|
+
response,
|
|
312
|
+
lastAiMessage,
|
|
313
|
+
collectedCommands
|
|
314
|
+
};
|
|
284
315
|
}
|
|
285
316
|
/**
|
|
286
317
|
* If the model returns multiple structured outputs, we need to handle it.
|
|
@@ -438,9 +469,13 @@ var AgentNode = class extends RunnableCallable {
|
|
|
438
469
|
const modelRunnable = this.#options.includeAgentName === "inline" ? withAgentName(modelWithTools, this.#options.includeAgentName) : modelWithTools;
|
|
439
470
|
return modelRunnable;
|
|
440
471
|
}
|
|
472
|
+
/**
|
|
473
|
+
* Returns internal bookkeeping state for StateManager, not graph output.
|
|
474
|
+
* The return shape differs from the node's output type (Command).
|
|
475
|
+
*/
|
|
441
476
|
getState() {
|
|
442
477
|
const state = super.getState();
|
|
443
|
-
const origState = state && !(state
|
|
478
|
+
const origState = state && !isCommand(state) ? state : {};
|
|
444
479
|
return {
|
|
445
480
|
messages: [],
|
|
446
481
|
...origState
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentNode.js","names":["response: unknown","options: AgentNodeOptions<StructuredResponseFormat, ContextSchema>","#run","#options","#systemMessage","model: string | LanguageModelLike","state: InternalAgentState<StructuredResponseFormat>","config: RunnableConfig","#invokeModel","#areMoreStepsNeeded","options: {\n lastMessage?: string;\n }","#deriveModel","request: ModelRequest","#getResponseFormat","#bindTools","#currentSystemMessage","#handleMultipleStructuredOutputs","#handleSingleStructuredOutput","wrappedHandler: (\n request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ) => Promise<InternalModelResponse<StructuredResponseFormat>>","request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >","runtime: Runtime<unknown>","requestWithStateAndRuntime: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >","req: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >","initialRequest: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >","response: AIMessage","toolCalls: ToolCall[]","responseFormat: ToolResponseFormat","#handleToolStrategyError","toolCall: ToolCall","lastMessage?: string","error: ToolStrategyError","response: BaseMessage","model: LanguageModelLike","preparedOptions: ModelRequest | undefined","structuredResponseFormat: ResponseFormat | undefined","options: Partial<BaseChatModelCallOptions>"],"sources":["../../../src/agents/nodes/AgentNode.ts"],"sourcesContent":["/* eslint-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 { Command, type LangGraphRunnableConfig } from \"@langchain/langgraph\";\nimport { type LanguageModelLike } from \"@langchain/core/language_models/base\";\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 { MultipleStructuredOutputsError, MiddlewareError } from \"../errors.js\";\nimport { RunnableCallable } from \"../RunnableCallable.js\";\nimport {\n bindTools,\n validateLLMHasNoBoundTools,\n hasToolCalls,\n isClientTool,\n} from \"../utils.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} 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\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 (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\n | AnyAnnotationRoot\n | InteropZodObject = 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 () => 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\n | AnyAnnotationRoot\n | InteropZodObject = AnyAnnotationRoot,\n> extends RunnableCallable<\n InternalAgentState<StructuredResponseFormat>,\n | (\n | { messages: BaseMessage[] }\n | { structuredResponse: StructuredResponseFormat }\n )\n | Command\n> {\n #options: AgentNodeOptions<StructuredResponseFormat, ContextSchema>;\n #systemMessage: SystemMessage;\n #currentSystemMessage: 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 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 #getResponseFormat(\n model: string | LanguageModelLike\n ): ResponseFormat | undefined {\n if (!this.#options.responseFormat) {\n return undefined;\n }\n\n const strategies = transformResponseFormat(\n this.#options.responseFormat,\n undefined,\n model\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 /**\n * return directly without invoking the model again\n */\n return { messages: [] };\n }\n\n const response = await this.#invokeModel(state, config);\n\n /**\n * if we were able to generate a structured response, return it\n */\n if (\"structuredResponse\" in response) {\n return {\n messages: [...state.messages, ...(response.messages || [])],\n structuredResponse: response.structuredResponse,\n };\n }\n\n /**\n * if we need to direct the agent to the model, return the update\n */\n if (response instanceof Command) {\n return response;\n }\n\n response.name = this.name;\n response.lc_kwargs.name = this.name;\n\n if (this.#areMoreStepsNeeded(state, response)) {\n return {\n messages: [\n new AIMessage({\n content: \"Sorry, need more steps to process this request.\",\n name: this.name,\n id: response.id,\n }),\n ],\n };\n }\n\n return { messages: [response] };\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<AIMessage | ResponseHandlerResult<StructuredResponseFormat>> {\n const model = await this.#deriveModel();\n const lgConfig = config as LangGraphRunnableConfig;\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 = this.#getResponseFormat(request.model);\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 ...(this.#currentSystemMessage.text === \"\"\n ? []\n : [this.#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 /**\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 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 [middleware, getMiddlewareState] = wrapperMiddleware[i];\n if (middleware.wrapModelCall) {\n const innerHandler = wrappedHandler;\n const currentMiddleware = middleware;\n const currentGetState = getMiddlewareState;\n\n wrappedHandler = async (\n request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ): Promise<InternalModelResponse<StructuredResponseFormat>> => {\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 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 ...currentGetState(),\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 /**\n * Verify that the user didn't add any new tools.\n * We can't allow this as the ToolNode is already initiated with given tools.\n */\n const modifiedTools = req.tools ?? [];\n const newTools = modifiedTools.filter(\n (tool) =>\n isClientTool(tool) &&\n !this.#options.toolClasses.some((t) => t.name === tool.name)\n );\n if (newTools.length > 0) {\n throw new Error(\n `You have added a new tool in \"wrapModelCall\" hook of middleware \"${\n currentMiddleware.name\n }\": ${newTools\n .map((tool) => tool.name)\n .join(\", \")}. This is not supported.`\n );\n }\n\n /**\n * Verify that user has not added or modified a tool with the same name.\n * We can't allow this as the ToolNode is already initiated with given tools.\n */\n const invalidTools = modifiedTools.filter(\n (tool) =>\n isClientTool(tool) &&\n this.#options.toolClasses.every((t) => t !== tool)\n );\n if (invalidTools.length > 0) {\n throw new Error(\n `You have modified a tool in \"wrapModelCall\" hook of middleware \"${\n currentMiddleware.name\n }\": ${invalidTools\n .map((tool) => tool.name)\n .join(\", \")}. This is not supported.`\n );\n }\n\n let normalizedReq = req;\n const hasSystemPromptChanged =\n req.systemPrompt !== this.#currentSystemMessage.text;\n const hasSystemMessageChanged =\n req.systemMessage !== this.#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 this.#currentSystemMessage = new SystemMessage({\n content: [{ type: \"text\", text: req.systemPrompt }],\n });\n normalizedReq = {\n ...req,\n systemPrompt: this.#currentSystemMessage.text,\n systemMessage: this.#currentSystemMessage,\n };\n }\n /**\n * If the systemMessage was changed, update the current system message\n */\n if (hasSystemMessageChanged) {\n this.#currentSystemMessage = new SystemMessage({\n ...req.systemMessage,\n });\n normalizedReq = {\n ...req,\n systemPrompt: this.#currentSystemMessage.text,\n systemMessage: this.#currentSystemMessage,\n };\n }\n\n return innerHandler(normalizedReq);\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 AIMessage\n */\n if (!isInternalModelResponse(middlewareResponse)) {\n throw new Error(\n `Invalid response from \"wrapModelCall\" in middleware \"${\n currentMiddleware.name\n }\": expected AIMessage, got ${typeof middlewareResponse}`\n );\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 this.#currentSystemMessage = this.#systemMessage;\n const initialRequest: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n > = {\n model,\n systemPrompt: this.#currentSystemMessage?.text,\n systemMessage: this.#currentSystemMessage,\n messages: state.messages,\n tools: this.#options.toolClasses,\n state,\n runtime: Object.freeze({\n context: lgConfig?.context,\n writer: lgConfig.writer,\n interrupt: lgConfig.interrupt,\n signal: lgConfig.signal,\n }) as Runtime<unknown>,\n };\n\n return wrappedHandler(initialRequest);\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<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 response_format: {\n type: \"json_schema\",\n json_schema: jsonSchemaParams,\n },\n output_format: {\n type: \"json_schema\",\n schema: structuredResponseFormat.strategy.schema,\n },\n headers: {\n \"anthropic-beta\": \"structured-outputs-2025-11-13\",\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 getState(): {\n messages: BaseMessage[];\n } {\n const state = super.getState();\n const origState = state && !(state instanceof Command) ? state : {};\n\n return {\n messages: [],\n ...origState,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentNode.js","names":["response: unknown","options: AgentNodeOptions<StructuredResponseFormat, ContextSchema>","#run","#options","#systemMessage","model: string | LanguageModelLike","state: InternalAgentState<StructuredResponseFormat>","config: RunnableConfig","#invokeModel","commands: Command[]","aiMessage: AIMessage | null","#areMoreStepsNeeded","options: {\n lastMessage?: string;\n }","#deriveModel","lastAiMessage: AIMessage | null","collectedCommands: Command[]","request: ModelRequest","#getResponseFormat","#bindTools","response","#handleMultipleStructuredOutputs","#handleSingleStructuredOutput","wrappedHandler: (\n request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ) => Promise<InternalModelResponse<StructuredResponseFormat>>","request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >","runtime: Runtime<unknown>","requestWithStateAndRuntime: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >","req: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >","initialRequest: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >","response: AIMessage","toolCalls: ToolCall[]","responseFormat: ToolResponseFormat","#handleToolStrategyError","toolCall: ToolCall","lastMessage?: string","error: ToolStrategyError","response: BaseMessage","model: LanguageModelLike","preparedOptions: ModelRequest | undefined","structuredResponseFormat: ResponseFormat | undefined","options: Partial<BaseChatModelCallOptions>"],"sources":["../../../src/agents/nodes/AgentNode.ts"],"sourcesContent":["/* eslint-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 LanguageModelLike } from \"@langchain/core/language_models/base\";\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 { MultipleStructuredOutputsError, MiddlewareError } from \"../errors.js\";\nimport { RunnableCallable } from \"../RunnableCallable.js\";\nimport {\n bindTools,\n validateLLMHasNoBoundTools,\n hasToolCalls,\n isClientTool,\n} from \"../utils.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} 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 () => 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 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 #getResponseFormat(\n model: string | LanguageModelLike\n ): ResponseFormat | undefined {\n if (!this.#options.responseFormat) {\n return undefined;\n }\n\n const strategies = transformResponseFormat(\n this.#options.responseFormat,\n undefined,\n model\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 = this.#getResponseFormat(request.model);\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 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 [middleware, getMiddlewareState] = wrapperMiddleware[i];\n if (middleware.wrapModelCall) {\n const innerHandler = wrappedHandler;\n const currentMiddleware = middleware;\n const currentGetState = getMiddlewareState;\n\n wrappedHandler = async (\n request: ModelRequest<\n InternalAgentState<StructuredResponseFormat>,\n unknown\n >\n ): Promise<InternalModelResponse<StructuredResponseFormat>> => {\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 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 ...currentGetState(),\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 /**\n * Verify that the user didn't add any new tools.\n * We can't allow this as the ToolNode is already initiated with given tools.\n */\n const modifiedTools = req.tools ?? [];\n const newTools = modifiedTools.filter(\n (tool) =>\n isClientTool(tool) &&\n !this.#options.toolClasses.some((t) => t.name === tool.name)\n );\n if (newTools.length > 0) {\n throw new Error(\n `You have added a new tool in \"wrapModelCall\" hook of middleware \"${\n currentMiddleware.name\n }\": ${newTools\n .map((tool) => tool.name)\n .join(\", \")}. This is not supported.`\n );\n }\n\n /**\n * Verify that user has not added or modified a tool with the same name.\n * We can't allow this as the ToolNode is already initiated with given tools.\n */\n const invalidTools = modifiedTools.filter(\n (tool) =>\n isClientTool(tool) &&\n this.#options.toolClasses.every((t) => t !== tool)\n );\n if (invalidTools.length > 0) {\n throw new Error(\n `You have modified a tool in \"wrapModelCall\" hook of middleware \"${\n currentMiddleware.name\n }\": ${invalidTools\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 middleware returns a Command, substitute the tracked\n * lastAiMessage. The raw Command is still captured in innerHandlerResult\n * for the framework's Command collection.\n */\n if (isCommand(innerHandlerResult) && lastAiMessage) {\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 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 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<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 response_format: {\n type: \"json_schema\",\n json_schema: jsonSchemaParams,\n },\n output_format: {\n type: \"json_schema\",\n schema: structuredResponseFormat.strategy.schema,\n },\n headers: {\n \"anthropic-beta\": \"structured-outputs-2025-11-13\",\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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"}
|
|
@@ -66,12 +66,11 @@ var MiddlewareNode = class extends require_RunnableCallable.RunnableCallable {
|
|
|
66
66
|
}))
|
|
67
67
|
);
|
|
68
68
|
/**
|
|
69
|
-
* If result is undefined,
|
|
69
|
+
* If result is undefined, the hook made no state changes — return
|
|
70
|
+
* only the jumpTo sentinel so we don't re-emit every input key as
|
|
71
|
+
* a state update.
|
|
70
72
|
*/
|
|
71
|
-
if (!result) return {
|
|
72
|
-
...state,
|
|
73
|
-
jumpTo: void 0
|
|
74
|
-
};
|
|
73
|
+
if (!result) return { jumpTo: void 0 };
|
|
75
74
|
/**
|
|
76
75
|
* Verify that the jump target is allowed for the middleware
|
|
77
76
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.cjs","names":["RunnableCallable","fields: RunnableCallableArgs<TStateSchema, NodeOutput<TStateSchema>>","options: MiddlewareNodeOptions","#options","invokeState: TStateSchema","config?: LangGraphRunnableConfig","relevantContext: Record<string, unknown>","state: TStateSchema","runtime: Runtime<TContextSchema>","jumpToConstraint: JumpToTarget[] | undefined","constraint: string | undefined","getHookConstraint","derivePrivateState"],"sources":["../../../src/agents/nodes/middleware.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { z } from \"zod/v4\";\nimport { LangGraphRunnableConfig, Command } from \"@langchain/langgraph\";\nimport { interopParse } from \"@langchain/core/utils/types\";\n\nimport { RunnableCallable, RunnableCallableArgs } from \"../RunnableCallable.js\";\nimport type { JumpToTarget } from \"../constants.js\";\nimport type { Runtime } from \"../runtime.js\";\nimport type { AgentMiddleware, MiddlewareResult } from \"../middleware/types.js\";\nimport { derivePrivateState } from \"./utils.js\";\nimport { getHookConstraint } from \"../middleware/utils.js\";\n\n/**\n * Named class for context objects to provide better error messages\n */\nclass AgentContext {}\nclass AgentRuntime {}\n\ntype NodeOutput<TStateSchema extends Record<string, any>> =\n | TStateSchema\n | Command<any, TStateSchema, string
|
|
1
|
+
{"version":3,"file":"middleware.cjs","names":["RunnableCallable","fields: RunnableCallableArgs<TStateSchema, NodeOutput<TStateSchema>>","options: MiddlewareNodeOptions","#options","invokeState: TStateSchema","config?: LangGraphRunnableConfig","relevantContext: Record<string, unknown>","state: TStateSchema","runtime: Runtime<TContextSchema>","jumpToConstraint: JumpToTarget[] | undefined","constraint: string | undefined","getHookConstraint","derivePrivateState"],"sources":["../../../src/agents/nodes/middleware.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { z } from \"zod/v4\";\nimport { LangGraphRunnableConfig, Command } from \"@langchain/langgraph\";\nimport { interopParse } from \"@langchain/core/utils/types\";\n\nimport { RunnableCallable, RunnableCallableArgs } from \"../RunnableCallable.js\";\nimport type { JumpToTarget } from \"../constants.js\";\nimport type { Runtime } from \"../runtime.js\";\nimport type { AgentMiddleware, MiddlewareResult } from \"../middleware/types.js\";\nimport { derivePrivateState } from \"./utils.js\";\nimport { getHookConstraint } from \"../middleware/utils.js\";\n\n/**\n * Named class for context objects to provide better error messages\n */\nclass AgentContext {}\nclass AgentRuntime {}\n\ntype NodeOutput<TStateSchema extends Record<string, any>> =\n | TStateSchema\n | Command<any, TStateSchema, string>\n | { jumpTo?: JumpToTarget };\n\nexport interface MiddlewareNodeOptions {\n getState: () => Record<string, unknown>;\n}\n\nexport abstract class MiddlewareNode<\n TStateSchema extends Record<string, any>,\n TContextSchema extends Record<string, any>,\n> extends RunnableCallable<TStateSchema, NodeOutput<TStateSchema>> {\n #options: MiddlewareNodeOptions;\n\n abstract middleware: AgentMiddleware<\n z.ZodObject<z.ZodRawShape>,\n z.ZodObject<z.ZodRawShape>\n >;\n\n constructor(\n fields: RunnableCallableArgs<TStateSchema, NodeOutput<TStateSchema>>,\n options: MiddlewareNodeOptions\n ) {\n super(fields);\n this.#options = options;\n }\n\n abstract runHook(\n state: TStateSchema,\n config?: Runtime<TContextSchema>\n ): Promise<MiddlewareResult<TStateSchema>> | MiddlewareResult<TStateSchema>;\n\n async invokeMiddleware(\n invokeState: TStateSchema,\n config?: LangGraphRunnableConfig\n ): Promise<NodeOutput<TStateSchema>> {\n /**\n * Filter context based on middleware's contextSchema\n */\n let filteredContext = {} as TContextSchema;\n /**\n * Parse context using middleware's contextSchema to apply defaults and validation\n */\n if (this.middleware.contextSchema) {\n /**\n * Extract only the fields relevant to this middleware's schema\n */\n const schemaShape = this.middleware.contextSchema?.shape;\n if (schemaShape) {\n const relevantContext: Record<string, unknown> = {};\n const invokeContext = config?.context || {};\n for (const key of Object.keys(schemaShape)) {\n if (key in invokeContext) {\n relevantContext[key] = invokeContext[key];\n }\n }\n /**\n * Parse to apply defaults and validation, even if relevantContext is empty\n * This will throw if required fields are missing and no defaults exist\n */\n filteredContext = interopParse(\n this.middleware.contextSchema,\n relevantContext\n ) as TContextSchema;\n }\n }\n\n const state: TStateSchema = {\n ...this.#options.getState(),\n ...invokeState,\n /**\n * don't overwrite possible outdated messages from other middleware nodes\n */\n messages: invokeState.messages,\n };\n\n /**\n * ToDo: implement later\n */\n const runtime: Runtime<TContextSchema> = {\n context: filteredContext,\n writer: config?.writer,\n interrupt: config?.interrupt,\n signal: config?.signal,\n };\n\n const result = await this.runHook(\n state,\n /**\n * assign runtime and context values into empty named class\n * instances to create a better error message.\n */\n Object.freeze(\n Object.assign(new AgentRuntime(), {\n ...runtime,\n context: Object.freeze(\n Object.assign(new AgentContext(), filteredContext)\n ),\n })\n )\n );\n\n /**\n * If result is undefined, the hook made no state changes — return\n * only the jumpTo sentinel so we don't re-emit every input key as\n * a state update.\n */\n if (!result) {\n return { jumpTo: undefined };\n }\n\n /**\n * Verify that the jump target is allowed for the middleware\n */\n let jumpToConstraint: JumpToTarget[] | undefined;\n let constraint: string | undefined;\n\n if (this.name?.startsWith(\"BeforeAgentNode_\")) {\n jumpToConstraint = getHookConstraint(this.middleware.beforeAgent);\n constraint = \"beforeAgent.canJumpTo\";\n } else if (this.name?.startsWith(\"BeforeModelNode_\")) {\n jumpToConstraint = getHookConstraint(this.middleware.beforeModel);\n constraint = \"beforeModel.canJumpTo\";\n } else if (this.name?.startsWith(\"AfterAgentNode_\")) {\n jumpToConstraint = getHookConstraint(this.middleware.afterAgent);\n constraint = \"afterAgent.canJumpTo\";\n } else if (this.name?.startsWith(\"AfterModelNode_\")) {\n jumpToConstraint = getHookConstraint(this.middleware.afterModel);\n constraint = \"afterModel.canJumpTo\";\n }\n\n if (\n typeof result.jumpTo === \"string\" &&\n !jumpToConstraint?.includes(result.jumpTo as JumpToTarget)\n ) {\n const suggestion =\n jumpToConstraint && jumpToConstraint.length > 0\n ? `must be one of: ${jumpToConstraint?.join(\", \")}.`\n : constraint\n ? `no ${constraint} defined in middleware ${this.middleware.name}`\n : \"\";\n throw new Error(`Invalid jump target: ${result.jumpTo}, ${suggestion}.`);\n }\n\n /**\n * If result is a control action, handle it\n */\n if (typeof result === \"object\" && \"type\" in result) {\n // Handle control actions\n if (result.type === \"terminate\") {\n if (result.error) {\n throw result.error;\n }\n return {\n ...state,\n ...(result.result || {}),\n jumpTo: result.jumpTo,\n };\n }\n\n throw new Error(`Invalid control action: ${JSON.stringify(result)}`);\n }\n\n /**\n * If result is a state update, merge it with current state\n */\n return { ...state, ...result, jumpTo: result.jumpTo };\n }\n\n get nodeOptions() {\n return {\n input: derivePrivateState(this.middleware.stateSchema),\n };\n }\n}\n"],"mappings":";;;;;;;;;;AAeA,IAAM,eAAN,MAAmB,CAAE;AACrB,IAAM,eAAN,MAAmB,CAAE;AAWrB,IAAsB,iBAAtB,cAGUA,0CAAyD;CACjE;CAOA,YACEC,QACAC,SACA;EACA,MAAM,OAAO;EACb,KAAKC,WAAW;CACjB;CAOD,MAAM,iBACJC,aACAC,QACmC;;;;EAInC,IAAI,kBAAkB,CAAE;;;;AAIxB,MAAI,KAAK,WAAW,eAAe;;;;GAIjC,MAAM,cAAc,KAAK,WAAW,eAAe;AACnD,OAAI,aAAa;IACf,MAAMC,kBAA2C,CAAE;IACnD,MAAM,gBAAgB,QAAQ,WAAW,CAAE;AAC3C,SAAK,MAAM,OAAO,OAAO,KAAK,YAAY,CACxC,KAAI,OAAO,eACT,gBAAgB,OAAO,cAAc;;;;;IAOzC,iEACE,KAAK,WAAW,eAChB,gBACD;GACF;EACF;EAED,MAAMC,QAAsB;GAC1B,GAAG,KAAKJ,SAAS,UAAU;GAC3B,GAAG;GAIH,UAAU,YAAY;EACvB;;;;EAKD,MAAMK,UAAmC;GACvC,SAAS;GACT,QAAQ,QAAQ;GAChB,WAAW,QAAQ;GACnB,QAAQ,QAAQ;EACjB;EAED,MAAM,SAAS,MAAM,KAAK;GACxB;;;;;GAKA,OAAO,OACL,OAAO,OAAO,IAAI,gBAAgB;IAChC,GAAG;IACH,SAAS,OAAO,OACd,OAAO,OAAO,IAAI,gBAAgB,gBAAgB,CACnD;GACF,EAAC,CACH;GACF;;;;;;AAOD,MAAI,CAAC,OACH,QAAO,EAAE,QAAQ,OAAW;;;;EAM9B,IAAIC;EACJ,IAAIC;AAEJ,MAAI,KAAK,MAAM,WAAW,mBAAmB,EAAE;GAC7C,mBAAmBC,kCAAkB,KAAK,WAAW,YAAY;GACjE,aAAa;EACd,WAAU,KAAK,MAAM,WAAW,mBAAmB,EAAE;GACpD,mBAAmBA,kCAAkB,KAAK,WAAW,YAAY;GACjE,aAAa;EACd,WAAU,KAAK,MAAM,WAAW,kBAAkB,EAAE;GACnD,mBAAmBA,kCAAkB,KAAK,WAAW,WAAW;GAChE,aAAa;EACd,WAAU,KAAK,MAAM,WAAW,kBAAkB,EAAE;GACnD,mBAAmBA,kCAAkB,KAAK,WAAW,WAAW;GAChE,aAAa;EACd;AAED,MACE,OAAO,OAAO,WAAW,YACzB,CAAC,kBAAkB,SAAS,OAAO,OAAuB,EAC1D;GACA,MAAM,aACJ,oBAAoB,iBAAiB,SAAS,IAC1C,CAAC,gBAAgB,EAAE,kBAAkB,KAAK,KAAK,CAAC,CAAC,CAAC,GAClD,aACE,CAAC,GAAG,EAAE,WAAW,uBAAuB,EAAE,KAAK,WAAW,MAAM,GAChE;AACR,SAAM,IAAI,MAAM,CAAC,qBAAqB,EAAE,OAAO,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;EACxE;;;;AAKD,MAAI,OAAO,WAAW,YAAY,UAAU,QAAQ;AAElD,OAAI,OAAO,SAAS,aAAa;AAC/B,QAAI,OAAO,MACT,OAAM,OAAO;AAEf,WAAO;KACL,GAAG;KACH,GAAI,OAAO,UAAU,CAAE;KACvB,QAAQ,OAAO;IAChB;GACF;AAED,SAAM,IAAI,MAAM,CAAC,wBAAwB,EAAE,KAAK,UAAU,OAAO,EAAE;EACpE;;;;AAKD,SAAO;GAAE,GAAG;GAAO,GAAG;GAAQ,QAAQ,OAAO;EAAQ;CACtD;CAED,IAAI,cAAc;AAChB,SAAO,EACL,OAAOC,iCAAmB,KAAK,WAAW,YAAY,CACvD;CACF;AACF"}
|
|
@@ -65,12 +65,11 @@ var MiddlewareNode = class extends RunnableCallable {
|
|
|
65
65
|
}))
|
|
66
66
|
);
|
|
67
67
|
/**
|
|
68
|
-
* If result is undefined,
|
|
68
|
+
* If result is undefined, the hook made no state changes — return
|
|
69
|
+
* only the jumpTo sentinel so we don't re-emit every input key as
|
|
70
|
+
* a state update.
|
|
69
71
|
*/
|
|
70
|
-
if (!result) return {
|
|
71
|
-
...state,
|
|
72
|
-
jumpTo: void 0
|
|
73
|
-
};
|
|
72
|
+
if (!result) return { jumpTo: void 0 };
|
|
74
73
|
/**
|
|
75
74
|
* Verify that the jump target is allowed for the middleware
|
|
76
75
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","names":["fields: RunnableCallableArgs<TStateSchema, NodeOutput<TStateSchema>>","options: MiddlewareNodeOptions","#options","invokeState: TStateSchema","config?: LangGraphRunnableConfig","relevantContext: Record<string, unknown>","state: TStateSchema","runtime: Runtime<TContextSchema>","jumpToConstraint: JumpToTarget[] | undefined","constraint: string | undefined"],"sources":["../../../src/agents/nodes/middleware.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { z } from \"zod/v4\";\nimport { LangGraphRunnableConfig, Command } from \"@langchain/langgraph\";\nimport { interopParse } from \"@langchain/core/utils/types\";\n\nimport { RunnableCallable, RunnableCallableArgs } from \"../RunnableCallable.js\";\nimport type { JumpToTarget } from \"../constants.js\";\nimport type { Runtime } from \"../runtime.js\";\nimport type { AgentMiddleware, MiddlewareResult } from \"../middleware/types.js\";\nimport { derivePrivateState } from \"./utils.js\";\nimport { getHookConstraint } from \"../middleware/utils.js\";\n\n/**\n * Named class for context objects to provide better error messages\n */\nclass AgentContext {}\nclass AgentRuntime {}\n\ntype NodeOutput<TStateSchema extends Record<string, any>> =\n | TStateSchema\n | Command<any, TStateSchema, string
|
|
1
|
+
{"version":3,"file":"middleware.js","names":["fields: RunnableCallableArgs<TStateSchema, NodeOutput<TStateSchema>>","options: MiddlewareNodeOptions","#options","invokeState: TStateSchema","config?: LangGraphRunnableConfig","relevantContext: Record<string, unknown>","state: TStateSchema","runtime: Runtime<TContextSchema>","jumpToConstraint: JumpToTarget[] | undefined","constraint: string | undefined"],"sources":["../../../src/agents/nodes/middleware.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { z } from \"zod/v4\";\nimport { LangGraphRunnableConfig, Command } from \"@langchain/langgraph\";\nimport { interopParse } from \"@langchain/core/utils/types\";\n\nimport { RunnableCallable, RunnableCallableArgs } from \"../RunnableCallable.js\";\nimport type { JumpToTarget } from \"../constants.js\";\nimport type { Runtime } from \"../runtime.js\";\nimport type { AgentMiddleware, MiddlewareResult } from \"../middleware/types.js\";\nimport { derivePrivateState } from \"./utils.js\";\nimport { getHookConstraint } from \"../middleware/utils.js\";\n\n/**\n * Named class for context objects to provide better error messages\n */\nclass AgentContext {}\nclass AgentRuntime {}\n\ntype NodeOutput<TStateSchema extends Record<string, any>> =\n | TStateSchema\n | Command<any, TStateSchema, string>\n | { jumpTo?: JumpToTarget };\n\nexport interface MiddlewareNodeOptions {\n getState: () => Record<string, unknown>;\n}\n\nexport abstract class MiddlewareNode<\n TStateSchema extends Record<string, any>,\n TContextSchema extends Record<string, any>,\n> extends RunnableCallable<TStateSchema, NodeOutput<TStateSchema>> {\n #options: MiddlewareNodeOptions;\n\n abstract middleware: AgentMiddleware<\n z.ZodObject<z.ZodRawShape>,\n z.ZodObject<z.ZodRawShape>\n >;\n\n constructor(\n fields: RunnableCallableArgs<TStateSchema, NodeOutput<TStateSchema>>,\n options: MiddlewareNodeOptions\n ) {\n super(fields);\n this.#options = options;\n }\n\n abstract runHook(\n state: TStateSchema,\n config?: Runtime<TContextSchema>\n ): Promise<MiddlewareResult<TStateSchema>> | MiddlewareResult<TStateSchema>;\n\n async invokeMiddleware(\n invokeState: TStateSchema,\n config?: LangGraphRunnableConfig\n ): Promise<NodeOutput<TStateSchema>> {\n /**\n * Filter context based on middleware's contextSchema\n */\n let filteredContext = {} as TContextSchema;\n /**\n * Parse context using middleware's contextSchema to apply defaults and validation\n */\n if (this.middleware.contextSchema) {\n /**\n * Extract only the fields relevant to this middleware's schema\n */\n const schemaShape = this.middleware.contextSchema?.shape;\n if (schemaShape) {\n const relevantContext: Record<string, unknown> = {};\n const invokeContext = config?.context || {};\n for (const key of Object.keys(schemaShape)) {\n if (key in invokeContext) {\n relevantContext[key] = invokeContext[key];\n }\n }\n /**\n * Parse to apply defaults and validation, even if relevantContext is empty\n * This will throw if required fields are missing and no defaults exist\n */\n filteredContext = interopParse(\n this.middleware.contextSchema,\n relevantContext\n ) as TContextSchema;\n }\n }\n\n const state: TStateSchema = {\n ...this.#options.getState(),\n ...invokeState,\n /**\n * don't overwrite possible outdated messages from other middleware nodes\n */\n messages: invokeState.messages,\n };\n\n /**\n * ToDo: implement later\n */\n const runtime: Runtime<TContextSchema> = {\n context: filteredContext,\n writer: config?.writer,\n interrupt: config?.interrupt,\n signal: config?.signal,\n };\n\n const result = await this.runHook(\n state,\n /**\n * assign runtime and context values into empty named class\n * instances to create a better error message.\n */\n Object.freeze(\n Object.assign(new AgentRuntime(), {\n ...runtime,\n context: Object.freeze(\n Object.assign(new AgentContext(), filteredContext)\n ),\n })\n )\n );\n\n /**\n * If result is undefined, the hook made no state changes — return\n * only the jumpTo sentinel so we don't re-emit every input key as\n * a state update.\n */\n if (!result) {\n return { jumpTo: undefined };\n }\n\n /**\n * Verify that the jump target is allowed for the middleware\n */\n let jumpToConstraint: JumpToTarget[] | undefined;\n let constraint: string | undefined;\n\n if (this.name?.startsWith(\"BeforeAgentNode_\")) {\n jumpToConstraint = getHookConstraint(this.middleware.beforeAgent);\n constraint = \"beforeAgent.canJumpTo\";\n } else if (this.name?.startsWith(\"BeforeModelNode_\")) {\n jumpToConstraint = getHookConstraint(this.middleware.beforeModel);\n constraint = \"beforeModel.canJumpTo\";\n } else if (this.name?.startsWith(\"AfterAgentNode_\")) {\n jumpToConstraint = getHookConstraint(this.middleware.afterAgent);\n constraint = \"afterAgent.canJumpTo\";\n } else if (this.name?.startsWith(\"AfterModelNode_\")) {\n jumpToConstraint = getHookConstraint(this.middleware.afterModel);\n constraint = \"afterModel.canJumpTo\";\n }\n\n if (\n typeof result.jumpTo === \"string\" &&\n !jumpToConstraint?.includes(result.jumpTo as JumpToTarget)\n ) {\n const suggestion =\n jumpToConstraint && jumpToConstraint.length > 0\n ? `must be one of: ${jumpToConstraint?.join(\", \")}.`\n : constraint\n ? `no ${constraint} defined in middleware ${this.middleware.name}`\n : \"\";\n throw new Error(`Invalid jump target: ${result.jumpTo}, ${suggestion}.`);\n }\n\n /**\n * If result is a control action, handle it\n */\n if (typeof result === \"object\" && \"type\" in result) {\n // Handle control actions\n if (result.type === \"terminate\") {\n if (result.error) {\n throw result.error;\n }\n return {\n ...state,\n ...(result.result || {}),\n jumpTo: result.jumpTo,\n };\n }\n\n throw new Error(`Invalid control action: ${JSON.stringify(result)}`);\n }\n\n /**\n * If result is a state update, merge it with current state\n */\n return { ...state, ...result, jumpTo: result.jumpTo };\n }\n\n get nodeOptions() {\n return {\n input: derivePrivateState(this.middleware.stateSchema),\n };\n }\n}\n"],"mappings":";;;;;;;;;AAeA,IAAM,eAAN,MAAmB,CAAE;AACrB,IAAM,eAAN,MAAmB,CAAE;AAWrB,IAAsB,iBAAtB,cAGU,iBAAyD;CACjE;CAOA,YACEA,QACAC,SACA;EACA,MAAM,OAAO;EACb,KAAKC,WAAW;CACjB;CAOD,MAAM,iBACJC,aACAC,QACmC;;;;EAInC,IAAI,kBAAkB,CAAE;;;;AAIxB,MAAI,KAAK,WAAW,eAAe;;;;GAIjC,MAAM,cAAc,KAAK,WAAW,eAAe;AACnD,OAAI,aAAa;IACf,MAAMC,kBAA2C,CAAE;IACnD,MAAM,gBAAgB,QAAQ,WAAW,CAAE;AAC3C,SAAK,MAAM,OAAO,OAAO,KAAK,YAAY,CACxC,KAAI,OAAO,eACT,gBAAgB,OAAO,cAAc;;;;;IAOzC,kBAAkB,aAChB,KAAK,WAAW,eAChB,gBACD;GACF;EACF;EAED,MAAMC,QAAsB;GAC1B,GAAG,KAAKJ,SAAS,UAAU;GAC3B,GAAG;GAIH,UAAU,YAAY;EACvB;;;;EAKD,MAAMK,UAAmC;GACvC,SAAS;GACT,QAAQ,QAAQ;GAChB,WAAW,QAAQ;GACnB,QAAQ,QAAQ;EACjB;EAED,MAAM,SAAS,MAAM,KAAK;GACxB;;;;;GAKA,OAAO,OACL,OAAO,OAAO,IAAI,gBAAgB;IAChC,GAAG;IACH,SAAS,OAAO,OACd,OAAO,OAAO,IAAI,gBAAgB,gBAAgB,CACnD;GACF,EAAC,CACH;GACF;;;;;;AAOD,MAAI,CAAC,OACH,QAAO,EAAE,QAAQ,OAAW;;;;EAM9B,IAAIC;EACJ,IAAIC;AAEJ,MAAI,KAAK,MAAM,WAAW,mBAAmB,EAAE;GAC7C,mBAAmB,kBAAkB,KAAK,WAAW,YAAY;GACjE,aAAa;EACd,WAAU,KAAK,MAAM,WAAW,mBAAmB,EAAE;GACpD,mBAAmB,kBAAkB,KAAK,WAAW,YAAY;GACjE,aAAa;EACd,WAAU,KAAK,MAAM,WAAW,kBAAkB,EAAE;GACnD,mBAAmB,kBAAkB,KAAK,WAAW,WAAW;GAChE,aAAa;EACd,WAAU,KAAK,MAAM,WAAW,kBAAkB,EAAE;GACnD,mBAAmB,kBAAkB,KAAK,WAAW,WAAW;GAChE,aAAa;EACd;AAED,MACE,OAAO,OAAO,WAAW,YACzB,CAAC,kBAAkB,SAAS,OAAO,OAAuB,EAC1D;GACA,MAAM,aACJ,oBAAoB,iBAAiB,SAAS,IAC1C,CAAC,gBAAgB,EAAE,kBAAkB,KAAK,KAAK,CAAC,CAAC,CAAC,GAClD,aACE,CAAC,GAAG,EAAE,WAAW,uBAAuB,EAAE,KAAK,WAAW,MAAM,GAChE;AACR,SAAM,IAAI,MAAM,CAAC,qBAAqB,EAAE,OAAO,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;EACxE;;;;AAKD,MAAI,OAAO,WAAW,YAAY,UAAU,QAAQ;AAElD,OAAI,OAAO,SAAS,aAAa;AAC/B,QAAI,OAAO,MACT,OAAM,OAAO;AAEf,WAAO;KACL,GAAG;KACH,GAAI,OAAO,UAAU,CAAE;KACvB,QAAQ,OAAO;IAChB;GACF;AAED,SAAM,IAAI,MAAM,CAAC,wBAAwB,EAAE,KAAK,UAAU,OAAO,EAAE;EACpE;;;;AAKD,SAAO;GAAE,GAAG;GAAO,GAAG;GAAQ,QAAQ,OAAO;EAAQ;CACtD;CAED,IAAI,cAAc;AAChB,SAAO,EACL,OAAO,mBAAmB,KAAK,WAAW,YAAY,CACvD;CACF;AACF"}
|