@langchain/langgraph
Version:
793 lines (792 loc) • 35.7 kB
JavaScript
const require_timeout = require("../pregel/utils/timeout.cjs");
const require_constants = require("../constants.cjs");
const require_errors = require("../errors.cjs");
const require_last_value = require("../channels/last_value.cjs");
const require_annotation = require("./annotation.cjs");
const require_utils = require("../utils.cjs");
const require_write = require("../pregel/write.cjs");
const require_read = require("../pregel/read.cjs");
require("../pregel/utils/index.cjs");
const require_subgraph = require("../pregel/utils/subgraph.cjs");
const require_ephemeral_value = require("../channels/ephemeral_value.cjs");
const require_graph = require("./graph.cjs");
const require_named_barrier_value = require("../channels/named_barrier_value.cjs");
const require_schema = require("../state/schema.cjs");
require("../state/index.cjs");
const require_meta = require("./zod/meta.cjs");
const require_types = require("./types.cjs");
let _langchain_core_runnables = require("@langchain/core/runnables");
let _langchain_core_utils_types = require("@langchain/core/utils/types");
//#region src/graph/state.ts
const ROOT = "__root__";
/**
* Reserved node name for the single shared error handler that is materialized
* when a graph-wide default error handler is set via
* {@link StateGraph.setNodeDefaults}. Every regular node that lacks its own
* `errorHandler` routes failures to this node. Mirrors Python's
* `__default_error_handler__`.
*/
const DEFAULT_ERROR_HANDLER_NODE = "__default_error_handler__";
const PartialStateSchema = Symbol.for("langgraph.state.partial");
/**
* A graph whose nodes communicate by reading and writing to a shared state.
* Each node takes a defined `State` as input and returns a `Partial<State>`.
*
* Each state key can optionally be annotated with a reducer function that
* will be used to aggregate the values of that key received from multiple nodes.
* The signature of a reducer function is (left: Value, right: UpdateValue) => Value.
*
* See {@link Annotation} for more on defining state.
*
* After adding nodes and edges to your graph, you must call `.compile()` on it before
* you can use it.
*
* @typeParam SD - The state definition used to construct the graph. Can be an
* {@link AnnotationRoot}, {@link StateSchema}, or Zod object schema. This is the
* primary generic from which `S` and `U` are derived.
*
* @typeParam S - The full state type representing the complete shape of your graph's
* state after all reducers have been applied. Automatically inferred from `SD`.
*
* @typeParam U - The update type representing what nodes can return to modify state.
* Typically a partial of the state type. Automatically inferred from `SD`.
*
* @typeParam N - Union of all node names in the graph (e.g., `"agent" | "tool"`).
* Accumulated as you call `.addNode()`. Used for type-safe routing.
*
* @typeParam I - The input schema definition. Set via the `input` option in the
* constructor to restrict what data the graph accepts when invoked.
*
* @typeParam O - The output schema definition. Set via the `output` option in the
* constructor to restrict what data the graph returns after execution.
*
* @typeParam C - The config/context schema definition. Set via the `context` option
* to define additional configuration passed at runtime.
*
* @typeParam NodeReturnType - Constrains what types nodes in this graph can return.
*
* @typeParam InterruptType - The type for {@link interrupt} resume values. Set via
* the `interrupt` option for typed human-in-the-loop patterns.
*
* @typeParam WriterType - The type for custom stream writers. Set via the `writer`
* option to enable typed custom streaming from within nodes.
*
* @example
* ```ts
* import {
* type BaseMessage,
* AIMessage,
* HumanMessage,
* } from "@langchain/core/messages";
* import { StateGraph, Annotation } from "@langchain/langgraph";
*
* // Define a state with a single key named "messages" that will
* // combine a returned BaseMessage or arrays of BaseMessages
* const StateAnnotation = Annotation.Root({
* sentiment: Annotation<string>,
* messages: Annotation<BaseMessage[]>({
* reducer: (left: BaseMessage[], right: BaseMessage | BaseMessage[]) => {
* if (Array.isArray(right)) {
* return left.concat(right);
* }
* return left.concat([right]);
* },
* default: () => [],
* }),
* });
*
* const graphBuilder = new StateGraph(StateAnnotation);
*
* // A node in the graph that returns an object with a "messages" key
* // will update the state by combining the existing value with the returned one.
* const myNode = (state: typeof StateAnnotation.State) => {
* return {
* messages: [new AIMessage("Some new response")],
* sentiment: "positive",
* };
* };
*
* const graph = graphBuilder
* .addNode("myNode", myNode)
* .addEdge("__start__", "myNode")
* .addEdge("myNode", "__end__")
* .compile();
*
* await graph.invoke({ messages: [new HumanMessage("how are you?")] });
*
* // {
* // messages: [HumanMessage("how are you?"), AIMessage("Some new response")],
* // sentiment: "positive",
* // }
* ```
*/
var StateGraph = class extends require_graph.Graph {
channels = {};
waitingEdges = /* @__PURE__ */ new Set();
/** @internal */
_schemaDefinition;
/** @internal */
_schemaRuntimeDefinition;
/** @internal */
_inputDefinition;
/** @internal */
_inputRuntimeDefinition;
/** @internal */
_outputDefinition;
/** @internal */
_outputRuntimeDefinition;
/**
* Map schemas to managed values
* @internal
*/
_schemaDefinitions = /* @__PURE__ */ new Map();
/** @internal */
_metaRegistry = require_meta.schemaMetaRegistry;
/** @internal Used only for typing. */
_configSchema;
/** @internal */
_configRuntimeSchema;
/** @internal */
_interrupt;
/** @internal */
_writer;
/**
* Graph-wide default node policies, resolved at `compile()` time.
* @internal
*/
_nodeDefaults = {};
constructor(stateOrInit, options) {
super();
const init = this._normalizeToStateGraphInit(stateOrInit, options);
const stateSchema = init.state ?? init.stateSchema ?? init.input;
if (!stateSchema) throw new require_errors.StateGraphInputError();
const stateChannelDef = this._getChannelsFromSchema(stateSchema);
this._schemaDefinition = stateChannelDef;
if (require_schema.StateSchema.isInstance(stateSchema)) this._schemaRuntimeDefinition = stateSchema;
else if ((0, _langchain_core_utils_types.isInteropZodObject)(stateSchema)) this._schemaRuntimeDefinition = stateSchema;
if (init.input) if (require_schema.StateSchema.isInstance(init.input)) this._inputRuntimeDefinition = init.input;
else if ((0, _langchain_core_utils_types.isInteropZodObject)(init.input)) this._inputRuntimeDefinition = init.input;
else this._inputRuntimeDefinition = PartialStateSchema;
else this._inputRuntimeDefinition = PartialStateSchema;
if (init.output) if (require_schema.StateSchema.isInstance(init.output)) this._outputRuntimeDefinition = init.output;
else if ((0, _langchain_core_utils_types.isInteropZodObject)(init.output)) this._outputRuntimeDefinition = init.output;
else this._outputRuntimeDefinition = this._schemaRuntimeDefinition;
else this._outputRuntimeDefinition = this._schemaRuntimeDefinition;
const inputChannelDef = init.input ? this._getChannelsFromSchema(init.input) : stateChannelDef;
const outputChannelDef = init.output ? this._getChannelsFromSchema(init.output) : stateChannelDef;
this._inputDefinition = inputChannelDef;
this._outputDefinition = outputChannelDef;
this._addSchema(this._schemaDefinition);
this._addSchema(this._inputDefinition);
this._addSchema(this._outputDefinition);
if (init.context) {
if ((0, _langchain_core_utils_types.isInteropZodObject)(init.context)) this._configRuntimeSchema = init.context;
}
this._interrupt = init.interrupt;
this._writer = init.writer;
}
/**
* Set graph-wide default node policies that apply to every node in this
* graph.
*
* Per-node values passed to {@link addNode} always take precedence over these
* defaults. Defaults are resolved at {@link compile} time, so call order does
* not matter — you may call this before or after `addNode`, including as the
* last step before `compile()`. Calling it multiple times merges the provided
* fields, with later calls overriding earlier ones on a per-field basis.
*
* Policies set here are **not** inherited by subgraphs.
*
* `retryPolicy` and `timeout` defaults apply to **all** nodes, including
* auto-generated error-handler nodes. `cachePolicy` and `errorHandler`
* defaults apply to **regular nodes only** — caching an error-handler result
* is unsafe, and a handler must never catch its own (or another handler's)
* failure.
*
* @param defaults - The default node policies to apply.
* @returns The builder instance, for chaining.
*
* @example Call before `addNode`
* ```ts
* const graph = new StateGraph(State)
* .setNodeDefaults({
* retryPolicy: { maxAttempts: 3 },
* cachePolicy: { ttl: 60 },
* timeout: 60_000,
* errorHandler: (state, { node, error }) => ({ lastError: error.message }),
* })
* .addNode("a", nodeA)
* .addNode("b", nodeB, { retryPolicy: { maxAttempts: 5 } }) // overrides default
* .addEdge(START, "a")
* .compile();
* ```
*
* @example Call after `addNode`, immediately before `compile()`
* ```ts
* const graph = new StateGraph(State)
* .addNode("a", nodeA)
* .addNode("b", nodeB, { retryPolicy: { maxAttempts: 5 } }) // overrides default
* .addEdge(START, "a")
* .setNodeDefaults({
* retryPolicy: { maxAttempts: 3 },
* cachePolicy: { ttl: 60 },
* })
* .compile();
* ```
*/
setNodeDefaults(defaults) {
if (defaults.retryPolicy !== void 0) this._nodeDefaults.retryPolicy = defaults.retryPolicy;
if (defaults.cachePolicy !== void 0) this._nodeDefaults.cachePolicy = typeof defaults.cachePolicy === "boolean" ? defaults.cachePolicy ? {} : void 0 : defaults.cachePolicy;
if (defaults.timeout !== void 0) this._nodeDefaults.timeout = require_timeout.coerceTimeoutPolicy(defaults.timeout);
if (defaults.errorHandler !== void 0) this._nodeDefaults.errorHandler = defaults.errorHandler;
return this;
}
/**
* Build the shared spec for a graph-wide default error handler, or
* `undefined` when {@link setNodeDefaults} did not configure one. The spec is
* installed under {@link DEFAULT_ERROR_HANDLER_NODE} for the duration of a
* single {@link compile} call and routes failures from every regular node
* that lacks its own handler.
* @internal
*/
_createDefaultErrorHandlerSpec() {
const userHandler = this._nodeDefaults.errorHandler;
if (userHandler === void 0) return;
return {
runnable: new require_utils.RunnableCallable({
func: (state, config) => {
const nodeError = config?.configurable?.[require_constants.CONFIG_KEY_NODE_ERROR];
return userHandler(state, nodeError, config);
},
name: DEFAULT_ERROR_HANDLER_NODE,
trace: false
}),
metadata: void 0,
input: this._schemaDefinition,
retryPolicy: void 0,
cachePolicy: void 0,
isErrorHandler: true
};
}
/**
* Normalize all constructor input patterns to a unified StateGraphInit object.
* @internal
*/
_normalizeToStateGraphInit(stateOrInit, options) {
if (require_types.isStateGraphInit(stateOrInit)) {
if ((0, _langchain_core_utils_types.isInteropZodObject)(options) || require_annotation.AnnotationRoot.isInstance(options)) return {
...stateOrInit,
context: options
};
const opts = options;
return {
...stateOrInit,
input: stateOrInit.input ?? opts?.input,
output: stateOrInit.output ?? opts?.output,
context: stateOrInit.context ?? opts?.context,
interrupt: stateOrInit.interrupt ?? opts?.interrupt,
writer: stateOrInit.writer ?? opts?.writer,
nodes: stateOrInit.nodes ?? opts?.nodes
};
}
if (require_types.isStateDefinitionInit(stateOrInit)) {
if ((0, _langchain_core_utils_types.isInteropZodObject)(options) || require_annotation.AnnotationRoot.isInstance(options)) return {
state: stateOrInit,
context: options
};
const opts = options;
return {
state: stateOrInit,
input: opts?.input,
output: opts?.output,
context: opts?.context,
interrupt: opts?.interrupt,
writer: opts?.writer,
nodes: opts?.nodes
};
}
if (isStateGraphArgs(stateOrInit)) return { state: _getChannels(stateOrInit.channels) };
throw new require_errors.StateGraphInputError();
}
/**
* Convert any supported schema type to a StateDefinition (channel map).
* @internal
*/
_getChannelsFromSchema(schema) {
if (require_schema.StateSchema.isInstance(schema)) return schema.getChannels();
if ((0, _langchain_core_utils_types.isInteropZodObject)(schema)) return this._metaRegistry.getChannelsForSchema(schema);
if (typeof schema === "object" && "lc_graph_name" in schema && schema.lc_graph_name === "AnnotationRoot") return schema.spec;
if (typeof schema === "object" && !Array.isArray(schema) && Object.keys(schema).length > 0) return schema;
throw new require_errors.StateGraphInputError("Invalid schema type. Expected StateSchema, Zod object, AnnotationRoot, or StateDefinition.");
}
get allEdges() {
return new Set([...this.edges, ...Array.from(this.waitingEdges).flatMap(([starts, end]) => starts.map((start) => [start, end]))]);
}
_addSchema(stateDefinition) {
if (this._schemaDefinitions.has(stateDefinition)) return;
this._schemaDefinitions.set(stateDefinition, stateDefinition);
for (const [key, val] of Object.entries(stateDefinition)) {
let channel;
if (typeof val === "function") channel = val();
else channel = val;
if (this.channels[key] !== void 0) {
if (!this.channels[key].equals(channel)) {
if (channel.lc_graph_name !== "LastValue") throw new Error(`Channel "${key}" already exists with a different type.`);
}
} else this.channels[key] = channel;
}
}
addNode(...args) {
function isMultipleNodes(args) {
return args.length >= 1 && typeof args[0] !== "string";
}
const nodes = isMultipleNodes(args) ? Array.isArray(args[0]) ? args[0] : Object.entries(args[0]).map(([key, action]) => [key, action]) : [[
args[0],
args[1],
args[2]
]];
if (nodes.length === 0) throw new Error("No nodes provided in `addNode`");
for (const [key, action, options] of nodes) {
if (key in this.channels) throw new Error(`${key} is already being used as a state attribute (a.k.a. a channel), cannot also be used as a node name.`);
for (const reservedChar of ["|", ":"]) if (key.includes(reservedChar)) throw new Error(`"${reservedChar}" is a reserved character and is not allowed in node names.`);
this.warnIfCompiled(`Adding a node to a graph that has already been compiled. This will not be reflected in the compiled graph.`);
if (key in this.nodes) throw new Error(`Node \`${key}\` already present.`);
if (key === "__end__" || key === "__start__") throw new Error(`Node \`${key}\` is reserved.`);
let inputSpec = this._schemaDefinition;
if (options?.input !== void 0) inputSpec = this._getChannelsFromSchema(options.input);
this._addSchema(inputSpec);
let runnable;
if (_langchain_core_runnables.Runnable.isRunnable(action)) runnable = action;
else if (typeof action === "function") runnable = new require_utils.RunnableCallable({
func: action,
name: key,
trace: false
});
else runnable = (0, _langchain_core_runnables._coerceToRunnable)(action);
const rawCachePolicy = options?.cachePolicy;
let cachePolicy;
if (rawCachePolicy !== void 0) cachePolicy = typeof rawCachePolicy === "boolean" ? rawCachePolicy ? {} : false : rawCachePolicy;
let errorHandlerNode;
if (options?.errorHandler !== void 0) {
errorHandlerNode = `__error_handler__${key}`;
if (errorHandlerNode in this.nodes) throw new Error(`Cannot add error handler to node \`${key}\`: the reserved name \`${errorHandlerNode}\` is already in use. StateGraph registers \`__error_handler__<nodeName>\` when you pass \`errorHandler\` in addNode options. Remove or rename the existing node with that name (for example, you may have added it manually).`);
const userHandler = options.errorHandler;
const handlerSpec = {
runnable: new require_utils.RunnableCallable({
func: (state, config) => {
const nodeError = config?.configurable?.[require_constants.CONFIG_KEY_NODE_ERROR];
return userHandler(state, nodeError, config);
},
name: errorHandlerNode,
trace: false
}),
metadata: void 0,
input: inputSpec ?? this._schemaDefinition,
retryPolicy: void 0,
cachePolicy: void 0,
isErrorHandler: true
};
this.nodes[errorHandlerNode] = handlerSpec;
}
const nodeSpec = {
runnable,
retryPolicy: options?.retryPolicy,
cachePolicy,
timeout: require_timeout.coerceTimeoutPolicy(options?.timeout),
metadata: options?.metadata,
input: inputSpec ?? this._schemaDefinition,
subgraphs: require_subgraph.isPregelLike(runnable) ? [runnable] : options?.subgraphs,
ends: options?.ends,
defer: options?.defer,
errorHandlerNode
};
this.nodes[key] = nodeSpec;
}
return this;
}
addEdge(startKey, endKey) {
if (typeof startKey === "string") return super.addEdge(startKey, endKey);
if (this.compiled) console.warn("Adding an edge to a graph that has already been compiled. This will not be reflected in the compiled graph.");
for (const start of startKey) {
if (start === "__end__") throw new Error("END cannot be a start node");
if (!Object.keys(this.nodes).some((node) => node === start)) throw new Error(`Need to add a node named "${start}" first`);
}
if (endKey === "__end__") throw new Error("END cannot be an end node");
if (!Object.keys(this.nodes).some((node) => node === endKey)) throw new Error(`Need to add a node named "${endKey}" first`);
this.waitingEdges.add([startKey, endKey]);
return this;
}
addSequence(nodes) {
const parsedNodes = Array.isArray(nodes) ? nodes : Object.entries(nodes);
if (parsedNodes.length === 0) throw new Error("Sequence requires at least one node.");
let previousNode;
for (const [key, action, options] of parsedNodes) {
if (key in this.nodes) throw new Error(`Node names must be unique: node with the name "${key}" already exists.`);
const validKey = key;
this.addNode(key, action, options);
if (previousNode != null) this.addEdge(previousNode, validKey);
previousNode = validKey;
}
return this;
}
compile({ checkpointer, store, cache, interruptBefore, interruptAfter, name, description, transformers } = {}) {
const defaultErrorHandlerSpec = this._createDefaultErrorHandlerSpec();
if (defaultErrorHandlerSpec !== void 0) {
if (DEFAULT_ERROR_HANDLER_NODE in this.nodes) throw new Error(`Cannot apply a default error handler: the reserved node name \`${DEFAULT_ERROR_HANDLER_NODE}\` is already in use. setNodeDefaults({ errorHandler }) registers a node with that name; rename the conflicting node.`);
this.nodes[DEFAULT_ERROR_HANDLER_NODE] = defaultErrorHandlerSpec;
}
try {
return this._compileResolved({
checkpointer,
store,
cache,
interruptBefore,
interruptAfter,
name,
description,
transformers,
defaultErrorHandlerNode: defaultErrorHandlerSpec !== void 0 ? DEFAULT_ERROR_HANDLER_NODE : void 0
});
} finally {
if (defaultErrorHandlerSpec !== void 0) delete this.nodes[DEFAULT_ERROR_HANDLER_NODE];
}
}
/** @internal */
_compileResolved({ checkpointer, store, cache, interruptBefore, interruptAfter, name, description, transformers, defaultErrorHandlerNode }) {
this.validate([...Array.isArray(interruptBefore) ? interruptBefore : [], ...Array.isArray(interruptAfter) ? interruptAfter : []]);
const outputKeys = Object.keys(this._schemaDefinitions.get(this._outputDefinition));
const outputChannels = outputKeys.length === 1 && outputKeys[0] === ROOT ? ROOT : outputKeys;
const streamKeys = Object.keys(this.channels);
const streamChannels = streamKeys.length === 1 && streamKeys[0] === ROOT ? ROOT : streamKeys;
const userInterrupt = this._interrupt;
const compiled = new CompiledStateGraph({
builder: this,
checkpointer,
interruptAfter,
interruptBefore,
autoValidate: false,
nodes: {},
channels: {
...this.channels,
[require_constants.START]: new require_ephemeral_value.EphemeralValue()
},
inputChannels: require_constants.START,
outputChannels,
streamChannels,
streamMode: "updates",
store,
cache,
name,
description,
userInterrupt,
streamTransformers: transformers
});
compiled.attachNode(require_constants.START);
const nodeDefaults = this._nodeDefaults;
const hasNodeDefaults = nodeDefaults.retryPolicy !== void 0 || nodeDefaults.cachePolicy !== void 0 || nodeDefaults.timeout !== void 0 || defaultErrorHandlerNode !== void 0;
for (const [key, node] of Object.entries(this.nodes)) {
const isErrorHandlerNode = node.isErrorHandler === true;
const resolvedNode = hasNodeDefaults ? {
...node,
retryPolicy: node.retryPolicy ?? nodeDefaults.retryPolicy,
cachePolicy: isErrorHandlerNode ? void 0 : node.cachePolicy === false ? void 0 : node.cachePolicy ?? nodeDefaults.cachePolicy,
timeout: node.timeout ?? nodeDefaults.timeout,
errorHandlerNode: !isErrorHandlerNode && defaultErrorHandlerNode !== void 0 && node.errorHandlerNode === void 0 ? defaultErrorHandlerNode : node.errorHandlerNode
} : node;
compiled.attachNode(key, resolvedNode);
}
compiled.attachBranch(require_constants.START, require_constants.SELF, _getControlBranch(), { withReader: false });
for (const [key] of Object.entries(this.nodes)) compiled.attachBranch(key, require_constants.SELF, _getControlBranch(), { withReader: false });
for (const [start, end] of this.edges) compiled.attachEdge(start, end);
for (const [starts, end] of this.waitingEdges) compiled.attachEdge(starts, end);
for (const [start, branches] of Object.entries(this.branches)) for (const [name, branch] of Object.entries(branches)) compiled.attachBranch(start, name, branch);
return compiled.validate();
}
};
function _getChannels(schema) {
const channels = {};
for (const [name, val] of Object.entries(schema)) if (name === ROOT) channels[name] = require_annotation.getChannel(val);
else channels[name] = require_annotation.getChannel(val);
return channels;
}
/**
* Final result from building and compiling a {@link StateGraph}.
* Should not be instantiated directly, only using the StateGraph `.compile()`
* instance method.
*
* @typeParam S - The full state type representing the complete shape of your graph's
* state after all reducers have been applied. This is the type you receive when
* reading state in nodes or after invoking the graph.
*
* @typeParam U - The update type representing what nodes can return to modify state.
* Typically a partial of the state type, allowing nodes to update only specific fields.
* Can also include {@link Command} objects for advanced control flow.
*
* @typeParam N - Union of all node names in the graph (e.g., `"agent" | "tool"`).
* Used for type-safe routing with {@link Command.goto} and edge definitions.
*
* @typeParam I - The input schema definition. Determines what shape of data the graph
* accepts when invoked. Defaults to the main state schema if not explicitly set.
*
* @typeParam O - The output schema definition. Determines what shape of data the graph
* returns after execution. Defaults to the main state schema if not explicitly set.
*
* @typeParam C - The config/context schema definition. Defines additional configuration
* that can be passed to the graph at runtime via {@link LangGraphRunnableConfig}.
*
* @typeParam NodeReturnType - Constrains what types nodes in this graph can return.
* Useful for enforcing consistent return patterns across all nodes.
*
* @typeParam InterruptType - The type of values that can be passed when resuming from
* an {@link interrupt}. Used with human-in-the-loop patterns.
*
* @typeParam WriterType - The type for custom stream writers. Used with the `writer`
* option to enable typed custom streaming from within nodes.
*
* @typeParam TStreamTransformers - Stream transformer factories registered at
* compile time via the `transformers` option. Used to type extensions on
* `streamEvents(..., { version: "v3" })`.
*/
var CompiledStateGraph = class extends require_graph.CompiledGraph {
/**
* The description of the compiled graph.
* This is used by the supervisor agent to describe the handoff to the agent.
*/
description;
/** @internal */
_metaRegistry = require_meta.schemaMetaRegistry;
constructor({ description, ...rest }) {
super(rest);
this.description = description;
}
attachNode(key, node) {
let outputKeys;
if (key === "__start__") outputKeys = Object.entries(this.builder._schemaDefinitions.get(this.builder._inputDefinition)).map(([k]) => k);
else outputKeys = Object.keys(this.builder.channels);
function _getRoot(input) {
if (require_constants.isCommand(input)) {
if (input.graph === require_constants.Command.PARENT) return null;
return input._updateAsTuples();
} else if (Array.isArray(input) && input.length > 0 && input.some((i) => require_constants.isCommand(i))) {
const updates = [];
for (const i of input) if (require_constants.isCommand(i)) {
if (i.graph === require_constants.Command.PARENT) continue;
updates.push(...i._updateAsTuples());
} else updates.push([ROOT, i]);
return updates;
} else if (input != null) return [[ROOT, input]];
return null;
}
const nodeKey = key;
const validateStateUpdates = async (updates) => {
if (updates == null || updates.length === 0) return updates;
const schemaDef = this.builder._schemaRuntimeDefinition;
if (require_schema.StateSchema.isInstance(schemaDef)) {
const schemaKeys = new Set(schemaDef.getChannelKeys());
return Promise.all(updates.map(async ([k, v]) => {
if (!schemaKeys.has(k)) return [k, v];
const parsed = await schemaDef.validateInput({ [k]: v });
return [k, Object.prototype.hasOwnProperty.call(parsed, k) ? parsed[k] : v];
}));
}
if ((0, _langchain_core_utils_types.isInteropZodObject)(schemaDef)) {
const schemaKeys = new Set(Object.keys((0, _langchain_core_utils_types.getInteropZodObjectShape)(schemaDef)));
if (updates.filter(([k]) => schemaKeys.has(k)).length === 0) return updates;
const updateSchema = (0, _langchain_core_utils_types.interopZodObjectPartial)(this._metaRegistry.getExtendedChannelSchemas(schemaDef, { withReducerSchema: true }));
const valueSchema = (0, _langchain_core_utils_types.interopZodObjectPartial)(schemaDef);
return updates.map(([k, v]) => {
if (!schemaKeys.has(k)) return [k, v];
const [isOverwrite, overwriteValue] = require_constants._getOverwriteValue(v);
if (isOverwrite) {
const parsed = (0, _langchain_core_utils_types.interopParse)(valueSchema, { [k]: overwriteValue });
return [k, Object.prototype.hasOwnProperty.call(parsed, k) ? { [require_constants.OVERWRITE]: parsed[k] } : v];
}
const parsed = (0, _langchain_core_utils_types.interopParse)(updateSchema, { [k]: v });
return [k, Object.prototype.hasOwnProperty.call(parsed, k) ? parsed[k] : v];
});
}
return updates;
};
async function _getUpdates(input) {
if (!input) return null;
else if (require_constants.isCommand(input)) {
if (input.graph === require_constants.Command.PARENT) return null;
return validateStateUpdates(input._updateAsTuples().filter(([k]) => outputKeys.includes(k)));
} else if (Array.isArray(input) && input.length > 0 && input.some(require_constants.isCommand)) {
const updates = [];
for (const item of input) if (require_constants.isCommand(item)) {
if (item.graph === require_constants.Command.PARENT) continue;
updates.push(...item._updateAsTuples().filter(([k]) => outputKeys.includes(k)));
} else {
const itemUpdates = await _getUpdates(item);
if (itemUpdates) updates.push(...itemUpdates ?? []);
}
return validateStateUpdates(updates);
} else if (typeof input === "object" && !Array.isArray(input)) return validateStateUpdates(Object.entries(input).filter(([k]) => outputKeys.includes(k)));
else {
const typeofInput = Array.isArray(input) ? "array" : typeof input;
throw new require_errors.InvalidUpdateError(`Expected node "${nodeKey.toString()}" to return an object or an array containing at least one Command object, received ${typeofInput}`, { lc_error_code: "INVALID_GRAPH_NODE_RETURN_VALUE" });
}
}
const stateWriteEntries = [{
value: require_write.PASSTHROUGH,
mapper: new require_utils.RunnableCallable({
func: outputKeys.length && outputKeys[0] === ROOT ? _getRoot : _getUpdates,
trace: false,
recurse: false
})
}];
if (key === "__start__") this.nodes[key] = new require_read.PregelNode({
tags: [require_constants.TAG_HIDDEN],
triggers: [require_constants.START],
channels: [require_constants.START],
writers: [new require_write.ChannelWrite(stateWriteEntries, [require_constants.TAG_HIDDEN])]
});
else {
const inputDefinition = node?.input ?? this.builder._schemaDefinition;
const inputValues = Object.fromEntries(Object.keys(this.builder._schemaDefinitions.get(inputDefinition)).map((k) => [k, k]));
const isSingleInput = Object.keys(inputValues).length === 1 && ROOT in inputValues;
const branchChannel = `branch:to:${key}`;
this.channels[branchChannel] = node?.defer ? new require_last_value.LastValueAfterFinish() : new require_ephemeral_value.EphemeralValue(false);
const nodeCachePolicy = node?.cachePolicy;
const cachePolicy = nodeCachePolicy === false ? void 0 : nodeCachePolicy;
this.nodes[key] = new require_read.PregelNode({
triggers: [branchChannel],
channels: isSingleInput ? Object.keys(inputValues) : inputValues,
writers: [new require_write.ChannelWrite(stateWriteEntries, [require_constants.TAG_HIDDEN])],
mapper: isSingleInput ? void 0 : (input) => {
return Object.fromEntries(Object.entries(input).filter(([k]) => k in inputValues));
},
bound: node?.runnable,
metadata: node?.metadata,
retryPolicy: node?.retryPolicy,
cachePolicy,
timeout: node?.timeout,
subgraphs: node?.subgraphs,
ends: node?.ends,
isErrorHandler: node?.isErrorHandler,
errorHandlerNode: node?.errorHandlerNode
});
}
}
attachEdge(starts, end) {
if (end === "__end__") return;
if (typeof starts === "string") this.nodes[starts].writers.push(new require_write.ChannelWrite([{
channel: `branch:to:${end}`,
value: null
}], [require_constants.TAG_HIDDEN]));
else if (Array.isArray(starts)) {
const channelName = `join:${starts.join("+")}:${end}`;
this.channels[channelName] = this.builder.nodes[end].defer ? new require_named_barrier_value.NamedBarrierValueAfterFinish(new Set(starts)) : new require_named_barrier_value.NamedBarrierValue(new Set(starts));
this.nodes[end].triggers.push(channelName);
for (const start of starts) this.nodes[start].writers.push(new require_write.ChannelWrite([{
channel: channelName,
value: start
}], [require_constants.TAG_HIDDEN]));
}
}
attachBranch(start, _, branch, options = { withReader: true }) {
const branchWriter = async (packets, config) => {
const filteredPackets = packets.filter((p) => p !== require_constants.END);
if (!filteredPackets.length) return;
const writes = filteredPackets.map((p) => {
if (require_constants._isSend(p)) return p;
return {
channel: p === "__end__" ? p : `branch:to:${p}`,
value: start
};
});
await require_write.ChannelWrite.doWrite({
...config,
tags: (config.tags ?? []).concat([require_constants.TAG_HIDDEN])
}, writes);
};
this.nodes[start].writers.push(branch.run(branchWriter, options.withReader ? (config) => require_read.ChannelRead.doRead(config, this.streamChannels ?? this.outputChannels, true) : void 0));
}
async _validateInput(input) {
if (input == null) return input;
const inputDef = this.builder._inputRuntimeDefinition;
const schemaDef = this.builder._schemaRuntimeDefinition;
if (require_schema.StateSchema.isInstance(inputDef)) {
if (require_constants.isCommand(input)) {
const parsedInput = input;
if (input.update) parsedInput.update = await inputDef.validateInput(Array.isArray(input.update) ? Object.fromEntries(input.update) : input.update);
return parsedInput;
}
return await inputDef.validateInput(input);
}
if (inputDef === PartialStateSchema && require_schema.StateSchema.isInstance(schemaDef)) {
if (require_constants.isCommand(input)) {
const parsedInput = input;
if (input.update) parsedInput.update = await schemaDef.validateInput(Array.isArray(input.update) ? Object.fromEntries(input.update) : input.update);
return parsedInput;
}
return await schemaDef.validateInput(input);
}
const schema = (() => {
const apply = (schema) => {
if (schema == null) return void 0;
return this._metaRegistry.getExtendedChannelSchemas(schema, { withReducerSchema: true });
};
if ((0, _langchain_core_utils_types.isInteropZodObject)(inputDef)) return apply(inputDef);
if (inputDef === PartialStateSchema) {
if ((0, _langchain_core_utils_types.isInteropZodObject)(schemaDef)) return (0, _langchain_core_utils_types.interopZodObjectPartial)(apply(schemaDef));
return;
}
})();
if (require_constants.isCommand(input)) {
const parsedInput = input;
if (input.update && schema != null) {
const updateObj = Array.isArray(input.update) ? Object.fromEntries(input.update) : input.update;
const parsed = (0, _langchain_core_utils_types.interopParse)(schema, updateObj);
parsedInput.update = Object.fromEntries(Object.keys(updateObj).map((k) => [k, parsed[k]]));
}
return parsedInput;
}
if (schema != null) return (0, _langchain_core_utils_types.interopParse)(schema, input);
return input;
}
isInterrupted(input) {
return require_constants.isInterrupted(input);
}
async _validateContext(config) {
const configSchema = this.builder._configRuntimeSchema;
if ((0, _langchain_core_utils_types.isInteropZodObject)(configSchema)) (0, _langchain_core_utils_types.interopParse)(configSchema, config);
return config;
}
};
/**
* Check if value is a legacy StateGraphArgs with channels.
* @internal
* @deprecated Use StateGraphInit instead
*/
function isStateGraphArgs(obj) {
return typeof obj === "object" && obj !== null && obj.channels !== void 0;
}
function _controlBranch(value) {
if (require_constants._isSend(value)) return [value];
const commands = [];
if (require_constants.isCommand(value)) commands.push(value);
else if (Array.isArray(value)) commands.push(...value.filter(require_constants.isCommand));
const destinations = [];
for (const command of commands) {
if (command.graph === require_constants.Command.PARENT) throw new require_errors.ParentCommand(command);
if (require_constants._isSend(command.goto)) destinations.push(command.goto);
else if (typeof command.goto === "string") destinations.push(command.goto);
else if (Array.isArray(command.goto)) destinations.push(...command.goto);
}
return destinations;
}
function _getControlBranch() {
return new require_graph.Branch({ path: new require_utils.RunnableCallable({
func: _controlBranch,
tags: [require_constants.TAG_HIDDEN],
trace: false,
recurse: false,
name: "<control_branch>"
}) });
}
//#endregion
exports.CompiledStateGraph = CompiledStateGraph;
exports.StateGraph = StateGraph;
//# sourceMappingURL=state.cjs.map