@langchain/langgraph 0.0.12 → 0.0.14
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/checkpoint/sqlite.cjs +1 -0
- package/checkpoint/sqlite.d.cts +1 -0
- package/checkpoint/sqlite.d.ts +1 -0
- package/checkpoint/sqlite.js +1 -0
- package/dist/channels/any_value.cjs +57 -0
- package/dist/channels/any_value.d.ts +16 -0
- package/dist/channels/any_value.js +53 -0
- package/dist/channels/base.cjs +19 -28
- package/dist/channels/base.d.ts +13 -19
- package/dist/channels/base.js +17 -24
- package/dist/channels/binop.cjs +4 -3
- package/dist/channels/binop.d.ts +1 -1
- package/dist/channels/binop.js +3 -2
- package/dist/channels/dynamic_barrier_value.cjs +88 -0
- package/dist/channels/dynamic_barrier_value.d.ts +26 -0
- package/dist/channels/dynamic_barrier_value.js +84 -0
- package/dist/channels/ephemeral_value.cjs +64 -0
- package/dist/channels/ephemeral_value.d.ts +14 -0
- package/dist/channels/ephemeral_value.js +60 -0
- package/dist/channels/index.cjs +1 -3
- package/dist/channels/index.d.ts +1 -1
- package/dist/channels/index.js +1 -1
- package/dist/channels/last_value.cjs +11 -5
- package/dist/channels/last_value.d.ts +5 -1
- package/dist/channels/last_value.js +9 -3
- package/dist/channels/named_barrier_value.cjs +71 -0
- package/dist/channels/named_barrier_value.d.ts +18 -0
- package/dist/channels/named_barrier_value.js +66 -0
- package/dist/channels/topic.cjs +5 -3
- package/dist/channels/topic.d.ts +3 -3
- package/dist/channels/topic.js +5 -3
- package/dist/checkpoint/base.cjs +30 -12
- package/dist/checkpoint/base.d.ts +39 -22
- package/dist/checkpoint/base.js +28 -11
- package/dist/checkpoint/id.cjs +40 -0
- package/dist/checkpoint/id.d.ts +2 -0
- package/dist/checkpoint/id.js +35 -0
- package/dist/checkpoint/index.cjs +2 -2
- package/dist/checkpoint/index.d.ts +2 -2
- package/dist/checkpoint/index.js +2 -2
- package/dist/checkpoint/memory.cjs +66 -49
- package/dist/checkpoint/memory.d.ts +7 -10
- package/dist/checkpoint/memory.js +65 -47
- package/dist/checkpoint/sqlite.cjs +173 -0
- package/dist/checkpoint/sqlite.d.ts +14 -0
- package/dist/checkpoint/sqlite.js +166 -0
- package/dist/constants.cjs +3 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/errors.cjs +31 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +24 -0
- package/dist/graph/graph.cjs +235 -95
- package/dist/graph/graph.d.ts +52 -23
- package/dist/graph/graph.js +234 -96
- package/dist/graph/index.cjs +2 -2
- package/dist/graph/index.d.ts +2 -2
- package/dist/graph/index.js +2 -2
- package/dist/graph/message.cjs +4 -3
- package/dist/graph/message.d.ts +4 -1
- package/dist/graph/message.js +4 -3
- package/dist/graph/state.cjs +237 -102
- package/dist/graph/state.d.ts +41 -18
- package/dist/graph/state.js +238 -104
- package/dist/index.cjs +12 -7
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/prebuilt/agent_executor.cjs +22 -36
- package/dist/prebuilt/agent_executor.d.ts +7 -10
- package/dist/prebuilt/agent_executor.js +23 -37
- package/dist/prebuilt/chat_agent_executor.cjs +13 -13
- package/dist/prebuilt/chat_agent_executor.d.ts +3 -1
- package/dist/prebuilt/chat_agent_executor.js +15 -15
- package/dist/prebuilt/index.cjs +4 -1
- package/dist/prebuilt/index.d.ts +1 -0
- package/dist/prebuilt/index.js +1 -0
- package/dist/prebuilt/tool_node.cjs +59 -0
- package/dist/prebuilt/tool_node.d.ts +17 -0
- package/dist/prebuilt/tool_node.js +54 -0
- package/dist/pregel/debug.cjs +6 -8
- package/dist/pregel/debug.d.ts +2 -2
- package/dist/pregel/debug.js +5 -7
- package/dist/pregel/index.cjs +504 -232
- package/dist/pregel/index.d.ts +80 -41
- package/dist/pregel/index.js +507 -238
- package/dist/pregel/io.cjs +117 -30
- package/dist/pregel/io.d.ts +11 -3
- package/dist/pregel/io.js +111 -28
- package/dist/pregel/read.cjs +126 -46
- package/dist/pregel/read.d.ts +27 -18
- package/dist/pregel/read.js +125 -45
- package/dist/pregel/types.cjs +2 -0
- package/dist/pregel/types.d.ts +37 -0
- package/dist/pregel/types.js +1 -0
- package/dist/pregel/validate.cjs +58 -51
- package/dist/pregel/validate.d.ts +14 -13
- package/dist/pregel/validate.js +56 -50
- package/dist/pregel/write.cjs +46 -30
- package/dist/pregel/write.d.ts +18 -8
- package/dist/pregel/write.js +45 -29
- package/dist/serde/base.cjs +2 -0
- package/dist/serde/base.d.ts +4 -0
- package/dist/serde/base.js +1 -0
- package/dist/setup/async_local_storage.cjs +2 -2
- package/dist/setup/async_local_storage.js +1 -1
- package/dist/tests/channels.test.d.ts +1 -0
- package/dist/tests/channels.test.js +151 -0
- package/dist/tests/chatbot.int.test.d.ts +1 -0
- package/dist/tests/chatbot.int.test.js +61 -0
- package/dist/tests/checkpoints.test.d.ts +1 -0
- package/dist/tests/checkpoints.test.js +190 -0
- package/dist/tests/graph.test.d.ts +1 -0
- package/dist/tests/graph.test.js +15 -0
- package/dist/tests/prebuilt.int.test.d.ts +1 -0
- package/dist/tests/prebuilt.int.test.js +101 -0
- package/dist/tests/prebuilt.test.d.ts +1 -0
- package/dist/tests/prebuilt.test.js +195 -0
- package/dist/tests/pregel.io.test.d.ts +1 -0
- package/dist/tests/pregel.io.test.js +332 -0
- package/dist/tests/pregel.read.test.d.ts +1 -0
- package/dist/tests/pregel.read.test.js +109 -0
- package/dist/tests/pregel.test.d.ts +1 -0
- package/dist/tests/pregel.test.js +1879 -0
- package/dist/tests/pregel.validate.test.d.ts +1 -0
- package/dist/tests/pregel.validate.test.js +198 -0
- package/dist/tests/pregel.write.test.d.ts +1 -0
- package/dist/tests/pregel.write.test.js +44 -0
- package/dist/tests/tracing.int.test.d.ts +1 -0
- package/dist/tests/tracing.int.test.js +449 -0
- package/dist/tests/utils.d.ts +22 -0
- package/dist/tests/utils.js +76 -0
- package/dist/utils.cjs +74 -0
- package/dist/utils.d.ts +18 -0
- package/dist/utils.js +70 -0
- package/package.json +33 -8
- package/dist/pregel/reserved.cjs +0 -6
- package/dist/pregel/reserved.d.ts +0 -3
- package/dist/pregel/reserved.js +0 -3
package/dist/pregel/index.js
CHANGED
|
@@ -1,83 +1,71 @@
|
|
|
1
1
|
/* eslint-disable no-param-reassign */
|
|
2
|
-
import { Runnable, _coerceToRunnable, ensureConfig, patchConfig, } from "@langchain/core/runnables";
|
|
2
|
+
import { Runnable, RunnableSequence, _coerceToRunnable, ensureConfig, patchConfig, } from "@langchain/core/runnables";
|
|
3
3
|
import { IterableReadableStream } from "@langchain/core/utils/stream";
|
|
4
|
-
import {
|
|
5
|
-
import { emptyCheckpoint, } from "../checkpoint/base.js";
|
|
6
|
-
import {
|
|
7
|
-
import { validateGraph } from "./validate.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { CONFIG_KEY_READ, CONFIG_KEY_SEND } from "../constants.js";
|
|
4
|
+
import { createCheckpoint, emptyChannels, } from "../channels/base.js";
|
|
5
|
+
import { copyCheckpoint, emptyCheckpoint, } from "../checkpoint/base.js";
|
|
6
|
+
import { PregelNode } from "./read.js";
|
|
7
|
+
import { validateGraph, validateKeys } from "./validate.js";
|
|
8
|
+
import { mapInput, mapOutputUpdates, mapOutputValues, readChannel, readChannels, single, } from "./io.js";
|
|
9
|
+
import { ChannelWrite, PASSTHROUGH } from "./write.js";
|
|
10
|
+
import { CONFIG_KEY_READ, CONFIG_KEY_SEND, INTERRUPT, TAG_HIDDEN, } from "../constants.js";
|
|
12
11
|
import { initializeAsyncLocalStorageSingleton } from "../setup/async_local_storage.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
constructor(message) {
|
|
16
|
-
super(message);
|
|
17
|
-
this.name = "GraphRecursionError";
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function _coerceWriteValue(value) {
|
|
21
|
-
if (!Runnable.isRunnable(value) && typeof value !== "function") {
|
|
22
|
-
return _coerceToRunnable(() => value);
|
|
23
|
-
}
|
|
24
|
-
return _coerceToRunnable(value);
|
|
25
|
-
}
|
|
12
|
+
import { EmptyChannelError, GraphRecursionError, GraphValueError, InvalidUpdateError, } from "../errors.js";
|
|
13
|
+
const DEFAULT_LOOP_LIMIT = 25;
|
|
26
14
|
function isString(value) {
|
|
27
15
|
return typeof value === "string";
|
|
28
16
|
}
|
|
29
17
|
export class Channel {
|
|
30
18
|
static subscribeTo(channels, options) {
|
|
31
|
-
const { key,
|
|
19
|
+
const { key, tags } = options ?? {};
|
|
32
20
|
if (Array.isArray(channels) && key !== undefined) {
|
|
33
21
|
throw new Error("Can't specify a key when subscribing to multiple channels");
|
|
34
22
|
}
|
|
35
|
-
let
|
|
23
|
+
let channelMappingOrArray;
|
|
36
24
|
if (isString(channels)) {
|
|
37
25
|
if (key) {
|
|
38
|
-
|
|
26
|
+
channelMappingOrArray = { [key]: channels };
|
|
39
27
|
}
|
|
40
28
|
else {
|
|
41
|
-
|
|
29
|
+
channelMappingOrArray = [channels];
|
|
42
30
|
}
|
|
43
31
|
}
|
|
44
32
|
else {
|
|
45
|
-
|
|
33
|
+
channelMappingOrArray = Object.fromEntries(channels.map((chan) => [chan, chan]));
|
|
46
34
|
}
|
|
47
35
|
const triggers = Array.isArray(channels) ? channels : [channels];
|
|
48
|
-
return new
|
|
49
|
-
channels:
|
|
36
|
+
return new PregelNode({
|
|
37
|
+
channels: channelMappingOrArray,
|
|
50
38
|
triggers,
|
|
51
|
-
when,
|
|
52
39
|
tags,
|
|
53
40
|
});
|
|
54
41
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// Handle the case where named arguments are passed as an object
|
|
63
|
-
const additionalArgs = args[0];
|
|
64
|
-
Object.entries(additionalArgs).forEach(([key, value]) => {
|
|
65
|
-
channelPairs.push([key, _coerceWriteValue(value)]);
|
|
42
|
+
static writeTo(channels, kwargs) {
|
|
43
|
+
const channelWriteEntries = [];
|
|
44
|
+
for (const channel of channels) {
|
|
45
|
+
channelWriteEntries.push({
|
|
46
|
+
channel,
|
|
47
|
+
value: PASSTHROUGH,
|
|
48
|
+
skipNone: false,
|
|
66
49
|
});
|
|
67
50
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
51
|
+
for (const [key, value] of Object.entries(kwargs ?? {})) {
|
|
52
|
+
if (Runnable.isRunnable(value) || typeof value === "function") {
|
|
53
|
+
channelWriteEntries.push({
|
|
54
|
+
channel: key,
|
|
55
|
+
value: PASSTHROUGH,
|
|
56
|
+
skipNone: true,
|
|
57
|
+
mapper: _coerceToRunnable(value),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
channelWriteEntries.push({
|
|
62
|
+
channel: key,
|
|
63
|
+
value,
|
|
64
|
+
skipNone: false,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
79
67
|
}
|
|
80
|
-
return new ChannelWrite(
|
|
68
|
+
return new ChannelWrite(channelWriteEntries);
|
|
81
69
|
}
|
|
82
70
|
}
|
|
83
71
|
export class Pregel extends Runnable {
|
|
@@ -93,43 +81,55 @@ export class Pregel extends Runnable {
|
|
|
93
81
|
writable: true,
|
|
94
82
|
value: ["langgraph", "pregel"]
|
|
95
83
|
});
|
|
84
|
+
Object.defineProperty(this, "nodes", {
|
|
85
|
+
enumerable: true,
|
|
86
|
+
configurable: true,
|
|
87
|
+
writable: true,
|
|
88
|
+
value: void 0
|
|
89
|
+
});
|
|
96
90
|
Object.defineProperty(this, "channels", {
|
|
97
91
|
enumerable: true,
|
|
98
92
|
configurable: true,
|
|
99
93
|
writable: true,
|
|
100
|
-
value:
|
|
94
|
+
value: void 0
|
|
101
95
|
});
|
|
102
|
-
Object.defineProperty(this, "
|
|
96
|
+
Object.defineProperty(this, "inputs", {
|
|
103
97
|
enumerable: true,
|
|
104
98
|
configurable: true,
|
|
105
99
|
writable: true,
|
|
106
|
-
value:
|
|
100
|
+
value: void 0
|
|
107
101
|
});
|
|
108
|
-
Object.defineProperty(this, "
|
|
102
|
+
Object.defineProperty(this, "outputs", {
|
|
109
103
|
enumerable: true,
|
|
110
104
|
configurable: true,
|
|
111
105
|
writable: true,
|
|
112
|
-
value:
|
|
106
|
+
value: void 0
|
|
113
107
|
});
|
|
114
|
-
Object.defineProperty(this, "
|
|
108
|
+
Object.defineProperty(this, "autoValidate", {
|
|
115
109
|
enumerable: true,
|
|
116
110
|
configurable: true,
|
|
117
111
|
writable: true,
|
|
118
|
-
value:
|
|
112
|
+
value: true
|
|
119
113
|
});
|
|
120
|
-
Object.defineProperty(this, "
|
|
114
|
+
Object.defineProperty(this, "streamMode", {
|
|
121
115
|
enumerable: true,
|
|
122
116
|
configurable: true,
|
|
123
117
|
writable: true,
|
|
124
|
-
value:
|
|
118
|
+
value: "values"
|
|
125
119
|
});
|
|
126
|
-
Object.defineProperty(this, "
|
|
120
|
+
Object.defineProperty(this, "streamChannels", {
|
|
127
121
|
enumerable: true,
|
|
128
122
|
configurable: true,
|
|
129
123
|
writable: true,
|
|
130
124
|
value: void 0
|
|
131
125
|
});
|
|
132
|
-
Object.defineProperty(this, "
|
|
126
|
+
Object.defineProperty(this, "interruptAfter", {
|
|
127
|
+
enumerable: true,
|
|
128
|
+
configurable: true,
|
|
129
|
+
writable: true,
|
|
130
|
+
value: void 0
|
|
131
|
+
});
|
|
132
|
+
Object.defineProperty(this, "interruptBefore", {
|
|
133
133
|
enumerable: true,
|
|
134
134
|
configurable: true,
|
|
135
135
|
writable: true,
|
|
@@ -141,135 +141,365 @@ export class Pregel extends Runnable {
|
|
|
141
141
|
writable: true,
|
|
142
142
|
value: void 0
|
|
143
143
|
});
|
|
144
|
-
Object.defineProperty(this, "
|
|
144
|
+
Object.defineProperty(this, "debug", {
|
|
145
145
|
enumerable: true,
|
|
146
146
|
configurable: true,
|
|
147
147
|
writable: true,
|
|
148
|
-
value:
|
|
148
|
+
value: false
|
|
149
|
+
});
|
|
150
|
+
Object.defineProperty(this, "checkpointer", {
|
|
151
|
+
enumerable: true,
|
|
152
|
+
configurable: true,
|
|
153
|
+
writable: true,
|
|
154
|
+
value: void 0
|
|
149
155
|
});
|
|
150
156
|
// Initialize global async local storage instance for tracing
|
|
151
157
|
initializeAsyncLocalStorageSingleton();
|
|
152
|
-
this.channels = fields.channels ?? this.channels;
|
|
153
|
-
this.output = fields.output ?? this.output;
|
|
154
|
-
this.input = fields.input ?? this.input;
|
|
155
|
-
this.hidden = fields.hidden ?? this.hidden;
|
|
156
|
-
this.debug = fields.debug ?? this.debug;
|
|
157
158
|
this.nodes = fields.nodes;
|
|
159
|
+
this.channels = fields.channels;
|
|
160
|
+
this.autoValidate = fields.autoValidate ?? this.autoValidate;
|
|
161
|
+
this.streamMode = fields.streamMode ?? this.streamMode;
|
|
162
|
+
this.outputs = fields.outputs;
|
|
163
|
+
this.streamChannels = fields.streamChannels ?? this.streamChannels;
|
|
164
|
+
this.interruptAfter = fields.interruptAfter;
|
|
165
|
+
this.interruptBefore = fields.interruptBefore;
|
|
166
|
+
this.inputs = fields.inputs;
|
|
167
|
+
this.stepTimeout = fields.stepTimeout ?? this.stepTimeout;
|
|
168
|
+
this.debug = fields.debug ?? this.debug;
|
|
158
169
|
this.checkpointer = fields.checkpointer;
|
|
159
|
-
this.stepTimeout = fields.stepTimeout;
|
|
160
|
-
this.interrupt = fields.interrupt ?? this.interrupt;
|
|
161
170
|
// Bind the method to the instance
|
|
162
171
|
this._transform = this._transform.bind(this);
|
|
172
|
+
if (this.autoValidate) {
|
|
173
|
+
this.validate();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
validate() {
|
|
163
177
|
validateGraph({
|
|
164
178
|
nodes: this.nodes,
|
|
165
179
|
channels: this.channels,
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
180
|
+
outputChannels: this.outputs,
|
|
181
|
+
inputChannels: this.inputs,
|
|
182
|
+
streamChannels: this.streamChannels,
|
|
183
|
+
interruptAfterNodes: this.interruptAfter,
|
|
184
|
+
interruptBeforeNodes: this.interruptBefore,
|
|
170
185
|
});
|
|
186
|
+
return this;
|
|
171
187
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
188
|
+
get streamChannelsList() {
|
|
189
|
+
if (Array.isArray(this.streamChannels)) {
|
|
190
|
+
return this.streamChannels;
|
|
191
|
+
}
|
|
192
|
+
else if (this.streamChannels) {
|
|
193
|
+
return [this.streamChannels];
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
return Object.keys(this.channels);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
get streamChannelsAsIs() {
|
|
200
|
+
if (this.streamChannels) {
|
|
201
|
+
return this.streamChannels;
|
|
178
202
|
}
|
|
179
203
|
else {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
204
|
+
return Object.keys(this.channels);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async getState(config) {
|
|
208
|
+
if (!this.checkpointer) {
|
|
209
|
+
throw new GraphValueError("No checkpointer set");
|
|
210
|
+
}
|
|
211
|
+
const saved = await this.checkpointer.getTuple(config);
|
|
212
|
+
const checkpoint = saved ? saved.checkpoint : emptyCheckpoint();
|
|
213
|
+
const channels = emptyChannels(this.channels, checkpoint);
|
|
214
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
215
|
+
const [_, nextTasks] = _prepareNextTasks(checkpoint, this.nodes, channels, false);
|
|
216
|
+
return {
|
|
217
|
+
values: readChannels(channels, this.streamChannelsAsIs),
|
|
218
|
+
next: nextTasks.map((task) => task.name),
|
|
219
|
+
metadata: saved?.metadata,
|
|
220
|
+
config: saved ? saved.config : config,
|
|
221
|
+
parentConfig: saved?.parentConfig,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async *getStateHistory(config, limit, before) {
|
|
225
|
+
if (!this.checkpointer) {
|
|
226
|
+
throw new GraphValueError("No checkpointer set");
|
|
227
|
+
}
|
|
228
|
+
for await (const saved of this.checkpointer.list(config, limit, before)) {
|
|
229
|
+
const channels = emptyChannels(this.channels, saved.checkpoint);
|
|
230
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
231
|
+
const [_, nextTasks] = _prepareNextTasks(saved.checkpoint, this.nodes, channels, false);
|
|
232
|
+
yield {
|
|
233
|
+
values: readChannels(channels, this.streamChannelsAsIs),
|
|
234
|
+
next: nextTasks.map((task) => task.name),
|
|
235
|
+
metadata: saved.metadata,
|
|
236
|
+
config: saved.config,
|
|
237
|
+
parentConfig: saved.parentConfig,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async updateState(config, values, asNode) {
|
|
242
|
+
if (!this.checkpointer) {
|
|
243
|
+
throw new GraphValueError("No checkpointer set");
|
|
244
|
+
}
|
|
245
|
+
// Get the latest checkpoint
|
|
246
|
+
const saved = await this.checkpointer.getTuple(config);
|
|
247
|
+
const checkpoint = saved
|
|
248
|
+
? copyCheckpoint(saved.checkpoint)
|
|
249
|
+
: emptyCheckpoint();
|
|
250
|
+
// Find last that updated the state, if not provided
|
|
251
|
+
const maxSeens = Object.entries(checkpoint.versions_seen).reduce((acc, [node, versions]) => {
|
|
252
|
+
const maxSeen = Math.max(...Object.values(versions));
|
|
253
|
+
if (maxSeen) {
|
|
254
|
+
if (!acc[maxSeen]) {
|
|
255
|
+
acc[maxSeen] = [];
|
|
183
256
|
}
|
|
257
|
+
acc[maxSeen].push(node);
|
|
258
|
+
}
|
|
259
|
+
return acc;
|
|
260
|
+
}, {});
|
|
261
|
+
if (!asNode && !Object.keys(maxSeens).length) {
|
|
262
|
+
if (!Array.isArray(this.inputs) && this.inputs in this.nodes) {
|
|
263
|
+
asNode = this.inputs;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else if (!asNode) {
|
|
267
|
+
const maxSeen = Math.max(...Object.keys(maxSeens).map(Number));
|
|
268
|
+
const nodes = maxSeens[maxSeen];
|
|
269
|
+
if (nodes.length === 1) {
|
|
270
|
+
asNode = nodes[0];
|
|
184
271
|
}
|
|
185
272
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
// get checkpoint, or create an empty one
|
|
189
|
-
let checkpoint;
|
|
190
|
-
if (this.checkpointer) {
|
|
191
|
-
checkpoint = this.checkpointer.get(config);
|
|
273
|
+
if (!asNode) {
|
|
274
|
+
throw new InvalidUpdateError("Ambiguous update, specify as_node");
|
|
192
275
|
}
|
|
193
|
-
|
|
194
|
-
// create channels from checkpoint
|
|
276
|
+
// update channels
|
|
195
277
|
const channels = emptyChannels(this.channels, checkpoint);
|
|
196
|
-
//
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
278
|
+
// create task to run all writers of the chosen node
|
|
279
|
+
const writers = this.nodes[asNode].getWriters();
|
|
280
|
+
if (!writers.length) {
|
|
281
|
+
throw new InvalidUpdateError(`No writers found for node ${asNode}`);
|
|
282
|
+
}
|
|
283
|
+
const task = {
|
|
284
|
+
name: asNode,
|
|
285
|
+
input: values,
|
|
286
|
+
proc:
|
|
287
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
288
|
+
writers.length > 1 ? RunnableSequence.from(writers) : writers[0],
|
|
289
|
+
writes: [],
|
|
290
|
+
config: undefined,
|
|
291
|
+
};
|
|
292
|
+
// execute task
|
|
293
|
+
await task.proc.invoke(task.input, patchConfig(config, {
|
|
294
|
+
runName: `${this.name}UpdateState`,
|
|
295
|
+
configurable: {
|
|
296
|
+
[CONFIG_KEY_SEND]: (items) => task.writes.push(...items),
|
|
297
|
+
[CONFIG_KEY_READ]: _localRead.bind(undefined, checkpoint, channels, task.writes),
|
|
298
|
+
},
|
|
299
|
+
}));
|
|
300
|
+
// apply to checkpoint and save
|
|
301
|
+
_applyWrites(checkpoint, channels, task.writes);
|
|
302
|
+
const step = (saved?.metadata?.step ?? -2) + 1;
|
|
303
|
+
return await this.checkpointer.put(saved?.config ?? config, createCheckpoint(checkpoint, channels, step), {
|
|
304
|
+
source: "update",
|
|
305
|
+
step,
|
|
306
|
+
writes: { [asNode]: values },
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
_defaults(config) {
|
|
310
|
+
const { debug, streamMode, inputKeys, outputKeys, interruptAfter, interruptBefore, ...rest } = config;
|
|
311
|
+
const defaultDebug = debug !== undefined ? debug : this.debug;
|
|
312
|
+
let defaultOutputKeys = outputKeys;
|
|
313
|
+
if (defaultOutputKeys === undefined) {
|
|
314
|
+
defaultOutputKeys = this.streamChannelsAsIs;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
validateKeys(defaultOutputKeys, this.channels);
|
|
318
|
+
}
|
|
319
|
+
let defaultInputKeys = inputKeys;
|
|
320
|
+
if (defaultInputKeys === undefined) {
|
|
321
|
+
defaultInputKeys = this.inputs;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
validateKeys(defaultInputKeys, this.channels);
|
|
325
|
+
}
|
|
326
|
+
const defaultInterruptBefore = interruptBefore ?? this.interruptBefore ?? [];
|
|
327
|
+
const defaultInterruptAfter = interruptAfter ?? this.interruptAfter ?? [];
|
|
328
|
+
let defaultStreamMode;
|
|
329
|
+
if (streamMode !== undefined) {
|
|
330
|
+
defaultStreamMode = streamMode;
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
defaultStreamMode = this.streamMode;
|
|
334
|
+
}
|
|
335
|
+
if (config.configurable !== undefined &&
|
|
336
|
+
config.configurable[CONFIG_KEY_READ] !== undefined) {
|
|
337
|
+
defaultStreamMode = "values";
|
|
204
338
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
339
|
+
return [
|
|
340
|
+
defaultDebug,
|
|
341
|
+
defaultStreamMode,
|
|
342
|
+
defaultInputKeys,
|
|
343
|
+
defaultOutputKeys,
|
|
344
|
+
rest,
|
|
345
|
+
defaultInterruptBefore,
|
|
346
|
+
defaultInterruptAfter,
|
|
347
|
+
];
|
|
348
|
+
}
|
|
349
|
+
async *_transform(input, runManager, config = {}) {
|
|
350
|
+
const bg = [];
|
|
351
|
+
try {
|
|
352
|
+
if (config.recursionLimit && config.recursionLimit < 1) {
|
|
353
|
+
throw new GraphValueError(`Recursion limit must be greater than 0, got ${config.recursionLimit}`);
|
|
218
354
|
}
|
|
219
|
-
|
|
220
|
-
throw new
|
|
355
|
+
if (this.checkpointer && !config.configurable) {
|
|
356
|
+
throw new GraphValueError(`Checkpointer requires one or more of the following 'configurable' keys: thread_id, checkpoint_id`);
|
|
221
357
|
}
|
|
222
|
-
//
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const tasks = tasksWithConfig.map(([proc, input, updatedConfig]) => () => proc.invoke(input, updatedConfig));
|
|
241
|
-
await executeTasks(tasks, this.stepTimeout);
|
|
242
|
-
// apply writes to channels
|
|
243
|
-
_applyWrites(checkpoint, channels, pendingWrites, config, step + 1);
|
|
244
|
-
// yield current value and checkpoint view
|
|
245
|
-
const stepOutput = mapOutput(outputKeys, pendingWrites, channels);
|
|
246
|
-
if (stepOutput) {
|
|
247
|
-
yield stepOutput;
|
|
248
|
-
if (typeof outputKeys !== "string") {
|
|
249
|
-
_applyWritesFromView(checkpoint, channels, stepOutput);
|
|
358
|
+
// assign defaults
|
|
359
|
+
const [debug, streamMode, inputKeys, outputKeys, restConfig, interruptBefore, interruptAfter,] = this._defaults(config);
|
|
360
|
+
// copy nodes to ignore mutations during execution
|
|
361
|
+
const processes = { ...this.nodes };
|
|
362
|
+
// get checkpoint, or create an empty one
|
|
363
|
+
const saved = this.checkpointer
|
|
364
|
+
? await this.checkpointer.getTuple(config)
|
|
365
|
+
: null;
|
|
366
|
+
let checkpoint = saved ? saved.checkpoint : emptyCheckpoint();
|
|
367
|
+
let checkpointConfig = saved ? saved.config : config;
|
|
368
|
+
let start = (saved?.metadata?.step ?? -2) + 1;
|
|
369
|
+
// create channels from checkpoint
|
|
370
|
+
const channels = emptyChannels(this.channels, checkpoint);
|
|
371
|
+
// map inputs to channel updates
|
|
372
|
+
const inputPendingWrites = [];
|
|
373
|
+
for await (const c of input) {
|
|
374
|
+
for (const value of mapInput(inputKeys, c)) {
|
|
375
|
+
inputPendingWrites.push(value);
|
|
250
376
|
}
|
|
251
377
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
checkpoint =
|
|
256
|
-
|
|
378
|
+
if (inputPendingWrites.length) {
|
|
379
|
+
// discard any unfinished tasks from previous checkpoint
|
|
380
|
+
const discarded = _prepareNextTasks(checkpoint, processes, channels, true);
|
|
381
|
+
checkpoint = discarded[0]; // eslint-disable-line prefer-destructuring
|
|
382
|
+
// apply input writes
|
|
383
|
+
_applyWrites(checkpoint, channels, inputPendingWrites);
|
|
384
|
+
// save input checkpoint
|
|
385
|
+
if (this.checkpointer) {
|
|
386
|
+
checkpoint = createCheckpoint(checkpoint, channels, start);
|
|
387
|
+
bg.push(this.checkpointer.put(checkpointConfig, checkpoint, {
|
|
388
|
+
source: "input",
|
|
389
|
+
step: start,
|
|
390
|
+
writes: Object.fromEntries(inputPendingWrites),
|
|
391
|
+
}));
|
|
392
|
+
checkpointConfig = {
|
|
393
|
+
configurable: {
|
|
394
|
+
...checkpointConfig.configurable,
|
|
395
|
+
checkpoint_id: checkpoint.id,
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
// increment start to 0
|
|
400
|
+
start += 1;
|
|
257
401
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
402
|
+
else {
|
|
403
|
+
checkpoint = copyCheckpoint(checkpoint);
|
|
404
|
+
for (const k of this.streamChannelsList) {
|
|
405
|
+
const version = checkpoint.channel_versions[k];
|
|
406
|
+
checkpoint.versions_seen[INTERRUPT][k] = version;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// Similarly to Bulk Synchronous Parallel / Pregel model
|
|
410
|
+
// computation proceeds in steps, while there are channel updates
|
|
411
|
+
// channel updates from step N are only visible in step N+1
|
|
412
|
+
// channels are guaranteed to be immutable for the duration of the step,
|
|
413
|
+
// with channel updates applied only at the transition between steps
|
|
414
|
+
const stop = start + (config.recursionLimit ?? DEFAULT_LOOP_LIMIT);
|
|
415
|
+
for (let step = start; step < stop + 1; step += 1) {
|
|
416
|
+
const [nextCheckpoint, nextTasks] = _prepareNextTasks(checkpoint, processes, channels, true);
|
|
417
|
+
// if no more tasks, we're done
|
|
418
|
+
if (nextTasks.length === 0 && step === start) {
|
|
419
|
+
throw new GraphValueError(`No tasks to run in graph.`);
|
|
420
|
+
}
|
|
421
|
+
else if (nextTasks.length === 0) {
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
else if (step === stop) {
|
|
425
|
+
throw new GraphRecursionError(`Recursion limit of ${config.recursionLimit} reached without hitting a stop condition. You can increase the limit by setting the "recursionLimit" config key.`);
|
|
426
|
+
}
|
|
427
|
+
// before execution, check if we should interrupt
|
|
428
|
+
if (_shouldInterrupt(checkpoint, interruptBefore, this.streamChannelsList, nextTasks)) {
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
checkpoint = nextCheckpoint;
|
|
433
|
+
}
|
|
434
|
+
if (debug) {
|
|
435
|
+
console.log(nextTasks);
|
|
436
|
+
}
|
|
437
|
+
const tasksWithConfig = nextTasks.map(
|
|
438
|
+
// eslint-disable-next-line no-loop-func
|
|
439
|
+
(task) => [
|
|
440
|
+
task.proc,
|
|
441
|
+
task.input,
|
|
442
|
+
patchConfig(restConfig, {
|
|
443
|
+
callbacks: runManager?.getChild(`graph:step:${step}`),
|
|
444
|
+
runName: task.name,
|
|
445
|
+
configurable: {
|
|
446
|
+
...config.configurable,
|
|
447
|
+
[CONFIG_KEY_SEND]: (items) => task.writes.push(...items),
|
|
448
|
+
[CONFIG_KEY_READ]: _localRead.bind(undefined, checkpoint, channels, task.writes),
|
|
449
|
+
},
|
|
450
|
+
}),
|
|
451
|
+
]);
|
|
452
|
+
// execute tasks, and wait for one to fail or all to finish.
|
|
453
|
+
// each task is independent from all other concurrent tasks
|
|
454
|
+
const tasks = tasksWithConfig.map(([proc, input, updatedConfig]) => () => proc.invoke(input, updatedConfig));
|
|
455
|
+
await executeTasks(tasks, this.stepTimeout);
|
|
456
|
+
// combine pending writes from all tasks
|
|
457
|
+
const pendingWrites = [];
|
|
458
|
+
for (const task of nextTasks) {
|
|
459
|
+
pendingWrites.push(...task.writes);
|
|
460
|
+
}
|
|
461
|
+
// apply writes to channels
|
|
462
|
+
_applyWrites(checkpoint, channels, pendingWrites);
|
|
463
|
+
// yield current value and checkpoint view
|
|
464
|
+
if (streamMode === "values") {
|
|
465
|
+
yield* mapOutputValues(outputKeys, pendingWrites, channels);
|
|
466
|
+
}
|
|
467
|
+
else if (streamMode === "updates") {
|
|
468
|
+
yield* mapOutputUpdates(outputKeys, nextTasks);
|
|
469
|
+
}
|
|
470
|
+
// save end of step checkpoint
|
|
471
|
+
if (this.checkpointer) {
|
|
472
|
+
checkpoint = createCheckpoint(checkpoint, channels, step);
|
|
473
|
+
bg.push(this.checkpointer.put(checkpointConfig, checkpoint, {
|
|
474
|
+
source: "loop",
|
|
475
|
+
step,
|
|
476
|
+
writes: single(streamMode === "values"
|
|
477
|
+
? mapOutputValues(outputKeys, pendingWrites, channels)
|
|
478
|
+
: mapOutputUpdates(outputKeys, nextTasks)),
|
|
479
|
+
}));
|
|
480
|
+
checkpointConfig = {
|
|
481
|
+
configurable: {
|
|
482
|
+
...checkpointConfig.configurable,
|
|
483
|
+
checkpoint_id: checkpoint.id,
|
|
484
|
+
},
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
if (_shouldInterrupt(checkpoint, interruptAfter, this.streamChannelsList, nextTasks)) {
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
261
490
|
}
|
|
262
491
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
checkpoint = await createCheckpoint(checkpoint, channels);
|
|
266
|
-
this.checkpointer.put(config, checkpoint);
|
|
492
|
+
finally {
|
|
493
|
+
await Promise.all(bg);
|
|
267
494
|
}
|
|
268
495
|
}
|
|
269
496
|
async invoke(input, options) {
|
|
270
497
|
const config = ensureConfig(options);
|
|
271
498
|
if (!config?.outputKeys) {
|
|
272
|
-
config.outputKeys = this.
|
|
499
|
+
config.outputKeys = this.outputs;
|
|
500
|
+
}
|
|
501
|
+
if (!config?.streamMode) {
|
|
502
|
+
config.streamMode = "values";
|
|
273
503
|
}
|
|
274
504
|
let latest;
|
|
275
505
|
for await (const chunk of await this.stream(input, config)) {
|
|
@@ -315,30 +545,31 @@ async function executeTasks(tasks, stepTimeout) {
|
|
|
315
545
|
}
|
|
316
546
|
}
|
|
317
547
|
}
|
|
318
|
-
function
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
548
|
+
export function _shouldInterrupt(checkpoint, interruptNodes, snapshotChannels, tasks) {
|
|
549
|
+
const seen = checkpoint.versions_seen[INTERRUPT];
|
|
550
|
+
const anySnapshotChannelUpdated = snapshotChannels.some((chan) => checkpoint.channel_versions[chan] > seen?.[chan]);
|
|
551
|
+
const anyTaskNodeInInterruptNodes = tasks.some((task) => interruptNodes === "*"
|
|
552
|
+
? !task.config?.tags?.includes(TAG_HIDDEN)
|
|
553
|
+
: interruptNodes.includes(task.name));
|
|
554
|
+
return anySnapshotChannelUpdated && anyTaskNodeInInterruptNodes;
|
|
555
|
+
}
|
|
556
|
+
export function _localRead(checkpoint, channels, writes, select, fresh = false) {
|
|
557
|
+
if (fresh) {
|
|
558
|
+
const newCheckpoint = createCheckpoint(checkpoint, channels, -1);
|
|
559
|
+
// create a new copy of channels
|
|
560
|
+
const newChannels = emptyChannels(channels, newCheckpoint);
|
|
561
|
+
// Note: _applyWrites contains side effects
|
|
562
|
+
_applyWrites(copyCheckpoint(newCheckpoint), newChannels, writes);
|
|
563
|
+
return readChannels(newChannels, select);
|
|
322
564
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
return null;
|
|
326
|
-
}
|
|
327
|
-
throw e;
|
|
565
|
+
else {
|
|
566
|
+
return readChannels(channels, select);
|
|
328
567
|
}
|
|
329
568
|
}
|
|
330
|
-
function _applyWrites(checkpoint, channels,
|
|
331
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
332
|
-
pendingWrites, config, forStep) {
|
|
333
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
569
|
+
export function _applyWrites(checkpoint, channels, pendingWrites) {
|
|
334
570
|
const pendingWritesByChannel = {};
|
|
335
571
|
// Group writes by channel
|
|
336
572
|
for (const [chan, val] of pendingWrites) {
|
|
337
|
-
for (const c in ReservedChannelsMap) {
|
|
338
|
-
if (chan === c) {
|
|
339
|
-
throw new Error(`Can't write to reserved channel ${chan}`);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
573
|
if (chan in pendingWritesByChannel) {
|
|
343
574
|
pendingWritesByChannel[chan].push(val);
|
|
344
575
|
}
|
|
@@ -346,104 +577,142 @@ pendingWrites, config, forStep) {
|
|
|
346
577
|
pendingWritesByChannel[chan] = [val];
|
|
347
578
|
}
|
|
348
579
|
}
|
|
349
|
-
//
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
580
|
+
// find the highest version of all channels
|
|
581
|
+
let maxVersion = 0;
|
|
582
|
+
if (Object.keys(checkpoint.channel_versions).length > 0) {
|
|
583
|
+
maxVersion = Math.max(...Object.values(checkpoint.channel_versions));
|
|
584
|
+
}
|
|
353
585
|
const updatedChannels = new Set();
|
|
354
586
|
// Apply writes to channels
|
|
355
|
-
for (const chan
|
|
356
|
-
if (chan in
|
|
357
|
-
|
|
358
|
-
|
|
587
|
+
for (const [chan, vals] of Object.entries(pendingWritesByChannel)) {
|
|
588
|
+
if (chan in channels) {
|
|
589
|
+
// side effect: update channels
|
|
590
|
+
try {
|
|
359
591
|
channels[chan].update(vals);
|
|
360
|
-
|
|
361
|
-
checkpoint.channelVersions[chan] = 1;
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
364
|
-
checkpoint.channelVersions[chan] += 1;
|
|
365
|
-
}
|
|
366
|
-
updatedChannels.add(chan);
|
|
592
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
367
593
|
}
|
|
368
|
-
|
|
369
|
-
|
|
594
|
+
catch (e) {
|
|
595
|
+
if (e.name === InvalidUpdateError.name) {
|
|
596
|
+
throw new InvalidUpdateError(`Invalid update for channel ${chan}. Values: ${vals}`);
|
|
597
|
+
}
|
|
370
598
|
}
|
|
599
|
+
// side effect: update checkpoint channel versions
|
|
600
|
+
checkpoint.channel_versions[chan] = maxVersion + 1;
|
|
601
|
+
updatedChannels.add(chan);
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
console.warn(`Skipping write for channel ${chan} which has no readers`);
|
|
371
605
|
}
|
|
372
606
|
}
|
|
373
607
|
// Channels that weren't updated in this step are notified of a new step
|
|
374
608
|
for (const chan in channels) {
|
|
375
609
|
if (!updatedChannels.has(chan)) {
|
|
610
|
+
// side effect: update channels
|
|
376
611
|
channels[chan].update([]);
|
|
377
612
|
}
|
|
378
613
|
}
|
|
379
614
|
}
|
|
380
|
-
function
|
|
381
|
-
|
|
382
|
-
if (val === _readChannel(channels, chan)) {
|
|
383
|
-
continue;
|
|
384
|
-
}
|
|
385
|
-
if (channels[chan].lc_graph_name === "LastValue") {
|
|
386
|
-
throw new Error(`Can't modify channel ${chan} with LastValue`);
|
|
387
|
-
}
|
|
388
|
-
checkpoint.channelVersions[chan] += 1;
|
|
389
|
-
channels[chan].update([values[chan]]);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
function _prepareNextTasks(checkpoint, processes, channels) {
|
|
615
|
+
export function _prepareNextTasks(checkpoint, processes, channels, forExecution) {
|
|
616
|
+
const newCheckpoint = copyCheckpoint(checkpoint);
|
|
393
617
|
const tasks = [];
|
|
618
|
+
const taskDescriptions = [];
|
|
394
619
|
// Check if any processes should be run in next step
|
|
395
620
|
// If so, prepare the values to be passed to them
|
|
396
621
|
for (const [name, proc] of Object.entries(processes)) {
|
|
397
|
-
let seen =
|
|
622
|
+
let seen = newCheckpoint.versions_seen[name];
|
|
398
623
|
if (!seen) {
|
|
399
|
-
|
|
400
|
-
seen =
|
|
624
|
+
newCheckpoint.versions_seen[name] = {};
|
|
625
|
+
seen = newCheckpoint.versions_seen[name];
|
|
401
626
|
}
|
|
402
627
|
// If any of the channels read by this process were updated
|
|
403
|
-
if (proc.triggers
|
|
404
|
-
|
|
628
|
+
if (proc.triggers
|
|
629
|
+
.filter((chan) => {
|
|
405
630
|
try {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
631
|
+
readChannel(channels, chan, false);
|
|
632
|
+
return true;
|
|
633
|
+
}
|
|
634
|
+
catch (e) {
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
})
|
|
638
|
+
.some((chan) => newCheckpoint.channel_versions[chan] > (seen[chan] ?? 0))) {
|
|
639
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
640
|
+
let val;
|
|
641
|
+
// If all trigger channels subscribed by this process are not empty
|
|
642
|
+
// then invoke the process with the values of all non-empty channels
|
|
643
|
+
if (Array.isArray(proc.channels)) {
|
|
644
|
+
let emptyChannels = 0;
|
|
645
|
+
for (const chan of proc.channels) {
|
|
646
|
+
try {
|
|
647
|
+
val = readChannel(channels, chan, false);
|
|
648
|
+
break;
|
|
649
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
650
|
+
}
|
|
651
|
+
catch (e) {
|
|
652
|
+
if (e.name === EmptyChannelError.name) {
|
|
653
|
+
emptyChannels += 1;
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
throw e;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
410
660
|
}
|
|
411
|
-
|
|
661
|
+
if (emptyChannels === proc.channels.length) {
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
else if (typeof proc.channels === "object") {
|
|
666
|
+
val = {};
|
|
667
|
+
try {
|
|
412
668
|
for (const [k, chan] of Object.entries(proc.channels)) {
|
|
413
|
-
val[k] =
|
|
669
|
+
val[k] = readChannel(channels, chan, !proc.triggers.includes(chan));
|
|
414
670
|
}
|
|
671
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
415
672
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
val = val[Object.keys(proc.channels)[0]];
|
|
673
|
+
catch (e) {
|
|
674
|
+
if (e.name === EmptyChannelError.name) {
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
throw e;
|
|
679
|
+
}
|
|
424
680
|
}
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
throw new Error(`Invalid channels type, expected list or dict, got ${proc.channels}`);
|
|
684
|
+
}
|
|
685
|
+
// If the process has a mapper, apply it to the value
|
|
686
|
+
if (proc.mapper !== undefined) {
|
|
687
|
+
val = proc.mapper(val);
|
|
688
|
+
}
|
|
689
|
+
if (forExecution) {
|
|
425
690
|
// Update seen versions
|
|
426
691
|
proc.triggers.forEach((chan) => {
|
|
427
|
-
const version =
|
|
692
|
+
const version = newCheckpoint.channel_versions[chan];
|
|
428
693
|
if (version !== undefined) {
|
|
694
|
+
// side effect: updates newCheckpoint
|
|
429
695
|
seen[chan] = version;
|
|
430
696
|
}
|
|
431
697
|
});
|
|
432
|
-
|
|
433
|
-
if (
|
|
434
|
-
tasks.push(
|
|
698
|
+
const node = proc.getNode();
|
|
699
|
+
if (node !== undefined) {
|
|
700
|
+
tasks.push({
|
|
701
|
+
name,
|
|
702
|
+
input: val,
|
|
703
|
+
proc: node,
|
|
704
|
+
writes: [],
|
|
705
|
+
config: proc.config,
|
|
706
|
+
});
|
|
435
707
|
}
|
|
436
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
437
708
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
throw error;
|
|
444
|
-
}
|
|
709
|
+
else {
|
|
710
|
+
taskDescriptions.push({
|
|
711
|
+
name,
|
|
712
|
+
input: val,
|
|
713
|
+
});
|
|
445
714
|
}
|
|
446
715
|
}
|
|
447
716
|
}
|
|
448
|
-
return tasks;
|
|
717
|
+
return [newCheckpoint, forExecution ? tasks : taskDescriptions];
|
|
449
718
|
}
|