@langchain/langgraph 0.2.7 → 0.2.9
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/dist/constants.cjs +12 -2
- package/dist/constants.d.ts +7 -1
- package/dist/constants.js +11 -1
- package/dist/errors.cjs +5 -4
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +5 -4
- package/dist/graph/graph.cjs +7 -2
- package/dist/graph/graph.js +8 -3
- package/dist/graph/message.cjs +2 -0
- package/dist/graph/message.js +2 -0
- package/dist/graph/state.cjs +9 -7
- package/dist/graph/state.d.ts +2 -2
- package/dist/graph/state.js +10 -8
- package/dist/managed/shared_value.cjs +1 -1
- package/dist/managed/shared_value.js +1 -1
- package/dist/pregel/algo.cjs +119 -54
- package/dist/pregel/algo.d.ts +5 -2
- package/dist/pregel/algo.js +117 -53
- package/dist/pregel/debug.cjs +15 -18
- package/dist/pregel/debug.d.ts +3 -2
- package/dist/pregel/debug.js +16 -19
- package/dist/pregel/index.cjs +295 -123
- package/dist/pregel/index.d.ts +16 -5
- package/dist/pregel/index.js +292 -123
- package/dist/pregel/io.cjs +15 -8
- package/dist/pregel/io.d.ts +2 -2
- package/dist/pregel/io.js +15 -8
- package/dist/pregel/loop.cjs +256 -111
- package/dist/pregel/loop.d.ts +21 -5
- package/dist/pregel/loop.js +256 -109
- package/dist/pregel/read.cjs +9 -2
- package/dist/pregel/read.d.ts +2 -1
- package/dist/pregel/read.js +9 -2
- package/dist/pregel/retry.d.ts +1 -1
- package/dist/pregel/types.d.ts +5 -2
- package/dist/pregel/utils/config.cjs +72 -0
- package/dist/pregel/utils/config.d.ts +2 -0
- package/dist/pregel/utils/config.js +68 -0
- package/dist/pregel/{utils.cjs → utils/index.cjs} +33 -10
- package/dist/pregel/{utils.d.ts → utils/index.d.ts} +4 -7
- package/dist/pregel/{utils.js → utils/index.js} +30 -8
- package/dist/utils.cjs +5 -3
- package/dist/utils.js +5 -3
- package/dist/web.d.ts +2 -1
- package/package.json +1 -1
package/dist/pregel/loop.js
CHANGED
|
@@ -1,17 +1,44 @@
|
|
|
1
|
-
import Deque from "double-ended-queue";
|
|
2
1
|
import { copyCheckpoint, emptyCheckpoint, } from "@langchain/langgraph-checkpoint";
|
|
3
2
|
import { createCheckpoint, emptyChannels, } from "../channels/base.js";
|
|
4
|
-
import { CONFIG_KEY_READ, CONFIG_KEY_RESUMING, ERROR, INPUT, INTERRUPT, } from "../constants.js";
|
|
3
|
+
import { CHECKPOINT_NAMESPACE_SEPARATOR, CONFIG_KEY_CHECKPOINT_MAP, CONFIG_KEY_READ, CONFIG_KEY_RESUMING, CONFIG_KEY_STREAM, ERROR, INPUT, INTERRUPT, TAG_HIDDEN, TASKS, } from "../constants.js";
|
|
5
4
|
import { _applyWrites, _prepareNextTasks, increment, shouldInterrupt, } from "./algo.js";
|
|
6
5
|
import { gatherIterator, gatherIteratorSync, prefixGenerator, } from "../utils.js";
|
|
7
|
-
import { mapInput, mapOutputUpdates, mapOutputValues } from "./io.js";
|
|
8
|
-
import { EmptyInputError, GraphInterrupt } from "../errors.js";
|
|
9
|
-
import { getNewChannelVersions } from "./utils.js";
|
|
6
|
+
import { mapInput, mapOutputUpdates, mapOutputValues, readChannels, } from "./io.js";
|
|
7
|
+
import { EmptyInputError, GraphInterrupt, isGraphInterrupt, } from "../errors.js";
|
|
8
|
+
import { getNewChannelVersions, patchConfigurable } from "./utils/index.js";
|
|
10
9
|
import { mapDebugTasks, mapDebugCheckpoint, mapDebugTaskResults, } from "./debug.js";
|
|
11
10
|
import { AsyncBatchedStore } from "../store/batch.js";
|
|
12
11
|
const INPUT_DONE = Symbol.for("INPUT_DONE");
|
|
13
12
|
const INPUT_RESUMING = Symbol.for("INPUT_RESUMING");
|
|
14
13
|
const DEFAULT_LOOP_LIMIT = 25;
|
|
14
|
+
const SPECIAL_CHANNELS = [ERROR, INTERRUPT];
|
|
15
|
+
export class StreamProtocol {
|
|
16
|
+
constructor(pushFn, modes) {
|
|
17
|
+
Object.defineProperty(this, "push", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: void 0
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(this, "modes", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: void 0
|
|
28
|
+
});
|
|
29
|
+
this.push = pushFn;
|
|
30
|
+
this.modes = modes;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function createDuplexStream(...streams) {
|
|
34
|
+
return new StreamProtocol((value) => {
|
|
35
|
+
for (const stream of streams) {
|
|
36
|
+
if (stream.modes.has(value[1])) {
|
|
37
|
+
stream.push(value);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}, new Set(streams.flatMap((s) => Array.from(s.modes))));
|
|
41
|
+
}
|
|
15
42
|
export class PregelLoop {
|
|
16
43
|
constructor(params) {
|
|
17
44
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -21,6 +48,13 @@ export class PregelLoop {
|
|
|
21
48
|
writable: true,
|
|
22
49
|
value: void 0
|
|
23
50
|
});
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
Object.defineProperty(this, "output", {
|
|
53
|
+
enumerable: true,
|
|
54
|
+
configurable: true,
|
|
55
|
+
writable: true,
|
|
56
|
+
value: void 0
|
|
57
|
+
});
|
|
24
58
|
Object.defineProperty(this, "config", {
|
|
25
59
|
enumerable: true,
|
|
26
60
|
configurable: true,
|
|
@@ -69,6 +103,12 @@ export class PregelLoop {
|
|
|
69
103
|
writable: true,
|
|
70
104
|
value: void 0
|
|
71
105
|
});
|
|
106
|
+
Object.defineProperty(this, "checkpointNamespace", {
|
|
107
|
+
enumerable: true,
|
|
108
|
+
configurable: true,
|
|
109
|
+
writable: true,
|
|
110
|
+
value: void 0
|
|
111
|
+
});
|
|
72
112
|
Object.defineProperty(this, "checkpointPendingWrites", {
|
|
73
113
|
enumerable: true,
|
|
74
114
|
configurable: true,
|
|
@@ -117,6 +157,12 @@ export class PregelLoop {
|
|
|
117
157
|
writable: true,
|
|
118
158
|
value: void 0
|
|
119
159
|
});
|
|
160
|
+
Object.defineProperty(this, "taskWritesLeft", {
|
|
161
|
+
enumerable: true,
|
|
162
|
+
configurable: true,
|
|
163
|
+
writable: true,
|
|
164
|
+
value: 0
|
|
165
|
+
});
|
|
120
166
|
Object.defineProperty(this, "status", {
|
|
121
167
|
enumerable: true,
|
|
122
168
|
configurable: true,
|
|
@@ -128,14 +174,14 @@ export class PregelLoop {
|
|
|
128
174
|
enumerable: true,
|
|
129
175
|
configurable: true,
|
|
130
176
|
writable: true,
|
|
131
|
-
value:
|
|
177
|
+
value: {}
|
|
132
178
|
});
|
|
133
179
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
134
180
|
Object.defineProperty(this, "stream", {
|
|
135
181
|
enumerable: true,
|
|
136
182
|
configurable: true,
|
|
137
183
|
writable: true,
|
|
138
|
-
value:
|
|
184
|
+
value: void 0
|
|
139
185
|
});
|
|
140
186
|
Object.defineProperty(this, "checkpointerPromises", {
|
|
141
187
|
enumerable: true,
|
|
@@ -162,7 +208,6 @@ export class PregelLoop {
|
|
|
162
208
|
value: void 0
|
|
163
209
|
});
|
|
164
210
|
this.input = params.input;
|
|
165
|
-
this.config = params.config;
|
|
166
211
|
this.checkpointer = params.checkpointer;
|
|
167
212
|
// TODO: if managed values no longer needs graph we can replace with
|
|
168
213
|
// managed_specs, channel_specs
|
|
@@ -173,7 +218,6 @@ export class PregelLoop {
|
|
|
173
218
|
this.checkpointerGetNextVersion = increment;
|
|
174
219
|
}
|
|
175
220
|
this.checkpoint = params.checkpoint;
|
|
176
|
-
this.checkpointConfig = params.checkpointConfig;
|
|
177
221
|
this.checkpointMetadata = params.checkpointMetadata;
|
|
178
222
|
this.checkpointPreviousVersions = params.checkpointPreviousVersions;
|
|
179
223
|
this.channels = params.channels;
|
|
@@ -181,29 +225,58 @@ export class PregelLoop {
|
|
|
181
225
|
this.checkpointPendingWrites = params.checkpointPendingWrites;
|
|
182
226
|
this.step = params.step;
|
|
183
227
|
this.stop = params.stop;
|
|
184
|
-
this.
|
|
228
|
+
this.config = params.config;
|
|
229
|
+
this.checkpointConfig = params.checkpointConfig;
|
|
230
|
+
this.isNested = params.isNested;
|
|
185
231
|
this.outputKeys = params.outputKeys;
|
|
186
232
|
this.streamKeys = params.streamKeys;
|
|
187
233
|
this.nodes = params.nodes;
|
|
188
|
-
this.skipDoneTasks =
|
|
234
|
+
this.skipDoneTasks = params.skipDoneTasks;
|
|
189
235
|
this.store = params.store;
|
|
236
|
+
this.stream = params.stream;
|
|
237
|
+
this.checkpointNamespace = params.checkpointNamespace;
|
|
190
238
|
}
|
|
191
239
|
static async initialize(params) {
|
|
192
|
-
|
|
193
|
-
|
|
240
|
+
let { config, stream } = params;
|
|
241
|
+
if (stream !== undefined &&
|
|
242
|
+
config.configurable?.[CONFIG_KEY_STREAM] !== undefined) {
|
|
243
|
+
stream = createDuplexStream(stream, config.configurable[CONFIG_KEY_STREAM]);
|
|
244
|
+
}
|
|
245
|
+
const skipDoneTasks = config.configurable?.checkpoint_id === undefined;
|
|
246
|
+
const isNested = CONFIG_KEY_READ in (config.configurable ?? {});
|
|
247
|
+
if (!isNested &&
|
|
248
|
+
config.configurable?.checkpoint_ns !== undefined &&
|
|
249
|
+
config.configurable?.checkpoint_ns !== "") {
|
|
250
|
+
config = patchConfigurable(config, {
|
|
251
|
+
checkpoint_ns: "",
|
|
252
|
+
checkpoint_id: undefined,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
let checkpointConfig = config;
|
|
256
|
+
if (config.configurable?.[CONFIG_KEY_CHECKPOINT_MAP] !== undefined &&
|
|
257
|
+
config.configurable?.[CONFIG_KEY_CHECKPOINT_MAP]?.[config.configurable?.checkpoint_ns]) {
|
|
258
|
+
checkpointConfig = patchConfigurable(config, {
|
|
259
|
+
checkpoint_id: config.configurable[CONFIG_KEY_CHECKPOINT_MAP][config.configurable?.checkpoint_ns],
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
const checkpointNamespace = config.configurable?.checkpoint_ns?.split(CHECKPOINT_NAMESPACE_SEPARATOR) ?? [];
|
|
263
|
+
const saved = (await params.checkpointer?.getTuple(checkpointConfig)) ?? {
|
|
264
|
+
config,
|
|
194
265
|
checkpoint: emptyCheckpoint(),
|
|
195
266
|
metadata: {
|
|
196
267
|
source: "input",
|
|
197
268
|
step: -2,
|
|
198
269
|
writes: null,
|
|
270
|
+
parents: {},
|
|
199
271
|
},
|
|
200
272
|
pendingWrites: [],
|
|
201
273
|
};
|
|
202
|
-
|
|
203
|
-
...
|
|
274
|
+
checkpointConfig = {
|
|
275
|
+
...config,
|
|
204
276
|
...saved.config,
|
|
205
277
|
configurable: {
|
|
206
|
-
|
|
278
|
+
checkpoint_ns: "",
|
|
279
|
+
...config.configurable,
|
|
207
280
|
...saved.config.configurable,
|
|
208
281
|
},
|
|
209
282
|
};
|
|
@@ -212,7 +285,7 @@ export class PregelLoop {
|
|
|
212
285
|
const checkpointPendingWrites = saved.pendingWrites ?? [];
|
|
213
286
|
const channels = emptyChannels(params.channelSpecs, checkpoint);
|
|
214
287
|
const step = (checkpointMetadata.step ?? 0) + 1;
|
|
215
|
-
const stop = step + (
|
|
288
|
+
const stop = step + (config.recursionLimit ?? DEFAULT_LOOP_LIMIT) + 1;
|
|
216
289
|
const checkpointPreviousVersions = { ...checkpoint.channel_versions };
|
|
217
290
|
const store = params.store
|
|
218
291
|
? new AsyncBatchedStore(params.store)
|
|
@@ -223,13 +296,16 @@ export class PregelLoop {
|
|
|
223
296
|
}
|
|
224
297
|
return new PregelLoop({
|
|
225
298
|
input: params.input,
|
|
226
|
-
config
|
|
299
|
+
config,
|
|
227
300
|
checkpointer: params.checkpointer,
|
|
228
301
|
checkpoint,
|
|
229
302
|
checkpointMetadata,
|
|
230
303
|
checkpointConfig,
|
|
304
|
+
checkpointNamespace,
|
|
231
305
|
channels,
|
|
232
306
|
managed: params.managed,
|
|
307
|
+
isNested,
|
|
308
|
+
skipDoneTasks,
|
|
233
309
|
step,
|
|
234
310
|
stop,
|
|
235
311
|
checkpointPreviousVersions,
|
|
@@ -237,6 +313,7 @@ export class PregelLoop {
|
|
|
237
313
|
outputKeys: params.outputKeys ?? [],
|
|
238
314
|
streamKeys: params.streamKeys ?? [],
|
|
239
315
|
nodes: params.nodes,
|
|
316
|
+
stream,
|
|
240
317
|
store,
|
|
241
318
|
});
|
|
242
319
|
}
|
|
@@ -259,6 +336,22 @@ export class PregelLoop {
|
|
|
259
336
|
* @param writes
|
|
260
337
|
*/
|
|
261
338
|
putWrites(taskId, writes) {
|
|
339
|
+
if (writes.length === 0) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
// adjust taskWritesLeft
|
|
343
|
+
const firstChannel = writes[0][0];
|
|
344
|
+
const anyChannelIsSend = writes.find(([channel]) => channel === TASKS);
|
|
345
|
+
const alwaysSave = anyChannelIsSend || SPECIAL_CHANNELS.includes(firstChannel);
|
|
346
|
+
if (!alwaysSave && !this.taskWritesLeft) {
|
|
347
|
+
return this._outputWrites(taskId, writes);
|
|
348
|
+
}
|
|
349
|
+
else if (firstChannel !== INTERRUPT) {
|
|
350
|
+
// INTERRUPT makes us want to save the last task's writes
|
|
351
|
+
// so we don't decrement tasksWritesLeft in that case
|
|
352
|
+
this.taskWritesLeft -= 1;
|
|
353
|
+
}
|
|
354
|
+
// save writes
|
|
262
355
|
const pendingWrites = writes.map(([key, value]) => {
|
|
263
356
|
return [taskId, key, value];
|
|
264
357
|
});
|
|
@@ -274,10 +367,23 @@ export class PregelLoop {
|
|
|
274
367
|
if (putWritePromise !== undefined) {
|
|
275
368
|
this.checkpointerPromises.push(putWritePromise);
|
|
276
369
|
}
|
|
277
|
-
|
|
370
|
+
this._outputWrites(taskId, writes);
|
|
371
|
+
}
|
|
372
|
+
_outputWrites(taskId, writes, cached = false) {
|
|
373
|
+
const task = this.tasks[taskId];
|
|
278
374
|
if (task !== undefined) {
|
|
279
|
-
|
|
280
|
-
|
|
375
|
+
if (task.config !== undefined &&
|
|
376
|
+
(task.config.tags ?? []).includes(TAG_HIDDEN)) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (writes.length > 0 &&
|
|
380
|
+
writes[0][0] !== ERROR &&
|
|
381
|
+
writes[0][0] !== INTERRUPT) {
|
|
382
|
+
this._emit(gatherIteratorSync(prefixGenerator(mapOutputUpdates(this.outputKeys, [[task, writes]], cached), "updates")));
|
|
383
|
+
}
|
|
384
|
+
if (!cached) {
|
|
385
|
+
this._emit(gatherIteratorSync(prefixGenerator(mapDebugTaskResults(this.step, [[task, writes]], this.streamKeys), "debug")));
|
|
386
|
+
}
|
|
281
387
|
}
|
|
282
388
|
}
|
|
283
389
|
/**
|
|
@@ -286,35 +392,98 @@ export class PregelLoop {
|
|
|
286
392
|
* @param params
|
|
287
393
|
*/
|
|
288
394
|
async tick(params) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (this.status !== "pending") {
|
|
294
|
-
throw new Error(`Cannot tick when status is no longer "pending". Current status: "${this.status}"`);
|
|
295
|
-
}
|
|
296
|
-
if (![INPUT_DONE, INPUT_RESUMING].includes(this.input)) {
|
|
297
|
-
await this._first(inputKeys);
|
|
298
|
-
}
|
|
299
|
-
else if (this.tasks.every((task) => task.writes.length > 0)) {
|
|
300
|
-
const writes = this.tasks.flatMap((t) => t.writes);
|
|
301
|
-
// All tasks have finished
|
|
302
|
-
const myWrites = _applyWrites(this.checkpoint, this.channels, this.tasks, this.checkpointerGetNextVersion);
|
|
303
|
-
for (const [key, values] of Object.entries(myWrites)) {
|
|
304
|
-
await this.updateManagedValues(key, values);
|
|
395
|
+
let tickError;
|
|
396
|
+
try {
|
|
397
|
+
if (this.store && !this.store.isRunning) {
|
|
398
|
+
this.store?.start();
|
|
305
399
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
this.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
400
|
+
const { inputKeys = [], interruptAfter = [], interruptBefore = [], manager, } = params;
|
|
401
|
+
if (this.status !== "pending") {
|
|
402
|
+
throw new Error(`Cannot tick when status is no longer "pending". Current status: "${this.status}"`);
|
|
403
|
+
}
|
|
404
|
+
if (![INPUT_DONE, INPUT_RESUMING].includes(this.input)) {
|
|
405
|
+
await this._first(inputKeys);
|
|
406
|
+
}
|
|
407
|
+
else if (Object.values(this.tasks).every((task) => task.writes.length > 0)) {
|
|
408
|
+
const writes = Object.values(this.tasks).flatMap((t) => t.writes);
|
|
409
|
+
// All tasks have finished
|
|
410
|
+
const managedValueWrites = _applyWrites(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion);
|
|
411
|
+
for (const [key, values] of Object.entries(managedValueWrites)) {
|
|
412
|
+
await this.updateManagedValues(key, values);
|
|
413
|
+
}
|
|
414
|
+
// produce values output
|
|
415
|
+
const valuesOutput = await gatherIterator(prefixGenerator(mapOutputValues(this.outputKeys, writes, this.channels), "values"));
|
|
416
|
+
this._emit(valuesOutput);
|
|
417
|
+
// clear pending writes
|
|
418
|
+
this.checkpointPendingWrites = [];
|
|
419
|
+
await this._putCheckpoint({
|
|
420
|
+
source: "loop",
|
|
421
|
+
writes: mapOutputUpdates(this.outputKeys, Object.values(this.tasks).map((task) => [task, task.writes])).next().value ?? null,
|
|
422
|
+
});
|
|
423
|
+
// after execution, check if we should interrupt
|
|
424
|
+
if (shouldInterrupt(this.checkpoint, interruptAfter, Object.values(this.tasks))) {
|
|
425
|
+
this.status = "interrupt_after";
|
|
426
|
+
if (this.isNested) {
|
|
427
|
+
throw new GraphInterrupt();
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
if (this.step > this.stop) {
|
|
438
|
+
this.status = "out_of_steps";
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
const nextTasks = _prepareNextTasks(this.checkpoint, this.nodes, this.channels, this.managed, this.config, true, {
|
|
442
|
+
step: this.step,
|
|
443
|
+
checkpointer: this.checkpointer,
|
|
444
|
+
isResuming: this.input === INPUT_RESUMING,
|
|
445
|
+
manager,
|
|
314
446
|
});
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
447
|
+
this.tasks = nextTasks;
|
|
448
|
+
this.taskWritesLeft = Object.values(this.tasks).length - 1;
|
|
449
|
+
// Produce debug output
|
|
450
|
+
if (this.checkpointer) {
|
|
451
|
+
this._emit(await gatherIterator(prefixGenerator(mapDebugCheckpoint(this.step - 1, // printing checkpoint for previous step
|
|
452
|
+
this.checkpointConfig, this.channels, this.streamKeys, this.checkpointMetadata, Object.values(this.tasks), this.checkpointPendingWrites), "debug")));
|
|
453
|
+
}
|
|
454
|
+
if (Object.values(this.tasks).length === 0) {
|
|
455
|
+
this.status = "done";
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
// if there are pending writes from a previous loop, apply them
|
|
459
|
+
if (this.skipDoneTasks && this.checkpointPendingWrites.length > 0) {
|
|
460
|
+
for (const [tid, k, v] of this.checkpointPendingWrites) {
|
|
461
|
+
if (k === ERROR || k === INTERRUPT) {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
const task = Object.values(this.tasks).find((t) => t.id === tid);
|
|
465
|
+
if (task) {
|
|
466
|
+
task.writes.push([k, v]);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
for (const task of Object.values(this.tasks)) {
|
|
470
|
+
if (task.writes.length > 0) {
|
|
471
|
+
this._outputWrites(task.id, task.writes, true);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// if all tasks have finished, re-tick
|
|
476
|
+
if (Object.values(this.tasks).every((task) => task.writes.length > 0)) {
|
|
477
|
+
return this.tick({
|
|
478
|
+
inputKeys,
|
|
479
|
+
interruptAfter,
|
|
480
|
+
interruptBefore,
|
|
481
|
+
manager,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
// Before execution, check if we should interrupt
|
|
485
|
+
if (shouldInterrupt(this.checkpoint, interruptBefore, Object.values(this.tasks))) {
|
|
486
|
+
this.status = "interrupt_before";
|
|
318
487
|
if (this.isNested) {
|
|
319
488
|
throw new GraphInterrupt();
|
|
320
489
|
}
|
|
@@ -322,65 +491,29 @@ export class PregelLoop {
|
|
|
322
491
|
return false;
|
|
323
492
|
}
|
|
324
493
|
}
|
|
494
|
+
// Produce debug output
|
|
495
|
+
const debugOutput = await gatherIterator(prefixGenerator(mapDebugTasks(this.step, Object.values(this.tasks)), "debug"));
|
|
496
|
+
this._emit(debugOutput);
|
|
497
|
+
return true;
|
|
325
498
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
this.status = "out_of_steps";
|
|
331
|
-
return false;
|
|
332
|
-
}
|
|
333
|
-
const nextTasks = _prepareNextTasks(this.checkpoint, this.nodes, this.channels, this.managed, this.config, true, {
|
|
334
|
-
step: this.step,
|
|
335
|
-
checkpointer: this.checkpointer,
|
|
336
|
-
isResuming: this.input === INPUT_RESUMING,
|
|
337
|
-
manager,
|
|
338
|
-
});
|
|
339
|
-
this.tasks = nextTasks;
|
|
340
|
-
// Produce debug output
|
|
341
|
-
if (this.checkpointer) {
|
|
342
|
-
this.stream.push(...(await gatherIterator(prefixGenerator(mapDebugCheckpoint(this.step - 1, // printing checkpoint for previous step
|
|
343
|
-
this.checkpointConfig, this.channels, this.streamKeys, this.checkpointMetadata, this.tasks, this.checkpointPendingWrites), "debug"))));
|
|
344
|
-
}
|
|
345
|
-
if (this.tasks.length === 0) {
|
|
346
|
-
this.status = "done";
|
|
347
|
-
return false;
|
|
348
|
-
}
|
|
349
|
-
// if there are pending writes from a previous loop, apply them
|
|
350
|
-
if (this.checkpointPendingWrites.length > 0 && this.skipDoneTasks) {
|
|
351
|
-
for (const [tid, k, v] of this.checkpointPendingWrites) {
|
|
352
|
-
if (k === ERROR || k === INTERRUPT) {
|
|
353
|
-
continue;
|
|
354
|
-
}
|
|
355
|
-
const task = this.tasks.find((t) => t.id === tid);
|
|
356
|
-
if (task) {
|
|
357
|
-
task.writes.push([k, v]);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
// if all tasks have finished, re-tick
|
|
362
|
-
if (this.tasks.every((task) => task.writes.length > 0)) {
|
|
363
|
-
return this.tick({
|
|
364
|
-
inputKeys,
|
|
365
|
-
interruptAfter,
|
|
366
|
-
interruptBefore,
|
|
367
|
-
manager,
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
// Before execution, check if we should interrupt
|
|
371
|
-
if (shouldInterrupt(this.checkpoint, interruptBefore, this.tasks)) {
|
|
372
|
-
this.status = "interrupt_before";
|
|
373
|
-
if (this.isNested) {
|
|
374
|
-
throw new GraphInterrupt();
|
|
499
|
+
catch (e) {
|
|
500
|
+
tickError = e;
|
|
501
|
+
if (!this._suppressInterrupt(tickError)) {
|
|
502
|
+
throw tickError;
|
|
375
503
|
}
|
|
376
504
|
else {
|
|
377
|
-
|
|
505
|
+
this.output = readChannels(this.channels, this.outputKeys);
|
|
506
|
+
}
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
finally {
|
|
510
|
+
if (tickError === undefined) {
|
|
511
|
+
this.output = readChannels(this.channels, this.outputKeys);
|
|
378
512
|
}
|
|
379
513
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
this.
|
|
383
|
-
return true;
|
|
514
|
+
}
|
|
515
|
+
_suppressInterrupt(e) {
|
|
516
|
+
return isGraphInterrupt(e) && !this.isNested;
|
|
384
517
|
}
|
|
385
518
|
/**
|
|
386
519
|
* Resuming from previous checkpoint requires
|
|
@@ -388,9 +521,9 @@ export class PregelLoop {
|
|
|
388
521
|
* - receiving None input (outer graph) or RESUMING flag (subgraph)
|
|
389
522
|
*/
|
|
390
523
|
async _first(inputKeys) {
|
|
391
|
-
const isResuming =
|
|
392
|
-
this.config.configurable?.[CONFIG_KEY_RESUMING] !== undefined
|
|
393
|
-
|
|
524
|
+
const isResuming = Object.keys(this.checkpoint.channel_versions).length !== 0 &&
|
|
525
|
+
(this.config.configurable?.[CONFIG_KEY_RESUMING] !== undefined ||
|
|
526
|
+
this.input === null);
|
|
394
527
|
if (isResuming) {
|
|
395
528
|
for (const channelName of Object.keys(this.channels)) {
|
|
396
529
|
if (this.checkpoint.channel_versions[channelName] !== undefined) {
|
|
@@ -401,6 +534,9 @@ export class PregelLoop {
|
|
|
401
534
|
};
|
|
402
535
|
}
|
|
403
536
|
}
|
|
537
|
+
// produce values output
|
|
538
|
+
const valuesOutput = await gatherIterator(prefixGenerator(mapOutputValues(this.outputKeys, true, this.channels), "values"));
|
|
539
|
+
this._emit(valuesOutput);
|
|
404
540
|
// map inputs to channel updates
|
|
405
541
|
}
|
|
406
542
|
else {
|
|
@@ -409,7 +545,7 @@ export class PregelLoop {
|
|
|
409
545
|
throw new EmptyInputError(`Received no input writes for ${JSON.stringify(inputKeys, null, 2)}`);
|
|
410
546
|
}
|
|
411
547
|
const discardTasks = _prepareNextTasks(this.checkpoint, this.nodes, this.channels, this.managed, this.config, true, { step: this.step });
|
|
412
|
-
_applyWrites(this.checkpoint, this.channels, discardTasks.concat([
|
|
548
|
+
_applyWrites(this.checkpoint, this.channels, Object.values(discardTasks).concat([
|
|
413
549
|
{
|
|
414
550
|
name: INPUT,
|
|
415
551
|
writes: inputWrites,
|
|
@@ -424,12 +560,25 @@ export class PregelLoop {
|
|
|
424
560
|
}
|
|
425
561
|
// done with input
|
|
426
562
|
this.input = isResuming ? INPUT_RESUMING : INPUT_DONE;
|
|
563
|
+
if (!this.isNested) {
|
|
564
|
+
this.config = patchConfigurable(this.config, {
|
|
565
|
+
[CONFIG_KEY_RESUMING]: isResuming,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
_emit(values) {
|
|
570
|
+
for (const chunk of values) {
|
|
571
|
+
if (this.stream.modes.has(chunk[0])) {
|
|
572
|
+
this.stream.push([this.checkpointNamespace, ...chunk]);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
427
575
|
}
|
|
428
576
|
async _putCheckpoint(inputMetadata) {
|
|
429
577
|
// Assign step
|
|
430
578
|
const metadata = {
|
|
431
579
|
...inputMetadata,
|
|
432
580
|
step: this.step,
|
|
581
|
+
parents: this.config.configurable?.[CONFIG_KEY_CHECKPOINT_MAP] ?? {},
|
|
433
582
|
};
|
|
434
583
|
// Bail if no checkpointer
|
|
435
584
|
if (this.checkpointer !== undefined) {
|
|
@@ -439,9 +588,7 @@ export class PregelLoop {
|
|
|
439
588
|
// this is achieved by writing child checkpoints as progress is made
|
|
440
589
|
// (so that error recovery / resuming from interrupt don't lose work)
|
|
441
590
|
// but doing so always with an id equal to that of the parent checkpoint
|
|
442
|
-
this.checkpoint = createCheckpoint(this.checkpoint, this.channels, this.step
|
|
443
|
-
// id: this.isNested ? this.config.configurable?.checkpoint_id : undefined,
|
|
444
|
-
);
|
|
591
|
+
this.checkpoint = createCheckpoint(this.checkpoint, this.channels, this.step);
|
|
445
592
|
this.checkpointConfig = {
|
|
446
593
|
...this.checkpointConfig,
|
|
447
594
|
configurable: {
|
package/dist/pregel/read.cjs
CHANGED
|
@@ -63,10 +63,10 @@ const defaultRunnableBound =
|
|
|
63
63
|
/* #__PURE__ */ new runnables_1.RunnablePassthrough();
|
|
64
64
|
class PregelNode extends runnables_1.RunnableBinding {
|
|
65
65
|
constructor(fields) {
|
|
66
|
-
const { channels, triggers, mapper, writers, bound, kwargs, metadata, retryPolicy, } = fields;
|
|
66
|
+
const { channels, triggers, mapper, writers, bound, kwargs, metadata, retryPolicy, tags, } = fields;
|
|
67
67
|
const mergedTags = [
|
|
68
68
|
...(fields.config?.tags ? fields.config.tags : []),
|
|
69
|
-
...(
|
|
69
|
+
...(tags ?? []),
|
|
70
70
|
];
|
|
71
71
|
super({
|
|
72
72
|
...fields,
|
|
@@ -127,6 +127,12 @@ class PregelNode extends runnables_1.RunnableBinding {
|
|
|
127
127
|
writable: true,
|
|
128
128
|
value: {}
|
|
129
129
|
});
|
|
130
|
+
Object.defineProperty(this, "tags", {
|
|
131
|
+
enumerable: true,
|
|
132
|
+
configurable: true,
|
|
133
|
+
writable: true,
|
|
134
|
+
value: []
|
|
135
|
+
});
|
|
130
136
|
Object.defineProperty(this, "retryPolicy", {
|
|
131
137
|
enumerable: true,
|
|
132
138
|
configurable: true,
|
|
@@ -140,6 +146,7 @@ class PregelNode extends runnables_1.RunnableBinding {
|
|
|
140
146
|
this.bound = bound ?? this.bound;
|
|
141
147
|
this.kwargs = kwargs ?? this.kwargs;
|
|
142
148
|
this.metadata = metadata ?? this.metadata;
|
|
149
|
+
this.tags = mergedTags;
|
|
143
150
|
this.retryPolicy = retryPolicy;
|
|
144
151
|
}
|
|
145
152
|
getWriters() {
|
package/dist/pregel/read.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Runnable, RunnableBinding, RunnableBindingArgs, RunnableConfig, RunnableLike } from "@langchain/core/runnables";
|
|
2
2
|
import { RunnableCallable } from "../utils.js";
|
|
3
|
-
import type { RetryPolicy } from "./utils.js";
|
|
3
|
+
import type { RetryPolicy } from "./utils/index.js";
|
|
4
4
|
export declare class ChannelRead<RunInput = any> extends RunnableCallable {
|
|
5
5
|
lc_graph_name: string;
|
|
6
6
|
channel: string | Array<string>;
|
|
@@ -32,6 +32,7 @@ export declare class PregelNode<RunInput = PregelNodeInputType, RunOutput = Preg
|
|
|
32
32
|
bound: Runnable<RunInput, RunOutput>;
|
|
33
33
|
kwargs: Record<string, any>;
|
|
34
34
|
metadata: Record<string, unknown>;
|
|
35
|
+
tags: string[];
|
|
35
36
|
retryPolicy?: RetryPolicy;
|
|
36
37
|
constructor(fields: PregelNodeArgs<RunInput, RunOutput>);
|
|
37
38
|
getWriters(): Array<Runnable>;
|
package/dist/pregel/read.js
CHANGED
|
@@ -59,10 +59,10 @@ const defaultRunnableBound =
|
|
|
59
59
|
/* #__PURE__ */ new RunnablePassthrough();
|
|
60
60
|
export class PregelNode extends RunnableBinding {
|
|
61
61
|
constructor(fields) {
|
|
62
|
-
const { channels, triggers, mapper, writers, bound, kwargs, metadata, retryPolicy, } = fields;
|
|
62
|
+
const { channels, triggers, mapper, writers, bound, kwargs, metadata, retryPolicy, tags, } = fields;
|
|
63
63
|
const mergedTags = [
|
|
64
64
|
...(fields.config?.tags ? fields.config.tags : []),
|
|
65
|
-
...(
|
|
65
|
+
...(tags ?? []),
|
|
66
66
|
];
|
|
67
67
|
super({
|
|
68
68
|
...fields,
|
|
@@ -123,6 +123,12 @@ export class PregelNode extends RunnableBinding {
|
|
|
123
123
|
writable: true,
|
|
124
124
|
value: {}
|
|
125
125
|
});
|
|
126
|
+
Object.defineProperty(this, "tags", {
|
|
127
|
+
enumerable: true,
|
|
128
|
+
configurable: true,
|
|
129
|
+
writable: true,
|
|
130
|
+
value: []
|
|
131
|
+
});
|
|
126
132
|
Object.defineProperty(this, "retryPolicy", {
|
|
127
133
|
enumerable: true,
|
|
128
134
|
configurable: true,
|
|
@@ -136,6 +142,7 @@ export class PregelNode extends RunnableBinding {
|
|
|
136
142
|
this.bound = bound ?? this.bound;
|
|
137
143
|
this.kwargs = kwargs ?? this.kwargs;
|
|
138
144
|
this.metadata = metadata ?? this.metadata;
|
|
145
|
+
this.tags = mergedTags;
|
|
139
146
|
this.retryPolicy = retryPolicy;
|
|
140
147
|
}
|
|
141
148
|
getWriters() {
|
package/dist/pregel/retry.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PregelExecutableTask } from "./types.js";
|
|
2
|
-
import type { RetryPolicy } from "./utils.js";
|
|
2
|
+
import type { RetryPolicy } from "./utils/index.js";
|
|
3
3
|
export declare const DEFAULT_INITIAL_INTERVAL = 500;
|
|
4
4
|
export declare const DEFAULT_BACKOFF_FACTOR = 2;
|
|
5
5
|
export declare const DEFAULT_MAX_INTERVAL = 128000;
|