@langchain/langgraph 0.0.33 → 0.0.34

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/README.md CHANGED
@@ -249,7 +249,7 @@ Is there anything else you'd like to know about the weather in New York or any o
249
249
  - [Conceptual Guides](https://langchain-ai.github.io/langgraphjs/concepts/): In-depth explanations of the key concepts and principles behind LangGraph, such as nodes, edges, state and more.
250
250
  - [API Reference](https://langchain-ai.github.io/langgraphjs/reference/graphs/): Review important classes and methods, simple examples of how to use the graph and checkpointing APIs, higher-level prebuilt components and more.
251
251
 
252
- ## Running Example Juypter Notebooks
252
+ ## Running Example Jupyter Notebooks
253
253
 
254
254
  Please note that the *.ipynb notebooks in the `examples/` folder require [tslab](https://github.com/yunabe/tslab?tab=readme-ov-file) to be installed. In order to run these notebooks in VSCode, you will also need the [Jupyter](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter) VSCode Extension installed. After cloning this repository, you can run `yarn build` in the root. You should then be all set!
255
255
 
@@ -56,6 +56,7 @@ function createCheckpoint(checkpoint, channels, step) {
56
56
  channel_values: values,
57
57
  channel_versions: { ...checkpoint.channel_versions },
58
58
  versions_seen: (0, base_js_1.deepCopy)(checkpoint.versions_seen),
59
+ pending_sends: checkpoint.pending_sends ?? [],
59
60
  };
60
61
  }
61
62
  exports.createCheckpoint = createCheckpoint;
@@ -51,5 +51,6 @@ export function createCheckpoint(checkpoint, channels, step) {
51
51
  channel_values: values,
52
52
  channel_versions: { ...checkpoint.channel_versions },
53
53
  versions_seen: deepCopy(checkpoint.versions_seen),
54
+ pending_sends: checkpoint.pending_sends ?? [],
54
55
  };
55
56
  }
@@ -30,7 +30,7 @@ class EphemeralValue extends index_js_1.BaseChannel {
30
30
  this.guard = guard;
31
31
  }
32
32
  fromCheckpoint(checkpoint) {
33
- const empty = new EphemeralValue();
33
+ const empty = new EphemeralValue(this.guard);
34
34
  if (checkpoint) {
35
35
  empty.value = checkpoint;
36
36
  }
@@ -27,7 +27,7 @@ export class EphemeralValue extends BaseChannel {
27
27
  this.guard = guard;
28
28
  }
29
29
  fromCheckpoint(checkpoint) {
30
- const empty = new EphemeralValue();
30
+ const empty = new EphemeralValue(this.guard);
31
31
  if (checkpoint) {
32
32
  empty.value = checkpoint;
33
33
  }
@@ -32,6 +32,7 @@ function emptyCheckpoint() {
32
32
  channel_values: {},
33
33
  channel_versions: {},
34
34
  versions_seen: {},
35
+ pending_sends: [],
35
36
  };
36
37
  }
37
38
  exports.emptyCheckpoint = emptyCheckpoint;
@@ -43,6 +44,7 @@ function copyCheckpoint(checkpoint) {
43
44
  channel_values: { ...checkpoint.channel_values },
44
45
  channel_versions: { ...checkpoint.channel_versions },
45
46
  versions_seen: deepCopy(checkpoint.versions_seen),
47
+ pending_sends: [...checkpoint.pending_sends],
46
48
  };
47
49
  }
48
50
  exports.copyCheckpoint = copyCheckpoint;
@@ -1,5 +1,6 @@
1
1
  import { RunnableConfig } from "@langchain/core/runnables";
2
2
  import { SerializerProtocol } from "../serde/base.js";
3
+ import { SendInterface } from "../constants.js";
3
4
  export interface CheckpointMetadata {
4
5
  source: "input" | "loop" | "update";
5
6
  /**
@@ -40,6 +41,11 @@ export interface Checkpoint<N extends string = string, C extends string = string
40
41
  * @default {}
41
42
  */
42
43
  versions_seen: Record<N, Record<C, number>>;
44
+ /**
45
+ * List of packets sent to nodes but not yet processed.
46
+ * Cleared by the next checkpoint.
47
+ */
48
+ pending_sends: SendInterface[];
43
49
  }
44
50
  export interface ReadonlyCheckpoint extends Readonly<Checkpoint> {
45
51
  readonly channel_values: Readonly<Record<string, unknown>>;
@@ -26,6 +26,7 @@ export function emptyCheckpoint() {
26
26
  channel_values: {},
27
27
  channel_versions: {},
28
28
  versions_seen: {},
29
+ pending_sends: [],
29
30
  };
30
31
  }
31
32
  export function copyCheckpoint(checkpoint) {
@@ -36,6 +37,7 @@ export function copyCheckpoint(checkpoint) {
36
37
  channel_values: { ...checkpoint.channel_values },
37
38
  channel_versions: { ...checkpoint.channel_versions },
38
39
  versions_seen: deepCopy(checkpoint.versions_seen),
40
+ pending_sends: [...checkpoint.pending_sends],
39
41
  };
40
42
  }
41
43
  export class BaseCheckpointSaver {
@@ -1,8 +1,87 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TASKS = exports.TAG_HIDDEN = exports.INTERRUPT = exports.CONFIG_KEY_READ = exports.CONFIG_KEY_SEND = void 0;
3
+ exports._isSend = exports.Send = exports._isSendInterface = exports.TASKS = exports.TAG_HIDDEN = exports.INTERRUPT = exports.CONFIG_KEY_READ = exports.CONFIG_KEY_SEND = void 0;
4
4
  exports.CONFIG_KEY_SEND = "__pregel_send";
5
5
  exports.CONFIG_KEY_READ = "__pregel_read";
6
6
  exports.INTERRUPT = "__interrupt__";
7
7
  exports.TAG_HIDDEN = "langsmith:hidden";
8
8
  exports.TASKS = "__pregel_tasks";
9
+ function _isSendInterface(x) {
10
+ const operation = x;
11
+ return typeof operation.node === "string" && operation.args !== undefined;
12
+ }
13
+ exports._isSendInterface = _isSendInterface;
14
+ /**
15
+ * A message or packet to send to a specific node in the graph.
16
+ *
17
+ * The `Send` class is used within a `StateGraph`'s conditional edges to
18
+ * dynamically invoke a node with a custom state at the next step.
19
+ *
20
+ * Importantly, the sent state can differ from the core graph's state,
21
+ * allowing for flexible and dynamic workflow management.
22
+ *
23
+ * One such example is a "map-reduce" workflow where your graph invokes
24
+ * the same node multiple times in parallel with different states,
25
+ * before aggregating the results back into the main graph's state.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * import { Annotation, Send, StateGraph } from "@langchain/langgraph";
30
+ *
31
+ * const ChainState = Annotation.Root({
32
+ * subjects: Annotation<string[]>,
33
+ * jokes: Annotation<string[]>({
34
+ * reducer: (a, b) => a.concat(b),
35
+ * }),
36
+ * });
37
+ *
38
+ * const continueToJokes = async (state: typeof ChainState.State) => {
39
+ * return state.subjects.map((subject) => {
40
+ * return new Send("generate_joke", { subjects: [subject] });
41
+ * });
42
+ * };
43
+ *
44
+ * const graph = new StateGraph(ChainState)
45
+ * .addNode("generate_joke", (state) => ({
46
+ * jokes: [`Joke about ${state.subjects}`],
47
+ * }))
48
+ * .addConditionalEdges("__start__", continueToJokes)
49
+ * .addEdge("generate_joke", "__end__")
50
+ * .compile();
51
+ *
52
+ * const res = await graph.invoke({ subjects: ["cats", "dogs"] });
53
+ * console.log(res);
54
+ *
55
+ * // Invoking with two subjects results in a generated joke for each
56
+ * // { subjects: ["cats", "dogs"], jokes: [`Joke about cats`, `Joke about dogs`] }
57
+ * ```
58
+ */
59
+ class Send {
60
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+ constructor(node, args) {
62
+ Object.defineProperty(this, "node", {
63
+ enumerable: true,
64
+ configurable: true,
65
+ writable: true,
66
+ value: node
67
+ });
68
+ Object.defineProperty(this, "args", {
69
+ enumerable: true,
70
+ configurable: true,
71
+ writable: true,
72
+ value: args
73
+ });
74
+ Object.defineProperty(this, "lg_name", {
75
+ enumerable: true,
76
+ configurable: true,
77
+ writable: true,
78
+ value: "Send"
79
+ });
80
+ }
81
+ }
82
+ exports.Send = Send;
83
+ function _isSend(x) {
84
+ const operation = x;
85
+ return operation.lg_name === "Send";
86
+ }
87
+ exports._isSend = _isSend;
@@ -3,3 +3,60 @@ export declare const CONFIG_KEY_READ = "__pregel_read";
3
3
  export declare const INTERRUPT = "__interrupt__";
4
4
  export declare const TAG_HIDDEN = "langsmith:hidden";
5
5
  export declare const TASKS = "__pregel_tasks";
6
+ export interface SendInterface {
7
+ node: string;
8
+ args: any;
9
+ }
10
+ export declare function _isSendInterface(x: unknown): x is SendInterface;
11
+ /**
12
+ * A message or packet to send to a specific node in the graph.
13
+ *
14
+ * The `Send` class is used within a `StateGraph`'s conditional edges to
15
+ * dynamically invoke a node with a custom state at the next step.
16
+ *
17
+ * Importantly, the sent state can differ from the core graph's state,
18
+ * allowing for flexible and dynamic workflow management.
19
+ *
20
+ * One such example is a "map-reduce" workflow where your graph invokes
21
+ * the same node multiple times in parallel with different states,
22
+ * before aggregating the results back into the main graph's state.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { Annotation, Send, StateGraph } from "@langchain/langgraph";
27
+ *
28
+ * const ChainState = Annotation.Root({
29
+ * subjects: Annotation<string[]>,
30
+ * jokes: Annotation<string[]>({
31
+ * reducer: (a, b) => a.concat(b),
32
+ * }),
33
+ * });
34
+ *
35
+ * const continueToJokes = async (state: typeof ChainState.State) => {
36
+ * return state.subjects.map((subject) => {
37
+ * return new Send("generate_joke", { subjects: [subject] });
38
+ * });
39
+ * };
40
+ *
41
+ * const graph = new StateGraph(ChainState)
42
+ * .addNode("generate_joke", (state) => ({
43
+ * jokes: [`Joke about ${state.subjects}`],
44
+ * }))
45
+ * .addConditionalEdges("__start__", continueToJokes)
46
+ * .addEdge("generate_joke", "__end__")
47
+ * .compile();
48
+ *
49
+ * const res = await graph.invoke({ subjects: ["cats", "dogs"] });
50
+ * console.log(res);
51
+ *
52
+ * // Invoking with two subjects results in a generated joke for each
53
+ * // { subjects: ["cats", "dogs"], jokes: [`Joke about cats`, `Joke about dogs`] }
54
+ * ```
55
+ */
56
+ export declare class Send implements SendInterface {
57
+ node: string;
58
+ args: any;
59
+ lg_name: string;
60
+ constructor(node: string, args: any);
61
+ }
62
+ export declare function _isSend(x: unknown): x is Send;
package/dist/constants.js CHANGED
@@ -3,3 +3,79 @@ export const CONFIG_KEY_READ = "__pregel_read";
3
3
  export const INTERRUPT = "__interrupt__";
4
4
  export const TAG_HIDDEN = "langsmith:hidden";
5
5
  export const TASKS = "__pregel_tasks";
6
+ export function _isSendInterface(x) {
7
+ const operation = x;
8
+ return typeof operation.node === "string" && operation.args !== undefined;
9
+ }
10
+ /**
11
+ * A message or packet to send to a specific node in the graph.
12
+ *
13
+ * The `Send` class is used within a `StateGraph`'s conditional edges to
14
+ * dynamically invoke a node with a custom state at the next step.
15
+ *
16
+ * Importantly, the sent state can differ from the core graph's state,
17
+ * allowing for flexible and dynamic workflow management.
18
+ *
19
+ * One such example is a "map-reduce" workflow where your graph invokes
20
+ * the same node multiple times in parallel with different states,
21
+ * before aggregating the results back into the main graph's state.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { Annotation, Send, StateGraph } from "@langchain/langgraph";
26
+ *
27
+ * const ChainState = Annotation.Root({
28
+ * subjects: Annotation<string[]>,
29
+ * jokes: Annotation<string[]>({
30
+ * reducer: (a, b) => a.concat(b),
31
+ * }),
32
+ * });
33
+ *
34
+ * const continueToJokes = async (state: typeof ChainState.State) => {
35
+ * return state.subjects.map((subject) => {
36
+ * return new Send("generate_joke", { subjects: [subject] });
37
+ * });
38
+ * };
39
+ *
40
+ * const graph = new StateGraph(ChainState)
41
+ * .addNode("generate_joke", (state) => ({
42
+ * jokes: [`Joke about ${state.subjects}`],
43
+ * }))
44
+ * .addConditionalEdges("__start__", continueToJokes)
45
+ * .addEdge("generate_joke", "__end__")
46
+ * .compile();
47
+ *
48
+ * const res = await graph.invoke({ subjects: ["cats", "dogs"] });
49
+ * console.log(res);
50
+ *
51
+ * // Invoking with two subjects results in a generated joke for each
52
+ * // { subjects: ["cats", "dogs"], jokes: [`Joke about cats`, `Joke about dogs`] }
53
+ * ```
54
+ */
55
+ export class Send {
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ constructor(node, args) {
58
+ Object.defineProperty(this, "node", {
59
+ enumerable: true,
60
+ configurable: true,
61
+ writable: true,
62
+ value: node
63
+ });
64
+ Object.defineProperty(this, "args", {
65
+ enumerable: true,
66
+ configurable: true,
67
+ writable: true,
68
+ value: args
69
+ });
70
+ Object.defineProperty(this, "lg_name", {
71
+ enumerable: true,
72
+ configurable: true,
73
+ writable: true,
74
+ value: "Send"
75
+ });
76
+ }
77
+ }
78
+ export function _isSend(x) {
79
+ const operation = x;
80
+ return operation.lg_name === "Send";
81
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getChannel = exports.Annotation = exports.AnnotationRoot = void 0;
4
+ const binop_js_1 = require("../channels/binop.cjs");
5
+ const last_value_js_1 = require("../channels/last_value.cjs");
6
+ class AnnotationRoot {
7
+ constructor(s) {
8
+ Object.defineProperty(this, "lc_graph_name", {
9
+ enumerable: true,
10
+ configurable: true,
11
+ writable: true,
12
+ value: "AnnotationRoot"
13
+ });
14
+ Object.defineProperty(this, "spec", {
15
+ enumerable: true,
16
+ configurable: true,
17
+ writable: true,
18
+ value: void 0
19
+ });
20
+ this.spec = s;
21
+ }
22
+ }
23
+ exports.AnnotationRoot = AnnotationRoot;
24
+ function Annotation(annotation) {
25
+ if (annotation) {
26
+ return getChannel(annotation);
27
+ }
28
+ else {
29
+ // @ts-expect-error - Annotation without reducer
30
+ return new last_value_js_1.LastValue();
31
+ }
32
+ }
33
+ exports.Annotation = Annotation;
34
+ Annotation.Root = (sd) => new AnnotationRoot(sd);
35
+ function getChannel(reducer) {
36
+ if (typeof reducer === "object" &&
37
+ reducer &&
38
+ "reducer" in reducer &&
39
+ reducer.reducer) {
40
+ return new binop_js_1.BinaryOperatorAggregate(reducer.reducer, reducer.default);
41
+ }
42
+ if (typeof reducer === "object" &&
43
+ reducer &&
44
+ "value" in reducer &&
45
+ reducer.value) {
46
+ return new binop_js_1.BinaryOperatorAggregate(reducer.value, reducer.default);
47
+ }
48
+ // @ts-expect-error - Annotation without reducer
49
+ return new last_value_js_1.LastValue();
50
+ }
51
+ exports.getChannel = getChannel;
@@ -0,0 +1,41 @@
1
+ import { RunnableLike } from "@langchain/core/runnables";
2
+ import { BaseChannel } from "../channels/base.js";
3
+ import { BinaryOperator, BinaryOperatorAggregate } from "../channels/binop.js";
4
+ import { LastValue } from "../channels/last_value.js";
5
+ export type SingleReducer<ValueType, UpdateType = ValueType> = {
6
+ reducer: BinaryOperator<ValueType, UpdateType>;
7
+ default?: () => ValueType;
8
+ } | {
9
+ /**
10
+ * @deprecated Use `reducer` instead
11
+ */
12
+ value: BinaryOperator<ValueType, UpdateType>;
13
+ default?: () => ValueType;
14
+ } | null;
15
+ export interface StateDefinition {
16
+ [key: string]: BaseChannel | (() => BaseChannel);
17
+ }
18
+ type ExtractValueType<C> = C extends BaseChannel ? C["ValueType"] : C extends () => BaseChannel ? ReturnType<C>["ValueType"] : never;
19
+ type ExtractUpdateType<C> = C extends BaseChannel ? C["UpdateType"] : C extends () => BaseChannel ? ReturnType<C>["UpdateType"] : never;
20
+ export type StateType<SD extends StateDefinition> = {
21
+ [key in keyof SD]: ExtractValueType<SD[key]>;
22
+ };
23
+ export type UpdateType<SD extends StateDefinition> = {
24
+ [key in keyof SD]?: ExtractUpdateType<SD[key]>;
25
+ };
26
+ export type NodeType<SD extends StateDefinition> = RunnableLike<StateType<SD>, UpdateType<SD>>;
27
+ export declare class AnnotationRoot<SD extends StateDefinition> {
28
+ lc_graph_name: string;
29
+ State: StateType<SD>;
30
+ Update: UpdateType<SD>;
31
+ Node: NodeType<SD>;
32
+ spec: SD;
33
+ constructor(s: SD);
34
+ }
35
+ export declare function Annotation<ValueType>(): LastValue<ValueType>;
36
+ export declare function Annotation<ValueType, UpdateType = ValueType>(annotation: SingleReducer<ValueType, UpdateType>): BinaryOperatorAggregate<ValueType, UpdateType>;
37
+ export declare namespace Annotation {
38
+ var Root: <S extends StateDefinition>(sd: S) => AnnotationRoot<S>;
39
+ }
40
+ export declare function getChannel<V, U = V>(reducer: SingleReducer<V, U>): BaseChannel<V, U>;
41
+ export {};
@@ -0,0 +1,45 @@
1
+ import { BinaryOperatorAggregate } from "../channels/binop.js";
2
+ import { LastValue } from "../channels/last_value.js";
3
+ export class AnnotationRoot {
4
+ constructor(s) {
5
+ Object.defineProperty(this, "lc_graph_name", {
6
+ enumerable: true,
7
+ configurable: true,
8
+ writable: true,
9
+ value: "AnnotationRoot"
10
+ });
11
+ Object.defineProperty(this, "spec", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: void 0
16
+ });
17
+ this.spec = s;
18
+ }
19
+ }
20
+ export function Annotation(annotation) {
21
+ if (annotation) {
22
+ return getChannel(annotation);
23
+ }
24
+ else {
25
+ // @ts-expect-error - Annotation without reducer
26
+ return new LastValue();
27
+ }
28
+ }
29
+ Annotation.Root = (sd) => new AnnotationRoot(sd);
30
+ export function getChannel(reducer) {
31
+ if (typeof reducer === "object" &&
32
+ reducer &&
33
+ "reducer" in reducer &&
34
+ reducer.reducer) {
35
+ return new BinaryOperatorAggregate(reducer.reducer, reducer.default);
36
+ }
37
+ if (typeof reducer === "object" &&
38
+ reducer &&
39
+ "value" in reducer &&
40
+ reducer.value) {
41
+ return new BinaryOperatorAggregate(reducer.value, reducer.default);
42
+ }
43
+ // @ts-expect-error - Annotation without reducer
44
+ return new LastValue();
45
+ }
@@ -11,6 +11,7 @@ const ephemeral_value_js_1 = require("../channels/ephemeral_value.cjs");
11
11
  const write_js_1 = require("../pregel/write.cjs");
12
12
  const constants_js_1 = require("../constants.cjs");
13
13
  const utils_js_1 = require("../utils.cjs");
14
+ const errors_js_1 = require("../errors.cjs");
14
15
  exports.START = "__start__";
15
16
  exports.END = "__end__";
16
17
  class Branch {
@@ -47,7 +48,8 @@ class Branch {
47
48
  }
48
49
  let destinations;
49
50
  if (this.ends) {
50
- destinations = result.map((r) => this.ends[r]);
51
+ // destinations = [r if isinstance(r, Send) else self.ends[r] for r in result]
52
+ destinations = result.map((r) => ((0, constants_js_1._isSend)(r) ? r : this.ends[r]));
51
53
  }
52
54
  else {
53
55
  destinations = result;
@@ -55,6 +57,9 @@ class Branch {
55
57
  if (destinations.some((dest) => !dest)) {
56
58
  throw new Error("Branch condition returned unknown or null destination");
57
59
  }
60
+ if (destinations.filter(constants_js_1._isSend).some((packet) => packet.node === exports.END)) {
61
+ throw new errors_js_1.InvalidUpdateError("Cannot send a packet to the END node");
62
+ }
58
63
  return writer(destinations);
59
64
  }
60
65
  }
@@ -117,7 +122,9 @@ class Graph {
117
122
  if (key === exports.END) {
118
123
  throw new Error(`Node \`${key}\` is reserved.`);
119
124
  }
120
- this.nodes[key] = (0, runnables_1._coerceToRunnable)(action);
125
+ this.nodes[key] = (0, runnables_1._coerceToRunnable)(
126
+ // Account for arbitrary state due to Send API
127
+ action);
121
128
  return this;
122
129
  }
123
130
  addEdge(startKey, endKey) {
@@ -301,8 +308,16 @@ class CompiledGraph extends index_js_1.Pregel {
301
308
  }
302
309
  // attach branch writer
303
310
  this.nodes[start].pipe(branch.compile((dests) => {
304
- const channels = dests.map((dest) => dest === exports.END ? exports.END : `branch:${start}:${name}:${dest}`);
305
- return new write_js_1.ChannelWrite(channels.map((channel) => ({ channel, value: write_js_1.PASSTHROUGH })), [constants_js_1.TAG_HIDDEN]);
311
+ const writes = dests.map((dest) => {
312
+ if ((0, constants_js_1._isSend)(dest)) {
313
+ return dest;
314
+ }
315
+ return {
316
+ channel: dest === exports.END ? exports.END : `branch:${start}:${name}:${dest}`,
317
+ value: write_js_1.PASSTHROUGH,
318
+ };
319
+ });
320
+ return new write_js_1.ChannelWrite(writes, [constants_js_1.TAG_HIDDEN]);
306
321
  }));
307
322
  // attach branch readers
308
323
  const ends = branch.ends
@@ -5,6 +5,7 @@ import { Pregel, PregelInterface } from "../pregel/index.js";
5
5
  import { BaseCheckpointSaver } from "../checkpoint/base.js";
6
6
  import { BaseChannel } from "../channels/base.js";
7
7
  import { All } from "../pregel/types.js";
8
+ import { Send } from "../constants.js";
8
9
  import { RunnableCallable } from "../utils.js";
9
10
  export declare const START = "__start__";
10
11
  export declare const END = "__end__";
@@ -14,11 +15,11 @@ export interface BranchOptions<IO, N extends string> {
14
15
  pathMap?: Record<string, N | typeof END> | N[];
15
16
  }
16
17
  export declare class Branch<IO, N extends string> {
17
- condition: (input: IO, config?: RunnableConfig) => string | string[] | Promise<string> | Promise<string[]>;
18
+ condition: (input: IO, config?: RunnableConfig) => string | Send | (string | Send)[] | Promise<string | Send | (string | Send)[]>;
18
19
  ends?: Record<string, N | typeof END>;
19
20
  constructor(options: Omit<BranchOptions<IO, N>, "source">);
20
- compile(writer: (dests: string[]) => Runnable | undefined, reader?: (config: RunnableConfig) => IO): RunnableCallable<unknown, unknown>;
21
- _route(input: IO, config: RunnableConfig, writer: (dests: string[]) => Runnable | undefined, reader?: (config: RunnableConfig) => IO): Promise<Runnable | undefined>;
21
+ compile(writer: (dests: (string | Send)[]) => Runnable | undefined, reader?: (config: RunnableConfig) => IO): RunnableCallable<unknown, unknown>;
22
+ _route(input: IO, config: RunnableConfig, writer: (dests: (string | Send)[]) => Runnable | undefined, reader?: (config: RunnableConfig) => IO): Promise<Runnable | undefined>;
22
23
  }
23
24
  export declare class Graph<N extends string = typeof END, RunInput = any, RunOutput = any> {
24
25
  nodes: Record<N, Runnable<RunInput, RunOutput>>;
@@ -30,7 +31,7 @@ export declare class Graph<N extends string = typeof END, RunInput = any, RunOut
30
31
  constructor();
31
32
  private warnIfCompiled;
32
33
  get allEdges(): Set<[string, string]>;
33
- addNode<K extends string>(key: K, action: RunnableLike<RunInput, RunOutput>): Graph<N | K, RunInput, RunOutput>;
34
+ addNode<K extends string, NodeInput = RunInput>(key: K, action: RunnableLike<NodeInput, RunOutput>): Graph<N | K, RunInput, RunOutput>;
34
35
  addEdge(startKey: N | typeof START, endKey: N | typeof END): this;
35
36
  addConditionalEdges(source: BranchOptions<RunInput, N>): this;
36
37
  addConditionalEdges(source: N, path: Branch<RunInput, N>["condition"], pathMap?: BranchOptions<RunInput, N>["pathMap"]): this;
@@ -6,8 +6,9 @@ import { PregelNode } from "../pregel/read.js";
6
6
  import { Channel, Pregel } from "../pregel/index.js";
7
7
  import { EphemeralValue } from "../channels/ephemeral_value.js";
8
8
  import { ChannelWrite, PASSTHROUGH } from "../pregel/write.js";
9
- import { TAG_HIDDEN } from "../constants.js";
9
+ import { _isSend, TAG_HIDDEN } from "../constants.js";
10
10
  import { RunnableCallable } from "../utils.js";
11
+ import { InvalidUpdateError } from "../errors.js";
11
12
  export const START = "__start__";
12
13
  export const END = "__end__";
13
14
  export class Branch {
@@ -44,7 +45,8 @@ export class Branch {
44
45
  }
45
46
  let destinations;
46
47
  if (this.ends) {
47
- destinations = result.map((r) => this.ends[r]);
48
+ // destinations = [r if isinstance(r, Send) else self.ends[r] for r in result]
49
+ destinations = result.map((r) => (_isSend(r) ? r : this.ends[r]));
48
50
  }
49
51
  else {
50
52
  destinations = result;
@@ -52,6 +54,9 @@ export class Branch {
52
54
  if (destinations.some((dest) => !dest)) {
53
55
  throw new Error("Branch condition returned unknown or null destination");
54
56
  }
57
+ if (destinations.filter(_isSend).some((packet) => packet.node === END)) {
58
+ throw new InvalidUpdateError("Cannot send a packet to the END node");
59
+ }
55
60
  return writer(destinations);
56
61
  }
57
62
  }
@@ -113,7 +118,9 @@ export class Graph {
113
118
  if (key === END) {
114
119
  throw new Error(`Node \`${key}\` is reserved.`);
115
120
  }
116
- this.nodes[key] = _coerceToRunnable(action);
121
+ this.nodes[key] = _coerceToRunnable(
122
+ // Account for arbitrary state due to Send API
123
+ action);
117
124
  return this;
118
125
  }
119
126
  addEdge(startKey, endKey) {
@@ -296,8 +303,16 @@ export class CompiledGraph extends Pregel {
296
303
  }
297
304
  // attach branch writer
298
305
  this.nodes[start].pipe(branch.compile((dests) => {
299
- const channels = dests.map((dest) => dest === END ? END : `branch:${start}:${name}:${dest}`);
300
- return new ChannelWrite(channels.map((channel) => ({ channel, value: PASSTHROUGH })), [TAG_HIDDEN]);
306
+ const writes = dests.map((dest) => {
307
+ if (_isSend(dest)) {
308
+ return dest;
309
+ }
310
+ return {
311
+ channel: dest === END ? END : `branch:${start}:${name}:${dest}`,
312
+ value: PASSTHROUGH,
313
+ };
314
+ });
315
+ return new ChannelWrite(writes, [TAG_HIDDEN]);
301
316
  }));
302
317
  // attach branch readers
303
318
  const ends = branch.ends
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.messagesStateReducer = exports.MessageGraph = exports.StateGraph = exports.Graph = exports.START = exports.END = void 0;
3
+ exports.messagesStateReducer = exports.MessageGraph = exports.StateGraph = exports.Graph = exports.START = exports.END = exports.Annotation = void 0;
4
+ var annotation_js_1 = require("./annotation.cjs");
5
+ Object.defineProperty(exports, "Annotation", { enumerable: true, get: function () { return annotation_js_1.Annotation; } });
4
6
  var graph_js_1 = require("./graph.cjs");
5
7
  Object.defineProperty(exports, "END", { enumerable: true, get: function () { return graph_js_1.END; } });
6
8
  Object.defineProperty(exports, "START", { enumerable: true, get: function () { return graph_js_1.START; } });
@@ -1,3 +1,4 @@
1
+ export { Annotation, type StateType, type UpdateType } from "./annotation.js";
1
2
  export { END, START, Graph } from "./graph.js";
2
3
  export { type StateGraphArgs, StateGraph, type CompiledStateGraph, } from "./state.js";
3
4
  export { MessageGraph, messagesStateReducer } from "./message.js";
@@ -1,3 +1,4 @@
1
+ export { Annotation } from "./annotation.js";
1
2
  export { END, START, Graph } from "./graph.js";
2
3
  export { StateGraph, } from "./state.js";
3
4
  export { MessageGraph, messagesStateReducer } from "./message.js";
@@ -1,7 +1,7 @@
1
1
  import { BaseMessage, BaseMessageLike } from "@langchain/core/messages";
2
2
  import { StateGraph } from "./state.js";
3
3
  type Messages = Array<BaseMessage | BaseMessageLike> | BaseMessage | BaseMessageLike;
4
- export declare function messagesStateReducer(left: Messages, right: Messages): BaseMessage[];
4
+ export declare function messagesStateReducer(left: BaseMessage[], right: Messages): BaseMessage[];
5
5
  export declare class MessageGraph extends StateGraph<BaseMessage[], BaseMessage[], Messages> {
6
6
  constructor();
7
7
  }