@langchain/langgraph 0.0.34 → 0.1.0-rc.1
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/LICENSE +1 -1
- package/README.md +11 -15
- package/dist/channels/any_value.cjs +3 -1
- package/dist/channels/any_value.d.ts +1 -1
- package/dist/channels/any_value.js +3 -1
- package/dist/channels/base.cjs +28 -15
- package/dist/channels/base.d.ts +14 -4
- package/dist/channels/base.js +26 -13
- package/dist/channels/binop.cjs +2 -1
- package/dist/channels/binop.d.ts +1 -1
- package/dist/channels/binop.js +2 -1
- package/dist/channels/dynamic_barrier_value.cjs +30 -18
- package/dist/channels/dynamic_barrier_value.d.ts +2 -1
- package/dist/channels/dynamic_barrier_value.js +30 -18
- package/dist/channels/ephemeral_value.cjs +3 -1
- package/dist/channels/ephemeral_value.d.ts +1 -1
- package/dist/channels/ephemeral_value.js +3 -1
- package/dist/channels/last_value.cjs +3 -2
- package/dist/channels/last_value.d.ts +1 -1
- package/dist/channels/last_value.js +3 -2
- package/dist/channels/named_barrier_value.cjs +14 -6
- package/dist/channels/named_barrier_value.d.ts +2 -1
- package/dist/channels/named_barrier_value.js +15 -7
- package/dist/channels/topic.cjs +10 -11
- package/dist/channels/topic.d.ts +1 -1
- package/dist/channels/topic.js +10 -11
- package/dist/checkpoint/sqlite.cjs +14 -170
- package/dist/checkpoint/sqlite.d.ts +1 -14
- package/dist/checkpoint/sqlite.js +1 -166
- package/dist/constants.cjs +17 -1
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +16 -0
- package/dist/errors.cjs +21 -1
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +18 -0
- package/dist/graph/graph.cjs +6 -3
- package/dist/graph/graph.d.ts +7 -3
- package/dist/graph/graph.js +7 -4
- package/dist/graph/index.d.ts +1 -1
- package/dist/graph/state.cjs +9 -8
- package/dist/graph/state.d.ts +1 -1
- package/dist/graph/state.js +9 -8
- package/dist/prebuilt/react_agent_executor.d.ts +1 -1
- package/dist/pregel/algo.cjs +391 -0
- package/dist/pregel/algo.d.ts +35 -0
- package/dist/pregel/algo.js +381 -0
- package/dist/pregel/debug.cjs +155 -9
- package/dist/pregel/debug.d.ts +40 -2
- package/dist/pregel/debug.js +147 -7
- package/dist/pregel/index.cjs +286 -512
- package/dist/pregel/index.d.ts +66 -65
- package/dist/pregel/index.js +285 -506
- package/dist/pregel/io.cjs +2 -2
- package/dist/pregel/io.d.ts +5 -4
- package/dist/pregel/io.js +2 -2
- package/dist/pregel/loop.cjs +432 -0
- package/dist/pregel/loop.d.ts +83 -0
- package/dist/pregel/loop.js +425 -0
- package/dist/pregel/types.d.ts +56 -4
- package/dist/pregel/utils.cjs +48 -0
- package/dist/pregel/utils.d.ts +10 -0
- package/dist/pregel/utils.js +41 -0
- package/dist/pregel/write.cjs +3 -1
- package/dist/pregel/write.js +3 -1
- package/dist/utils.cjs +21 -1
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +18 -0
- package/dist/web.cjs +6 -7
- package/dist/web.d.ts +2 -4
- package/dist/web.js +1 -2
- package/package.json +6 -12
- package/dist/checkpoint/base.cjs +0 -66
- package/dist/checkpoint/base.d.ts +0 -73
- package/dist/checkpoint/base.js +0 -57
- package/dist/checkpoint/id.cjs +0 -8
- package/dist/checkpoint/id.d.ts +0 -1
- package/dist/checkpoint/id.js +0 -4
- package/dist/checkpoint/index.cjs +0 -9
- package/dist/checkpoint/index.d.ts +0 -2
- package/dist/checkpoint/index.js +0 -2
- package/dist/checkpoint/memory.cjs +0 -82
- package/dist/checkpoint/memory.d.ts +0 -10
- package/dist/checkpoint/memory.js +0 -78
- package/dist/serde/base.cjs +0 -8
- package/dist/serde/base.d.ts +0 -12
- package/dist/serde/base.js +0 -5
package/dist/graph/graph.cjs
CHANGED
|
@@ -115,6 +115,9 @@ class Graph {
|
|
|
115
115
|
return this.edges;
|
|
116
116
|
}
|
|
117
117
|
addNode(key, action) {
|
|
118
|
+
if (key.includes(constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR)) {
|
|
119
|
+
throw new Error(`"${constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR}" is a reserved character and is not allowed in node names.`);
|
|
120
|
+
}
|
|
118
121
|
this.warnIfCompiled(`Adding a node to a graph that has already been compiled. This will not be reflected in the compiled graph.`);
|
|
119
122
|
if (key in this.nodes) {
|
|
120
123
|
throw new Error(`Node \`${key}\` already present.`);
|
|
@@ -137,7 +140,7 @@ class Graph {
|
|
|
137
140
|
}
|
|
138
141
|
if (!this.supportMultipleEdges &&
|
|
139
142
|
Array.from(this.edges).some(([start]) => start === startKey)) {
|
|
140
|
-
throw new Error(`Already found path for ${startKey}
|
|
143
|
+
throw new Error(`Already found path for ${startKey}. For multiple edges, use StateGraph with an annotated state key.`);
|
|
141
144
|
}
|
|
142
145
|
this.edges.add([startKey, endKey]);
|
|
143
146
|
return this;
|
|
@@ -190,8 +193,8 @@ class Graph {
|
|
|
190
193
|
[exports.START]: new ephemeral_value_js_1.EphemeralValue(),
|
|
191
194
|
[exports.END]: new ephemeral_value_js_1.EphemeralValue(),
|
|
192
195
|
},
|
|
193
|
-
|
|
194
|
-
|
|
196
|
+
inputChannels: exports.START,
|
|
197
|
+
outputChannels: exports.END,
|
|
195
198
|
streamChannels: [],
|
|
196
199
|
streamMode: "values",
|
|
197
200
|
});
|
package/dist/graph/graph.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Runnable, RunnableConfig, RunnableLike } from "@langchain/core/runnables";
|
|
2
2
|
import { Graph as RunnableGraph } from "@langchain/core/runnables/graph";
|
|
3
|
+
import { BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
|
|
3
4
|
import { PregelNode } from "../pregel/read.js";
|
|
4
|
-
import { Pregel
|
|
5
|
-
import {
|
|
5
|
+
import { Pregel } from "../pregel/index.js";
|
|
6
|
+
import type { PregelParams } from "../pregel/types.js";
|
|
6
7
|
import { BaseChannel } from "../channels/base.js";
|
|
7
8
|
import { All } from "../pregel/types.js";
|
|
8
9
|
import { Send } from "../constants.js";
|
|
@@ -51,10 +52,13 @@ export declare class Graph<N extends string = typeof END, RunInput = any, RunOut
|
|
|
51
52
|
validate(interrupt?: string[]): void;
|
|
52
53
|
}
|
|
53
54
|
export declare class CompiledGraph<N extends string, RunInput = any, RunOutput = any> extends Pregel<Record<N | typeof START, PregelNode<RunInput, RunOutput>>, Record<N | typeof START | typeof END | string, BaseChannel>> {
|
|
55
|
+
NodeType: N;
|
|
56
|
+
RunInput: RunInput;
|
|
57
|
+
RunOutput: RunOutput;
|
|
54
58
|
builder: Graph<N, RunInput, RunOutput>;
|
|
55
59
|
constructor({ builder, ...rest }: {
|
|
56
60
|
builder: Graph<N, RunInput, RunOutput>;
|
|
57
|
-
} &
|
|
61
|
+
} & PregelParams<Record<N | typeof START, PregelNode<RunInput, RunOutput>>, Record<N | typeof START | typeof END | string, BaseChannel>>);
|
|
58
62
|
attachNode(key: N, node: Runnable<RunInput, RunOutput>): void;
|
|
59
63
|
attachEdge(start: N | typeof START, end: N | typeof END): void;
|
|
60
64
|
attachBranch(start: N | typeof START, name: string, branch: Branch<RunInput, N>): void;
|
package/dist/graph/graph.js
CHANGED
|
@@ -6,7 +6,7 @@ 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 { _isSend, TAG_HIDDEN } from "../constants.js";
|
|
9
|
+
import { _isSend, CHECKPOINT_NAMESPACE_SEPARATOR, TAG_HIDDEN, } from "../constants.js";
|
|
10
10
|
import { RunnableCallable } from "../utils.js";
|
|
11
11
|
import { InvalidUpdateError } from "../errors.js";
|
|
12
12
|
export const START = "__start__";
|
|
@@ -111,6 +111,9 @@ export class Graph {
|
|
|
111
111
|
return this.edges;
|
|
112
112
|
}
|
|
113
113
|
addNode(key, action) {
|
|
114
|
+
if (key.includes(CHECKPOINT_NAMESPACE_SEPARATOR)) {
|
|
115
|
+
throw new Error(`"${CHECKPOINT_NAMESPACE_SEPARATOR}" is a reserved character and is not allowed in node names.`);
|
|
116
|
+
}
|
|
114
117
|
this.warnIfCompiled(`Adding a node to a graph that has already been compiled. This will not be reflected in the compiled graph.`);
|
|
115
118
|
if (key in this.nodes) {
|
|
116
119
|
throw new Error(`Node \`${key}\` already present.`);
|
|
@@ -133,7 +136,7 @@ export class Graph {
|
|
|
133
136
|
}
|
|
134
137
|
if (!this.supportMultipleEdges &&
|
|
135
138
|
Array.from(this.edges).some(([start]) => start === startKey)) {
|
|
136
|
-
throw new Error(`Already found path for ${startKey}
|
|
139
|
+
throw new Error(`Already found path for ${startKey}. For multiple edges, use StateGraph with an annotated state key.`);
|
|
137
140
|
}
|
|
138
141
|
this.edges.add([startKey, endKey]);
|
|
139
142
|
return this;
|
|
@@ -186,8 +189,8 @@ export class Graph {
|
|
|
186
189
|
[START]: new EphemeralValue(),
|
|
187
190
|
[END]: new EphemeralValue(),
|
|
188
191
|
},
|
|
189
|
-
|
|
190
|
-
|
|
192
|
+
inputChannels: START,
|
|
193
|
+
outputChannels: END,
|
|
191
194
|
streamChannels: [],
|
|
192
195
|
streamMode: "values",
|
|
193
196
|
});
|
package/dist/graph/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { Annotation, type StateType, type UpdateType } from "./annotation.js";
|
|
2
|
-
export { END, START, Graph } from "./graph.js";
|
|
2
|
+
export { END, START, Graph, type CompiledGraph } from "./graph.js";
|
|
3
3
|
export { type StateGraphArgs, StateGraph, type CompiledStateGraph, } from "./state.js";
|
|
4
4
|
export { MessageGraph, messagesStateReducer } from "./message.js";
|
package/dist/graph/state.cjs
CHANGED
|
@@ -74,14 +74,14 @@ class StateGraph extends graph_js_1.Graph {
|
|
|
74
74
|
throw new Error("END cannot be a start node");
|
|
75
75
|
}
|
|
76
76
|
if (!Object.keys(this.nodes).some((node) => node === start)) {
|
|
77
|
-
throw new Error(`Need to
|
|
77
|
+
throw new Error(`Need to addNode ${start} first`);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
if (endKey === graph_js_1.END) {
|
|
81
81
|
throw new Error("END cannot be an end node");
|
|
82
82
|
}
|
|
83
83
|
if (!Object.keys(this.nodes).some((node) => node === endKey)) {
|
|
84
|
-
throw new Error(`Need to
|
|
84
|
+
throw new Error(`Need to addNode ${endKey} first`);
|
|
85
85
|
}
|
|
86
86
|
this.waitingEdges.add([startKey, endKey]);
|
|
87
87
|
return this;
|
|
@@ -94,7 +94,7 @@ class StateGraph extends graph_js_1.Graph {
|
|
|
94
94
|
]);
|
|
95
95
|
// prepare output channels
|
|
96
96
|
const stateKeys = Object.keys(this.channels);
|
|
97
|
-
const
|
|
97
|
+
const outputChannels = stateKeys.length === 1 && stateKeys[0] === ROOT
|
|
98
98
|
? stateKeys[0]
|
|
99
99
|
: stateKeys;
|
|
100
100
|
// create empty compiled graph
|
|
@@ -109,9 +109,9 @@ class StateGraph extends graph_js_1.Graph {
|
|
|
109
109
|
...this.channels,
|
|
110
110
|
[graph_js_1.START]: new ephemeral_value_js_1.EphemeralValue(),
|
|
111
111
|
},
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
streamChannels:
|
|
112
|
+
inputChannels: graph_js_1.START,
|
|
113
|
+
outputChannels,
|
|
114
|
+
streamChannels: outputChannels,
|
|
115
115
|
streamMode: "updates",
|
|
116
116
|
});
|
|
117
117
|
// attach nodes, edges and branches
|
|
@@ -155,7 +155,8 @@ class CompiledStateGraph extends graph_js_1.CompiledGraph {
|
|
|
155
155
|
return write_js_1.SKIP_WRITE;
|
|
156
156
|
}
|
|
157
157
|
else if (typeof input !== "object" || Array.isArray(input)) {
|
|
158
|
-
|
|
158
|
+
const typeofInput = Array.isArray(input) ? "array" : typeof input;
|
|
159
|
+
throw new errors_js_1.InvalidUpdateError(`Expected object, got ${typeofInput}`);
|
|
159
160
|
}
|
|
160
161
|
else {
|
|
161
162
|
return key in input ? input[key] : write_js_1.SKIP_WRITE;
|
|
@@ -252,7 +253,7 @@ class CompiledStateGraph extends graph_js_1.CompiledGraph {
|
|
|
252
253
|
return new write_js_1.ChannelWrite(writes, [constants_js_1.TAG_HIDDEN]);
|
|
253
254
|
},
|
|
254
255
|
// reader
|
|
255
|
-
(config) => read_js_1.ChannelRead.doRead(config, this.
|
|
256
|
+
(config) => read_js_1.ChannelRead.doRead(config, this.outputChannels, true)));
|
|
256
257
|
// attach branch subscribers
|
|
257
258
|
const ends = branch.ends
|
|
258
259
|
? Object.values(branch.ends)
|
package/dist/graph/state.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Runnable, RunnableConfig, RunnableLike } from "@langchain/core/runnables";
|
|
2
|
+
import { BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
|
|
2
3
|
import { BaseChannel } from "../channels/base.js";
|
|
3
4
|
import { END, CompiledGraph, Graph, START, Branch } from "./graph.js";
|
|
4
|
-
import { BaseCheckpointSaver } from "../checkpoint/base.js";
|
|
5
5
|
import { All } from "../pregel/types.js";
|
|
6
6
|
import { AnnotationRoot, SingleReducer, StateDefinition, StateType, UpdateType } from "./annotation.js";
|
|
7
7
|
export type ChannelReducers<Channels extends object> = {
|
package/dist/graph/state.js
CHANGED
|
@@ -71,14 +71,14 @@ export class StateGraph extends Graph {
|
|
|
71
71
|
throw new Error("END cannot be a start node");
|
|
72
72
|
}
|
|
73
73
|
if (!Object.keys(this.nodes).some((node) => node === start)) {
|
|
74
|
-
throw new Error(`Need to
|
|
74
|
+
throw new Error(`Need to addNode ${start} first`);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
if (endKey === END) {
|
|
78
78
|
throw new Error("END cannot be an end node");
|
|
79
79
|
}
|
|
80
80
|
if (!Object.keys(this.nodes).some((node) => node === endKey)) {
|
|
81
|
-
throw new Error(`Need to
|
|
81
|
+
throw new Error(`Need to addNode ${endKey} first`);
|
|
82
82
|
}
|
|
83
83
|
this.waitingEdges.add([startKey, endKey]);
|
|
84
84
|
return this;
|
|
@@ -91,7 +91,7 @@ export class StateGraph extends Graph {
|
|
|
91
91
|
]);
|
|
92
92
|
// prepare output channels
|
|
93
93
|
const stateKeys = Object.keys(this.channels);
|
|
94
|
-
const
|
|
94
|
+
const outputChannels = stateKeys.length === 1 && stateKeys[0] === ROOT
|
|
95
95
|
? stateKeys[0]
|
|
96
96
|
: stateKeys;
|
|
97
97
|
// create empty compiled graph
|
|
@@ -106,9 +106,9 @@ export class StateGraph extends Graph {
|
|
|
106
106
|
...this.channels,
|
|
107
107
|
[START]: new EphemeralValue(),
|
|
108
108
|
},
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
streamChannels:
|
|
109
|
+
inputChannels: START,
|
|
110
|
+
outputChannels,
|
|
111
|
+
streamChannels: outputChannels,
|
|
112
112
|
streamMode: "updates",
|
|
113
113
|
});
|
|
114
114
|
// attach nodes, edges and branches
|
|
@@ -151,7 +151,8 @@ export class CompiledStateGraph extends CompiledGraph {
|
|
|
151
151
|
return SKIP_WRITE;
|
|
152
152
|
}
|
|
153
153
|
else if (typeof input !== "object" || Array.isArray(input)) {
|
|
154
|
-
|
|
154
|
+
const typeofInput = Array.isArray(input) ? "array" : typeof input;
|
|
155
|
+
throw new InvalidUpdateError(`Expected object, got ${typeofInput}`);
|
|
155
156
|
}
|
|
156
157
|
else {
|
|
157
158
|
return key in input ? input[key] : SKIP_WRITE;
|
|
@@ -248,7 +249,7 @@ export class CompiledStateGraph extends CompiledGraph {
|
|
|
248
249
|
return new ChannelWrite(writes, [TAG_HIDDEN]);
|
|
249
250
|
},
|
|
250
251
|
// reader
|
|
251
|
-
(config) => ChannelRead.doRead(config, this.
|
|
252
|
+
(config) => ChannelRead.doRead(config, this.outputChannels, true)));
|
|
252
253
|
// attach branch subscribers
|
|
253
254
|
const ends = branch.ends
|
|
254
255
|
? Object.values(branch.ends)
|
|
@@ -2,7 +2,7 @@ import { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
|
2
2
|
import { BaseMessage, SystemMessage } from "@langchain/core/messages";
|
|
3
3
|
import { Runnable, RunnableToolLike } from "@langchain/core/runnables";
|
|
4
4
|
import { StructuredToolInterface } from "@langchain/core/tools";
|
|
5
|
-
import { BaseCheckpointSaver } from "
|
|
5
|
+
import { BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
|
|
6
6
|
import { START } from "../graph/index.js";
|
|
7
7
|
import { MessagesState } from "../graph/message.js";
|
|
8
8
|
import { CompiledStateGraph } from "../graph/state.js";
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._prepareNextTasks = exports._applyWrites = exports._localWrite = exports._localRead = exports.shouldInterrupt = exports.executeTasks = exports.increment = void 0;
|
|
4
|
+
/* eslint-disable no-param-reassign */
|
|
5
|
+
const runnables_1 = require("@langchain/core/runnables");
|
|
6
|
+
const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
|
|
7
|
+
const base_js_1 = require("../channels/base.cjs");
|
|
8
|
+
const io_js_1 = require("./io.cjs");
|
|
9
|
+
const constants_js_1 = require("../constants.cjs");
|
|
10
|
+
const errors_js_1 = require("../errors.cjs");
|
|
11
|
+
const utils_js_1 = require("./utils.cjs");
|
|
12
|
+
const increment = (current) => {
|
|
13
|
+
return current !== undefined ? current + 1 : 1;
|
|
14
|
+
};
|
|
15
|
+
exports.increment = increment;
|
|
16
|
+
async function* executeTasks(tasks, stepTimeout, signal
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
) {
|
|
19
|
+
if (stepTimeout && signal) {
|
|
20
|
+
if ("any" in AbortSignal) {
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
signal = AbortSignal.any([
|
|
23
|
+
signal,
|
|
24
|
+
AbortSignal.timeout(stepTimeout),
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else if (stepTimeout) {
|
|
29
|
+
signal = AbortSignal.timeout(stepTimeout);
|
|
30
|
+
}
|
|
31
|
+
// Abort if signal is aborted
|
|
32
|
+
signal?.throwIfAborted();
|
|
33
|
+
// Start all tasks
|
|
34
|
+
const executingTasks = Object.fromEntries(Object.entries(tasks).map(([taskId, task]) => {
|
|
35
|
+
return [taskId, task()];
|
|
36
|
+
}));
|
|
37
|
+
let listener;
|
|
38
|
+
const signalPromise = new Promise((_resolve, reject) => {
|
|
39
|
+
listener = () => reject(new Error("Abort"));
|
|
40
|
+
signal?.addEventListener("abort", listener);
|
|
41
|
+
}).finally(() => signal?.removeEventListener("abort", listener));
|
|
42
|
+
while (Object.keys(executingTasks).length > 0) {
|
|
43
|
+
const { task, error } = await Promise.race([
|
|
44
|
+
...Object.values(executingTasks),
|
|
45
|
+
signalPromise,
|
|
46
|
+
]);
|
|
47
|
+
if (error !== undefined) {
|
|
48
|
+
// TODO: don't stop others if exception is interrupt
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
yield task;
|
|
52
|
+
delete executingTasks[task.id];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.executeTasks = executeTasks;
|
|
56
|
+
function shouldInterrupt(checkpoint, interruptNodes, tasks) {
|
|
57
|
+
const versionValues = Object.values(checkpoint.channel_versions);
|
|
58
|
+
const versionType = versionValues.length > 0 ? typeof versionValues[0] : undefined;
|
|
59
|
+
let nullVersion;
|
|
60
|
+
if (versionType === "number") {
|
|
61
|
+
nullVersion = 0;
|
|
62
|
+
}
|
|
63
|
+
else if (versionType === "string") {
|
|
64
|
+
nullVersion = "";
|
|
65
|
+
}
|
|
66
|
+
const seen = checkpoint.versions_seen[constants_js_1.INTERRUPT] ?? {};
|
|
67
|
+
const anyChannelUpdated = Object.entries(checkpoint.channel_versions).some(([chan, version]) => {
|
|
68
|
+
return version > (seen[chan] ?? nullVersion);
|
|
69
|
+
});
|
|
70
|
+
const anyTriggeredNodeInInterruptNodes = tasks.some((task) => interruptNodes === "*"
|
|
71
|
+
? !task.config?.tags?.includes(constants_js_1.TAG_HIDDEN)
|
|
72
|
+
: interruptNodes.includes(task.name));
|
|
73
|
+
return anyChannelUpdated && anyTriggeredNodeInInterruptNodes;
|
|
74
|
+
}
|
|
75
|
+
exports.shouldInterrupt = shouldInterrupt;
|
|
76
|
+
function _localRead(checkpoint, channels, task, select, fresh = false) {
|
|
77
|
+
if (fresh) {
|
|
78
|
+
const newCheckpoint = (0, base_js_1.createCheckpoint)(checkpoint, channels, -1);
|
|
79
|
+
// create a new copy of channels
|
|
80
|
+
const newChannels = (0, base_js_1.emptyChannels)(channels, newCheckpoint);
|
|
81
|
+
// Note: _applyWrites contains side effects
|
|
82
|
+
_applyWrites((0, langgraph_checkpoint_1.copyCheckpoint)(newCheckpoint), newChannels, [task]);
|
|
83
|
+
return (0, io_js_1.readChannels)(newChannels, select);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
return (0, io_js_1.readChannels)(channels, select);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports._localRead = _localRead;
|
|
90
|
+
function _localWrite(
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
|
+
commit, processes, channels,
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
|
+
writes) {
|
|
95
|
+
for (const [chan, value] of writes) {
|
|
96
|
+
if (chan === constants_js_1.TASKS) {
|
|
97
|
+
if (!(0, constants_js_1._isSend)(value)) {
|
|
98
|
+
throw new errors_js_1.InvalidUpdateError(`Invalid packet type, expected SendProtocol, got ${JSON.stringify(value)}`);
|
|
99
|
+
}
|
|
100
|
+
if (!(value.node in processes)) {
|
|
101
|
+
throw new errors_js_1.InvalidUpdateError(`Invalid node name ${value.node} in packet`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else if (!(chan in channels)) {
|
|
105
|
+
console.warn(`Skipping write for channel '${chan}' which has no readers`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
commit(writes);
|
|
109
|
+
}
|
|
110
|
+
exports._localWrite = _localWrite;
|
|
111
|
+
function _applyWrites(checkpoint, channels, tasks,
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
|
+
getNextVersion) {
|
|
114
|
+
// Update seen versions
|
|
115
|
+
for (const task of tasks) {
|
|
116
|
+
if (checkpoint.versions_seen[task.name] === undefined) {
|
|
117
|
+
checkpoint.versions_seen[task.name] = {};
|
|
118
|
+
}
|
|
119
|
+
for (const chan of task.triggers) {
|
|
120
|
+
if (chan in checkpoint.channel_versions) {
|
|
121
|
+
checkpoint.versions_seen[task.name][chan] =
|
|
122
|
+
checkpoint.channel_versions[chan];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Find the highest version of all channels
|
|
127
|
+
let maxVersion;
|
|
128
|
+
if (Object.keys(checkpoint.channel_versions).length > 0) {
|
|
129
|
+
maxVersion = Math.max(...Object.values(checkpoint.channel_versions));
|
|
130
|
+
}
|
|
131
|
+
// Consume all channels that were read
|
|
132
|
+
const channelsToConsume = new Set(tasks
|
|
133
|
+
.flatMap((task) => task.triggers)
|
|
134
|
+
.filter((chan) => !constants_js_1.RESERVED.includes(chan)));
|
|
135
|
+
for (const chan of channelsToConsume) {
|
|
136
|
+
if (channels[chan].consume()) {
|
|
137
|
+
if (getNextVersion !== undefined) {
|
|
138
|
+
checkpoint.channel_versions[chan] = getNextVersion(maxVersion, channels[chan]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Clear pending sends
|
|
143
|
+
if (checkpoint.pending_sends) {
|
|
144
|
+
checkpoint.pending_sends = [];
|
|
145
|
+
}
|
|
146
|
+
// Group writes by channel
|
|
147
|
+
const pendingWriteValuesByChannel = {};
|
|
148
|
+
for (const task of tasks) {
|
|
149
|
+
for (const [chan, val] of task.writes) {
|
|
150
|
+
if (chan === constants_js_1.TASKS) {
|
|
151
|
+
checkpoint.pending_sends.push({
|
|
152
|
+
node: val.node,
|
|
153
|
+
args: val.args,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
if (chan in pendingWriteValuesByChannel) {
|
|
158
|
+
pendingWriteValuesByChannel[chan].push(val);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
pendingWriteValuesByChannel[chan] = [val];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// find the highest version of all channels
|
|
167
|
+
maxVersion = undefined;
|
|
168
|
+
if (Object.keys(checkpoint.channel_versions).length > 0) {
|
|
169
|
+
maxVersion = Math.max(...Object.values(checkpoint.channel_versions));
|
|
170
|
+
}
|
|
171
|
+
const updatedChannels = new Set();
|
|
172
|
+
// Apply writes to channels
|
|
173
|
+
for (const [chan, vals] of Object.entries(pendingWriteValuesByChannel)) {
|
|
174
|
+
if (chan in channels) {
|
|
175
|
+
let updated;
|
|
176
|
+
try {
|
|
177
|
+
updated = channels[chan].update(vals);
|
|
178
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
if (e.name === errors_js_1.InvalidUpdateError.unminifiable_name) {
|
|
182
|
+
throw new errors_js_1.InvalidUpdateError(`Invalid update for channel ${chan} with values ${JSON.stringify(vals)}`);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
throw e;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (updated && getNextVersion !== undefined) {
|
|
189
|
+
checkpoint.channel_versions[chan] = getNextVersion(maxVersion, channels[chan]);
|
|
190
|
+
}
|
|
191
|
+
updatedChannels.add(chan);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Channels that weren't updated in this step are notified of a new step
|
|
195
|
+
for (const chan of Object.keys(channels)) {
|
|
196
|
+
if (!updatedChannels.has(chan)) {
|
|
197
|
+
const updated = channels[chan].update([]);
|
|
198
|
+
if (updated && getNextVersion !== undefined) {
|
|
199
|
+
checkpoint.channel_versions[chan] = getNextVersion(maxVersion, channels[chan]);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
exports._applyWrites = _applyWrites;
|
|
205
|
+
function _prepareNextTasks(checkpoint, processes, channels, config, forExecution, extra) {
|
|
206
|
+
const parentNamespace = config.configurable?.checkpoint_ns ?? "";
|
|
207
|
+
const tasks = [];
|
|
208
|
+
const taskDescriptions = [];
|
|
209
|
+
const { step, isResuming = false, checkpointer, manager } = extra;
|
|
210
|
+
for (const packet of checkpoint.pending_sends) {
|
|
211
|
+
if (!(0, constants_js_1._isSendInterface)(packet)) {
|
|
212
|
+
console.warn(`Ignoring invalid packet ${JSON.stringify(packet)} in pending sends.`);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (!(packet.node in processes)) {
|
|
216
|
+
console.warn(`Ignoring unknown node name ${packet.node} in pending sends.`);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const triggers = [constants_js_1.TASKS];
|
|
220
|
+
const metadata = (0, utils_js_1._getIdMetadata)({
|
|
221
|
+
langgraph_step: step,
|
|
222
|
+
langgraph_node: packet.node,
|
|
223
|
+
langgraph_triggers: triggers,
|
|
224
|
+
langgraph_task_idx: forExecution ? tasks.length : taskDescriptions.length,
|
|
225
|
+
});
|
|
226
|
+
const checkpointNamespace = parentNamespace === ""
|
|
227
|
+
? packet.node
|
|
228
|
+
: `${parentNamespace}${constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR}${packet.node}`;
|
|
229
|
+
const taskId = (0, langgraph_checkpoint_1.uuid5)(JSON.stringify([checkpointNamespace, metadata]), checkpoint.id);
|
|
230
|
+
if (forExecution) {
|
|
231
|
+
const proc = processes[packet.node];
|
|
232
|
+
const node = proc.getNode();
|
|
233
|
+
if (node !== undefined) {
|
|
234
|
+
const writes = [];
|
|
235
|
+
tasks.push({
|
|
236
|
+
name: packet.node,
|
|
237
|
+
input: packet.args,
|
|
238
|
+
proc: node,
|
|
239
|
+
writes,
|
|
240
|
+
triggers,
|
|
241
|
+
config: (0, runnables_1.patchConfig)((0, runnables_1.mergeConfigs)(config, processes[packet.node].config, {
|
|
242
|
+
metadata,
|
|
243
|
+
}), {
|
|
244
|
+
runName: packet.node,
|
|
245
|
+
callbacks: manager?.getChild(`graph:step:${step}`),
|
|
246
|
+
configurable: {
|
|
247
|
+
[constants_js_1.CONFIG_KEY_SEND]: _localWrite.bind(undefined, (items) => writes.push(...items), processes, channels),
|
|
248
|
+
[constants_js_1.CONFIG_KEY_READ]: _localRead.bind(undefined, checkpoint, channels, {
|
|
249
|
+
name: packet.node,
|
|
250
|
+
writes: writes,
|
|
251
|
+
triggers,
|
|
252
|
+
}),
|
|
253
|
+
},
|
|
254
|
+
}),
|
|
255
|
+
id: taskId,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
taskDescriptions.push({ id: taskId, name: packet.node });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// Check if any processes should be run in next step
|
|
264
|
+
// If so, prepare the values to be passed to them
|
|
265
|
+
const nullVersion = (0, utils_js_1.getNullChannelVersion)(checkpoint.channel_versions);
|
|
266
|
+
if (nullVersion === undefined) {
|
|
267
|
+
return forExecution ? tasks : taskDescriptions;
|
|
268
|
+
}
|
|
269
|
+
for (const [name, proc] of Object.entries(processes)) {
|
|
270
|
+
const seen = checkpoint.versions_seen[name] ?? {};
|
|
271
|
+
const triggers = proc.triggers
|
|
272
|
+
.filter((chan) => {
|
|
273
|
+
const result = (0, io_js_1.readChannel)(channels, chan, false, true);
|
|
274
|
+
const isEmptyChannelError =
|
|
275
|
+
// eslint-disable-next-line no-instanceof/no-instanceof
|
|
276
|
+
result instanceof Error &&
|
|
277
|
+
result.name === errors_js_1.EmptyChannelError.unminifiable_name;
|
|
278
|
+
return (!isEmptyChannelError &&
|
|
279
|
+
(checkpoint.channel_versions[chan] ?? nullVersion) >
|
|
280
|
+
(seen[chan] ?? nullVersion));
|
|
281
|
+
})
|
|
282
|
+
.sort();
|
|
283
|
+
// If any of the channels read by this process were updated
|
|
284
|
+
if (triggers.length > 0) {
|
|
285
|
+
const val = _procInput(proc, channels, forExecution);
|
|
286
|
+
if (val === undefined) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const metadata = (0, utils_js_1._getIdMetadata)({
|
|
290
|
+
langgraph_step: step,
|
|
291
|
+
langgraph_node: name,
|
|
292
|
+
langgraph_triggers: triggers,
|
|
293
|
+
langgraph_task_idx: forExecution
|
|
294
|
+
? tasks.length
|
|
295
|
+
: taskDescriptions.length,
|
|
296
|
+
});
|
|
297
|
+
const checkpointNamespace = parentNamespace === ""
|
|
298
|
+
? name
|
|
299
|
+
: `${parentNamespace}${constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR}${name}`;
|
|
300
|
+
const taskId = (0, langgraph_checkpoint_1.uuid5)(JSON.stringify([checkpointNamespace, metadata]), checkpoint.id);
|
|
301
|
+
if (forExecution) {
|
|
302
|
+
const node = proc.getNode();
|
|
303
|
+
if (node !== undefined) {
|
|
304
|
+
const writes = [];
|
|
305
|
+
tasks.push({
|
|
306
|
+
name,
|
|
307
|
+
input: val,
|
|
308
|
+
proc: node,
|
|
309
|
+
writes,
|
|
310
|
+
triggers,
|
|
311
|
+
config: (0, runnables_1.patchConfig)((0, runnables_1.mergeConfigs)(config, proc.config, { metadata }), {
|
|
312
|
+
runName: name,
|
|
313
|
+
callbacks: manager?.getChild(`graph:step:${step}`),
|
|
314
|
+
configurable: {
|
|
315
|
+
[constants_js_1.CONFIG_KEY_SEND]: _localWrite.bind(undefined, (items) => writes.push(...items), processes, channels),
|
|
316
|
+
[constants_js_1.CONFIG_KEY_READ]: _localRead.bind(undefined, checkpoint, channels, {
|
|
317
|
+
name,
|
|
318
|
+
writes: writes,
|
|
319
|
+
triggers,
|
|
320
|
+
}),
|
|
321
|
+
[constants_js_1.CONFIG_KEY_CHECKPOINTER]: checkpointer,
|
|
322
|
+
[constants_js_1.CONFIG_KEY_RESUMING]: isResuming,
|
|
323
|
+
checkpoint_id: checkpoint.id,
|
|
324
|
+
checkpoint_ns: checkpointNamespace,
|
|
325
|
+
},
|
|
326
|
+
}),
|
|
327
|
+
id: taskId,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
taskDescriptions.push({ id: taskId, name });
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return forExecution ? tasks : taskDescriptions;
|
|
337
|
+
}
|
|
338
|
+
exports._prepareNextTasks = _prepareNextTasks;
|
|
339
|
+
function _procInput(proc, channels, forExecution) {
|
|
340
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
341
|
+
let val;
|
|
342
|
+
// If all trigger channels subscribed by this process are not empty
|
|
343
|
+
// then invoke the process with the values of all non-empty channels
|
|
344
|
+
if (Array.isArray(proc.channels)) {
|
|
345
|
+
let successfulRead = false;
|
|
346
|
+
for (const chan of proc.channels) {
|
|
347
|
+
try {
|
|
348
|
+
val = (0, io_js_1.readChannel)(channels, chan, false);
|
|
349
|
+
successfulRead = true;
|
|
350
|
+
break;
|
|
351
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
352
|
+
}
|
|
353
|
+
catch (e) {
|
|
354
|
+
if (e.name === errors_js_1.EmptyChannelError.unminifiable_name) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
throw e;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (!successfulRead) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
else if (typeof proc.channels === "object") {
|
|
367
|
+
val = {};
|
|
368
|
+
try {
|
|
369
|
+
for (const [k, chan] of Object.entries(proc.channels)) {
|
|
370
|
+
val[k] = (0, io_js_1.readChannel)(channels, chan, !proc.triggers.includes(chan));
|
|
371
|
+
}
|
|
372
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
373
|
+
}
|
|
374
|
+
catch (e) {
|
|
375
|
+
if (e.name === errors_js_1.EmptyChannelError.unminifiable_name) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
throw e;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
throw new Error(`Invalid channels type, expected list or dict, got ${proc.channels}`);
|
|
385
|
+
}
|
|
386
|
+
// If the process has a mapper, apply it to the value
|
|
387
|
+
if (forExecution && proc.mapper !== undefined) {
|
|
388
|
+
val = proc.mapper(val);
|
|
389
|
+
}
|
|
390
|
+
return val;
|
|
391
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { RunnableConfig } from "@langchain/core/runnables";
|
|
2
|
+
import { CallbackManagerForChainRun } from "@langchain/core/callbacks/manager";
|
|
3
|
+
import { BaseCheckpointSaver, Checkpoint, ReadonlyCheckpoint, type PendingWrite } from "@langchain/langgraph-checkpoint";
|
|
4
|
+
import { BaseChannel } from "../channels/base.js";
|
|
5
|
+
import { PregelNode } from "./read.js";
|
|
6
|
+
import { All, PregelExecutableTask, PregelTaskDescription } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Construct a type with a set of properties K of type T
|
|
9
|
+
*/
|
|
10
|
+
export type StrRecord<K extends string, T> = {
|
|
11
|
+
[P in K]: T;
|
|
12
|
+
};
|
|
13
|
+
export type WritesProtocol<C = string> = {
|
|
14
|
+
name: string;
|
|
15
|
+
writes: PendingWrite<C>[];
|
|
16
|
+
triggers: string[];
|
|
17
|
+
};
|
|
18
|
+
export declare const increment: (current?: number) => number;
|
|
19
|
+
export declare function executeTasks(tasks: Record<string, () => Promise<{
|
|
20
|
+
task: PregelExecutableTask<any, any>;
|
|
21
|
+
result: any;
|
|
22
|
+
error: Error;
|
|
23
|
+
}>>, stepTimeout?: number, signal?: AbortSignal): AsyncGenerator<PregelExecutableTask<any, any>>;
|
|
24
|
+
export declare function shouldInterrupt<N extends PropertyKey, C extends PropertyKey>(checkpoint: Checkpoint, interruptNodes: All | N[], tasks: PregelExecutableTask<N, C>[]): boolean;
|
|
25
|
+
export declare function _localRead<Cc extends StrRecord<string, BaseChannel>>(checkpoint: ReadonlyCheckpoint, channels: Cc, task: WritesProtocol<keyof Cc>, select: Array<keyof Cc> | keyof Cc, fresh?: boolean): Record<string, unknown> | unknown;
|
|
26
|
+
export declare function _localWrite(commit: (writes: [string, any][]) => void, processes: Record<string, PregelNode>, channels: Record<string, BaseChannel>, writes: [string, any][]): void;
|
|
27
|
+
export declare function _applyWrites<Cc extends Record<string, BaseChannel>>(checkpoint: Checkpoint, channels: Cc, tasks: WritesProtocol<keyof Cc>[], getNextVersion?: (version: any, channel: BaseChannel) => any): void;
|
|
28
|
+
export type NextTaskExtraFields = {
|
|
29
|
+
step: number;
|
|
30
|
+
isResuming?: boolean;
|
|
31
|
+
checkpointer?: BaseCheckpointSaver;
|
|
32
|
+
manager?: CallbackManagerForChainRun;
|
|
33
|
+
};
|
|
34
|
+
export declare function _prepareNextTasks<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(checkpoint: ReadonlyCheckpoint, processes: Nn, channels: Cc, config: RunnableConfig, forExecution: false, extra: NextTaskExtraFields): PregelTaskDescription[];
|
|
35
|
+
export declare function _prepareNextTasks<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>>(checkpoint: ReadonlyCheckpoint, processes: Nn, channels: Cc, config: RunnableConfig, forExecution: true, extra: NextTaskExtraFields): PregelExecutableTask<keyof Nn, keyof Cc>[];
|