@langchain/langgraph 0.2.74 → 0.3.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/dist/channels/any_value.cjs +3 -0
- package/dist/channels/any_value.d.ts +1 -0
- package/dist/channels/any_value.js +3 -0
- package/dist/channels/any_value.js.map +1 -1
- package/dist/channels/base.cjs +31 -2
- package/dist/channels/base.d.ts +17 -2
- package/dist/channels/base.js +31 -2
- package/dist/channels/base.js.map +1 -1
- package/dist/channels/binop.cjs +3 -0
- package/dist/channels/binop.d.ts +1 -0
- package/dist/channels/binop.js +3 -0
- package/dist/channels/binop.js.map +1 -1
- package/dist/channels/dynamic_barrier_value.cjs +119 -9
- package/dist/channels/dynamic_barrier_value.d.ts +29 -0
- package/dist/channels/dynamic_barrier_value.js +117 -8
- package/dist/channels/dynamic_barrier_value.js.map +1 -1
- package/dist/channels/ephemeral_value.cjs +3 -0
- package/dist/channels/ephemeral_value.d.ts +1 -0
- package/dist/channels/ephemeral_value.js +3 -0
- package/dist/channels/ephemeral_value.js.map +1 -1
- package/dist/channels/last_value.cjs +82 -4
- package/dist/channels/last_value.d.ts +21 -0
- package/dist/channels/last_value.js +80 -3
- package/dist/channels/last_value.js.map +1 -1
- package/dist/channels/named_barrier_value.cjs +94 -1
- package/dist/channels/named_barrier_value.d.ts +23 -0
- package/dist/channels/named_barrier_value.js +92 -0
- package/dist/channels/named_barrier_value.js.map +1 -1
- package/dist/channels/topic.cjs +3 -0
- package/dist/channels/topic.d.ts +1 -0
- package/dist/channels/topic.js +3 -0
- package/dist/channels/topic.js.map +1 -1
- package/dist/constants.cjs +29 -8
- package/dist/constants.d.ts +47 -26
- package/dist/constants.js +27 -7
- package/dist/constants.js.map +1 -1
- package/dist/func/index.cjs +17 -4
- package/dist/func/index.d.ts +14 -5
- package/dist/func/index.js +17 -4
- package/dist/func/index.js.map +1 -1
- package/dist/func/types.d.ts +1 -1
- package/dist/graph/graph.d.ts +4 -2
- package/dist/graph/graph.js.map +1 -1
- package/dist/graph/index.cjs +2 -1
- package/dist/graph/index.d.ts +1 -1
- package/dist/graph/index.js +1 -1
- package/dist/graph/index.js.map +1 -1
- package/dist/graph/messages_annotation.cjs +3 -0
- package/dist/graph/messages_annotation.js +3 -0
- package/dist/graph/messages_annotation.js.map +1 -1
- package/dist/graph/state.cjs +67 -22
- package/dist/graph/state.d.ts +22 -8
- package/dist/graph/state.js +68 -24
- package/dist/graph/state.js.map +1 -1
- package/dist/graph/zod/schema.cjs +12 -61
- package/dist/graph/zod/schema.js +12 -61
- package/dist/graph/zod/schema.js.map +1 -1
- package/dist/graph/zod/state.cjs +63 -0
- package/dist/graph/zod/state.d.ts +10 -1
- package/dist/graph/zod/state.js +61 -0
- package/dist/graph/zod/state.js.map +1 -1
- package/dist/prebuilt/react_agent_executor.cjs +99 -45
- package/dist/prebuilt/react_agent_executor.d.ts +20 -4
- package/dist/prebuilt/react_agent_executor.js +99 -45
- package/dist/prebuilt/react_agent_executor.js.map +1 -1
- package/dist/pregel/algo.cjs +60 -20
- package/dist/pregel/algo.d.ts +1 -1
- package/dist/pregel/algo.js +61 -21
- package/dist/pregel/algo.js.map +1 -1
- package/dist/pregel/call.cjs +2 -1
- package/dist/pregel/call.d.ts +3 -2
- package/dist/pregel/call.js +2 -1
- package/dist/pregel/call.js.map +1 -1
- package/dist/pregel/debug.test.cjs +6 -0
- package/dist/pregel/debug.test.js +6 -0
- package/dist/pregel/debug.test.js.map +1 -1
- package/dist/pregel/index.cjs +99 -29
- package/dist/pregel/index.d.ts +19 -6
- package/dist/pregel/index.js +100 -30
- package/dist/pregel/index.js.map +1 -1
- package/dist/pregel/loop.cjs +126 -26
- package/dist/pregel/loop.d.ts +29 -2
- package/dist/pregel/loop.js +127 -27
- package/dist/pregel/loop.js.map +1 -1
- package/dist/pregel/read.cjs +12 -1
- package/dist/pregel/read.d.ts +3 -1
- package/dist/pregel/read.js +12 -1
- package/dist/pregel/read.js.map +1 -1
- package/dist/pregel/retry.cjs +2 -6
- package/dist/pregel/retry.js +2 -6
- package/dist/pregel/retry.js.map +1 -1
- package/dist/pregel/runner.cjs +1 -0
- package/dist/pregel/runner.js +1 -0
- package/dist/pregel/runner.js.map +1 -1
- package/dist/pregel/types.cjs +8 -1
- package/dist/pregel/types.d.ts +64 -8
- package/dist/pregel/types.js +8 -1
- package/dist/pregel/types.js.map +1 -1
- package/dist/pregel/utils/index.d.ts +15 -0
- package/dist/pregel/utils/index.js.map +1 -1
- package/dist/web.cjs +4 -1
- package/dist/web.d.ts +3 -3
- package/dist/web.js +2 -2
- package/dist/web.js.map +1 -1
- package/package.json +5 -5
package/dist/pregel/loop.cjs
CHANGED
|
@@ -26,6 +26,44 @@ function createDuplexStream(...streams) {
|
|
|
26
26
|
modes: new Set(streams.flatMap((s) => Array.from(s.modes))),
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
|
+
class AsyncBatchedCache extends langgraph_checkpoint_1.BaseCache {
|
|
30
|
+
constructor(cache) {
|
|
31
|
+
super();
|
|
32
|
+
Object.defineProperty(this, "cache", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value: void 0
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(this, "queue", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: Promise.resolve()
|
|
43
|
+
});
|
|
44
|
+
this.cache = cache;
|
|
45
|
+
}
|
|
46
|
+
async get(keys) {
|
|
47
|
+
return this.enqueueOperation("get", keys);
|
|
48
|
+
}
|
|
49
|
+
async set(pairs) {
|
|
50
|
+
return this.enqueueOperation("set", pairs);
|
|
51
|
+
}
|
|
52
|
+
async clear(namespaces) {
|
|
53
|
+
return this.enqueueOperation("clear", namespaces);
|
|
54
|
+
}
|
|
55
|
+
async stop() {
|
|
56
|
+
await this.queue;
|
|
57
|
+
}
|
|
58
|
+
enqueueOperation(type, ...args) {
|
|
59
|
+
const newPromise = this.queue.then(() => {
|
|
60
|
+
// @ts-expect-error Tuple type warning
|
|
61
|
+
return this.cache[type](...args);
|
|
62
|
+
});
|
|
63
|
+
this.queue = newPromise.then(() => void 0, () => void 0);
|
|
64
|
+
return newPromise;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
29
67
|
class PregelLoop {
|
|
30
68
|
get isResuming() {
|
|
31
69
|
const hasChannelVersions = Object.keys(this.checkpoint.channel_versions).length !== 0;
|
|
@@ -207,6 +245,12 @@ class PregelLoop {
|
|
|
207
245
|
writable: true,
|
|
208
246
|
value: void 0
|
|
209
247
|
});
|
|
248
|
+
Object.defineProperty(this, "cache", {
|
|
249
|
+
enumerable: true,
|
|
250
|
+
configurable: true,
|
|
251
|
+
writable: true,
|
|
252
|
+
value: void 0
|
|
253
|
+
});
|
|
210
254
|
Object.defineProperty(this, "manager", {
|
|
211
255
|
enumerable: true,
|
|
212
256
|
configurable: true,
|
|
@@ -237,6 +281,12 @@ class PregelLoop {
|
|
|
237
281
|
writable: true,
|
|
238
282
|
value: false
|
|
239
283
|
});
|
|
284
|
+
Object.defineProperty(this, "triggerToNodes", {
|
|
285
|
+
enumerable: true,
|
|
286
|
+
configurable: true,
|
|
287
|
+
writable: true,
|
|
288
|
+
value: void 0
|
|
289
|
+
});
|
|
240
290
|
this.input = params.input;
|
|
241
291
|
this.checkpointer = params.checkpointer;
|
|
242
292
|
// TODO: if managed values no longer needs graph we can replace with
|
|
@@ -264,12 +314,14 @@ class PregelLoop {
|
|
|
264
314
|
this.nodes = params.nodes;
|
|
265
315
|
this.skipDoneTasks = params.skipDoneTasks;
|
|
266
316
|
this.store = params.store;
|
|
317
|
+
this.cache = params.cache ? new AsyncBatchedCache(params.cache) : undefined;
|
|
267
318
|
this.stream = params.stream;
|
|
268
319
|
this.checkpointNamespace = params.checkpointNamespace;
|
|
269
320
|
this.prevCheckpointConfig = params.prevCheckpointConfig;
|
|
270
321
|
this.interruptAfter = params.interruptAfter;
|
|
271
322
|
this.interruptBefore = params.interruptBefore;
|
|
272
323
|
this.debug = params.debug;
|
|
324
|
+
this.triggerToNodes = params.triggerToNodes;
|
|
273
325
|
}
|
|
274
326
|
static async initialize(params) {
|
|
275
327
|
let { config, stream } = params;
|
|
@@ -367,9 +419,11 @@ class PregelLoop {
|
|
|
367
419
|
nodes: params.nodes,
|
|
368
420
|
stream,
|
|
369
421
|
store,
|
|
422
|
+
cache: params.cache,
|
|
370
423
|
interruptAfter: params.interruptAfter,
|
|
371
424
|
interruptBefore: params.interruptBefore,
|
|
372
425
|
debug: params.debug,
|
|
426
|
+
triggerToNodes: params.triggerToNodes,
|
|
373
427
|
});
|
|
374
428
|
}
|
|
375
429
|
_checkpointerPutAfterPrevious(input) {
|
|
@@ -423,6 +477,25 @@ class PregelLoop {
|
|
|
423
477
|
if (this.tasks) {
|
|
424
478
|
this._outputWrites(taskId, writesCopy);
|
|
425
479
|
}
|
|
480
|
+
if (!writes.length || !this.cache || !this.tasks) {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
// only cache tasks with a cache key
|
|
484
|
+
const task = this.tasks[taskId];
|
|
485
|
+
if (task == null || task.cache_key == null) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
// only cache successful tasks
|
|
489
|
+
if (writes[0][0] === constants_js_1.ERROR || writes[0][0] === constants_js_1.INTERRUPT) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
void this.cache.set([
|
|
493
|
+
{
|
|
494
|
+
key: [task.cache_key.ns, task.cache_key.key],
|
|
495
|
+
value: task.writes,
|
|
496
|
+
ttl: task.cache_key.ttl,
|
|
497
|
+
},
|
|
498
|
+
]);
|
|
426
499
|
}
|
|
427
500
|
_outputWrites(taskId, writes, cached = false) {
|
|
428
501
|
const task = this.tasks[taskId];
|
|
@@ -441,6 +514,34 @@ class PregelLoop {
|
|
|
441
514
|
}
|
|
442
515
|
}
|
|
443
516
|
}
|
|
517
|
+
async _matchCachedWrites() {
|
|
518
|
+
if (!this.cache)
|
|
519
|
+
return [];
|
|
520
|
+
const matched = [];
|
|
521
|
+
const serializeKey = ([ns, key]) => {
|
|
522
|
+
return `ns:${ns.join(",")}|key:${key}`;
|
|
523
|
+
};
|
|
524
|
+
const keys = [];
|
|
525
|
+
const keyMap = {};
|
|
526
|
+
for (const task of Object.values(this.tasks)) {
|
|
527
|
+
if (task.cache_key != null && !task.writes.length) {
|
|
528
|
+
keys.push([task.cache_key.ns, task.cache_key.key]);
|
|
529
|
+
keyMap[serializeKey([task.cache_key.ns, task.cache_key.key])] = task;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
if (keys.length === 0)
|
|
533
|
+
return [];
|
|
534
|
+
const cache = await this.cache.get(keys);
|
|
535
|
+
for (const { key, value } of cache) {
|
|
536
|
+
const task = keyMap[serializeKey(key)];
|
|
537
|
+
if (task != null) {
|
|
538
|
+
// update the task with the cached writes
|
|
539
|
+
task.writes.push(...value);
|
|
540
|
+
matched.push({ task, result: value });
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return matched;
|
|
544
|
+
}
|
|
444
545
|
/**
|
|
445
546
|
* Execute a single iteration of the Pregel loop.
|
|
446
547
|
* Returns true if more iterations are needed.
|
|
@@ -465,7 +566,7 @@ class PregelLoop {
|
|
|
465
566
|
// finish superstep
|
|
466
567
|
const writes = Object.values(this.tasks).flatMap((t) => t.writes);
|
|
467
568
|
// All tasks have finished
|
|
468
|
-
const managedValueWrites = (0, algo_js_1._applyWrites)(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion);
|
|
569
|
+
const managedValueWrites = (0, algo_js_1._applyWrites)(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion, this.triggerToNodes);
|
|
469
570
|
for (const [key, values] of Object.entries(managedValueWrites)) {
|
|
470
571
|
await this.updateManagedValues(key, values);
|
|
471
572
|
}
|
|
@@ -554,25 +655,22 @@ class PregelLoop {
|
|
|
554
655
|
if (this.tasks !== undefined &&
|
|
555
656
|
this.checkpointPendingWrites.length > 0 &&
|
|
556
657
|
Object.values(this.tasks).some((task) => task.writes.length > 0)) {
|
|
557
|
-
const managedValueWrites = (0, algo_js_1._applyWrites)(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion);
|
|
658
|
+
const managedValueWrites = (0, algo_js_1._applyWrites)(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion, this.triggerToNodes);
|
|
558
659
|
for (const [key, values] of Object.entries(managedValueWrites)) {
|
|
559
660
|
await this.updateManagedValues(key, values);
|
|
560
661
|
}
|
|
561
662
|
this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputValues)(this.outputKeys, Object.values(this.tasks).flatMap((t) => t.writes), this.channels), "values")));
|
|
562
663
|
}
|
|
563
664
|
// Emit INTERRUPT event
|
|
665
|
+
const interrupts = { [constants_js_1.INTERRUPT]: error.interrupts };
|
|
564
666
|
this._emit([
|
|
565
|
-
[
|
|
566
|
-
|
|
567
|
-
{
|
|
568
|
-
[constants_js_1.INTERRUPT]: error.interrupts,
|
|
569
|
-
},
|
|
570
|
-
],
|
|
667
|
+
["updates", interrupts],
|
|
668
|
+
["values", interrupts],
|
|
571
669
|
]);
|
|
572
670
|
}
|
|
573
671
|
return suppress;
|
|
574
672
|
}
|
|
575
|
-
acceptPush(task, writeIdx, call) {
|
|
673
|
+
async acceptPush(task, writeIdx, call) {
|
|
576
674
|
if (this.interruptAfter?.length > 0 &&
|
|
577
675
|
(0, algo_js_1.shouldInterrupt)(this.checkpoint, this.interruptAfter, [task])) {
|
|
578
676
|
this.toInterrupt.push(task);
|
|
@@ -585,22 +683,24 @@ class PregelLoop {
|
|
|
585
683
|
store: this.store,
|
|
586
684
|
stream: this.stream,
|
|
587
685
|
});
|
|
588
|
-
if (pushed)
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTasks)(this.step, [pushed]), "debug")));
|
|
595
|
-
if (this.debug) {
|
|
596
|
-
(0, debug_js_1.printStepTasks)(this.step, [pushed]);
|
|
597
|
-
}
|
|
598
|
-
this.tasks[pushed.id] = pushed;
|
|
599
|
-
if (this.skipDoneTasks) {
|
|
600
|
-
this._matchWrites({ [pushed.id]: pushed });
|
|
601
|
-
}
|
|
602
|
-
return pushed;
|
|
686
|
+
if (!pushed)
|
|
687
|
+
return;
|
|
688
|
+
if (this.interruptBefore?.length > 0 &&
|
|
689
|
+
(0, algo_js_1.shouldInterrupt)(this.checkpoint, this.interruptBefore, [pushed])) {
|
|
690
|
+
this.toInterrupt.push(pushed);
|
|
691
|
+
return;
|
|
603
692
|
}
|
|
693
|
+
this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTasks)(this.step, [pushed]), "debug")));
|
|
694
|
+
if (this.debug)
|
|
695
|
+
(0, debug_js_1.printStepTasks)(this.step, [pushed]);
|
|
696
|
+
this.tasks[pushed.id] = pushed;
|
|
697
|
+
if (this.skipDoneTasks)
|
|
698
|
+
this._matchWrites({ [pushed.id]: pushed });
|
|
699
|
+
const tasks = await this._matchCachedWrites();
|
|
700
|
+
for (const { task } of tasks) {
|
|
701
|
+
this._outputWrites(task.id, task.writes, true);
|
|
702
|
+
}
|
|
703
|
+
return pushed;
|
|
604
704
|
}
|
|
605
705
|
_suppressInterrupt(e) {
|
|
606
706
|
return (0, errors_js_1.isGraphInterrupt)(e) && !this.isNested;
|
|
@@ -649,7 +749,7 @@ class PregelLoop {
|
|
|
649
749
|
writes: nullWrites,
|
|
650
750
|
triggers: [],
|
|
651
751
|
},
|
|
652
|
-
], this.checkpointerGetNextVersion);
|
|
752
|
+
], this.checkpointerGetNextVersion, this.triggerToNodes);
|
|
653
753
|
}
|
|
654
754
|
const isCommandUpdateOrGoto = (0, constants_js_1.isCommand)(this.input) && nullWrites.length > 0;
|
|
655
755
|
if (this.isResuming || isCommandUpdateOrGoto) {
|
|
@@ -687,7 +787,7 @@ class PregelLoop {
|
|
|
687
787
|
writes: inputWrites,
|
|
688
788
|
triggers: [],
|
|
689
789
|
},
|
|
690
|
-
]), this.checkpointerGetNextVersion);
|
|
790
|
+
]), this.checkpointerGetNextVersion, this.triggerToNodes);
|
|
691
791
|
// save input checkpoint
|
|
692
792
|
await this._putCheckpoint({
|
|
693
793
|
source: "input",
|
package/dist/pregel/loop.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RunnableConfig } from "@langchain/core/runnables";
|
|
2
2
|
import type { CallbackManagerForChainRun } from "@langchain/core/callbacks/manager";
|
|
3
|
-
import { BaseCheckpointSaver, Checkpoint, PendingWrite, CheckpointPendingWrite, CheckpointMetadata, All, BaseStore, AsyncBatchedStore } from "@langchain/langgraph-checkpoint";
|
|
3
|
+
import { BaseCheckpointSaver, Checkpoint, PendingWrite, CheckpointPendingWrite, CheckpointMetadata, All, BaseStore, AsyncBatchedStore, BaseCache, CacheFullKey, CacheNamespace } from "@langchain/langgraph-checkpoint";
|
|
4
4
|
import { BaseChannel } from "../channels/base.js";
|
|
5
5
|
import { Call, PregelExecutableTask, StreamMode } from "./types.js";
|
|
6
6
|
import { Command } from "../constants.js";
|
|
@@ -19,10 +19,12 @@ export type PregelLoopInitializeParams = {
|
|
|
19
19
|
managed: ManagedValueMapping;
|
|
20
20
|
stream: IterableReadableWritableStream;
|
|
21
21
|
store?: BaseStore;
|
|
22
|
+
cache?: BaseCache<PendingWrite<string>[]>;
|
|
22
23
|
interruptAfter: string[] | All;
|
|
23
24
|
interruptBefore: string[] | All;
|
|
24
25
|
manager?: CallbackManagerForChainRun;
|
|
25
26
|
debug: boolean;
|
|
27
|
+
triggerToNodes: Record<string, string[]>;
|
|
26
28
|
};
|
|
27
29
|
type PregelLoopParams = {
|
|
28
30
|
input?: any | Command;
|
|
@@ -46,11 +48,30 @@ type PregelLoopParams = {
|
|
|
46
48
|
manager?: CallbackManagerForChainRun;
|
|
47
49
|
stream: IterableReadableWritableStream;
|
|
48
50
|
store?: AsyncBatchedStore;
|
|
51
|
+
cache?: BaseCache<PendingWrite<string>[]>;
|
|
49
52
|
prevCheckpointConfig: RunnableConfig | undefined;
|
|
50
53
|
interruptAfter: string[] | All;
|
|
51
54
|
interruptBefore: string[] | All;
|
|
52
55
|
debug: boolean;
|
|
56
|
+
triggerToNodes: Record<string, string[]>;
|
|
53
57
|
};
|
|
58
|
+
declare class AsyncBatchedCache extends BaseCache<PendingWrite<string>[]> {
|
|
59
|
+
protected cache: BaseCache<PendingWrite<string>[]>;
|
|
60
|
+
private queue;
|
|
61
|
+
constructor(cache: BaseCache<unknown>);
|
|
62
|
+
get(keys: CacheFullKey[]): Promise<{
|
|
63
|
+
key: CacheFullKey;
|
|
64
|
+
value: PendingWrite<string>[];
|
|
65
|
+
}[]>;
|
|
66
|
+
set(pairs: {
|
|
67
|
+
key: CacheFullKey;
|
|
68
|
+
value: PendingWrite<string>[];
|
|
69
|
+
ttl?: number;
|
|
70
|
+
}[]): Promise<void>;
|
|
71
|
+
clear(namespaces: CacheNamespace[]): Promise<void>;
|
|
72
|
+
stop(): Promise<void>;
|
|
73
|
+
private enqueueOperation;
|
|
74
|
+
}
|
|
54
75
|
export declare class PregelLoop {
|
|
55
76
|
protected input?: any | Command;
|
|
56
77
|
output: any;
|
|
@@ -79,11 +100,13 @@ export declare class PregelLoop {
|
|
|
79
100
|
isNested: boolean;
|
|
80
101
|
protected _checkpointerChainedPromise: Promise<unknown>;
|
|
81
102
|
store?: AsyncBatchedStore;
|
|
103
|
+
cache?: AsyncBatchedCache;
|
|
82
104
|
manager?: CallbackManagerForChainRun;
|
|
83
105
|
interruptAfter: string[] | All;
|
|
84
106
|
interruptBefore: string[] | All;
|
|
85
107
|
toInterrupt: PregelExecutableTask<string, string>[];
|
|
86
108
|
debug: boolean;
|
|
109
|
+
triggerToNodes: Record<string, string[]>;
|
|
87
110
|
get isResuming(): any;
|
|
88
111
|
constructor(params: PregelLoopParams);
|
|
89
112
|
static initialize(params: PregelLoopInitializeParams): Promise<PregelLoop>;
|
|
@@ -101,6 +124,10 @@ export declare class PregelLoop {
|
|
|
101
124
|
*/
|
|
102
125
|
putWrites(taskId: string, writes: PendingWrite<string>[]): void;
|
|
103
126
|
_outputWrites(taskId: string, writes: [string, unknown][], cached?: boolean): void;
|
|
127
|
+
_matchCachedWrites(): Promise<{
|
|
128
|
+
task: PregelExecutableTask<string, string>;
|
|
129
|
+
result: unknown;
|
|
130
|
+
}[]>;
|
|
104
131
|
/**
|
|
105
132
|
* Execute a single iteration of the Pregel loop.
|
|
106
133
|
* Returns true if more iterations are needed.
|
|
@@ -110,7 +137,7 @@ export declare class PregelLoop {
|
|
|
110
137
|
inputKeys?: string | string[];
|
|
111
138
|
}): Promise<boolean>;
|
|
112
139
|
finishAndHandleError(error?: Error): Promise<boolean>;
|
|
113
|
-
acceptPush(task: PregelExecutableTask<string, string>, writeIdx: number, call?: Call): PregelExecutableTask<string, string> | void
|
|
140
|
+
acceptPush(task: PregelExecutableTask<string, string>, writeIdx: number, call?: Call): Promise<PregelExecutableTask<string, string> | void>;
|
|
114
141
|
protected _suppressInterrupt(e?: Error): boolean;
|
|
115
142
|
protected _first(inputKeys: string | string[]): Promise<void>;
|
|
116
143
|
protected _emit(values: [StreamMode, unknown][]): void;
|
package/dist/pregel/loop.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { copyCheckpoint, emptyCheckpoint, AsyncBatchedStore, WRITES_IDX_MAP, } from "@langchain/langgraph-checkpoint";
|
|
1
|
+
import { copyCheckpoint, emptyCheckpoint, AsyncBatchedStore, WRITES_IDX_MAP, BaseCache, } from "@langchain/langgraph-checkpoint";
|
|
2
2
|
import { createCheckpoint, emptyChannels, } from "../channels/base.js";
|
|
3
3
|
import { isCommand, CHECKPOINT_NAMESPACE_SEPARATOR, CONFIG_KEY_CHECKPOINT_MAP, CONFIG_KEY_READ, CONFIG_KEY_RESUMING, CONFIG_KEY_STREAM, ERROR, INPUT, INTERRUPT, NULL_TASK_ID, RESUME, TAG_HIDDEN, PUSH, CONFIG_KEY_SCRATCHPAD, CONFIG_KEY_CHECKPOINT_NS, } from "../constants.js";
|
|
4
4
|
import { _applyWrites, _prepareNextTasks, _prepareSingleTask, increment, shouldInterrupt, } from "./algo.js";
|
|
@@ -23,6 +23,44 @@ function createDuplexStream(...streams) {
|
|
|
23
23
|
modes: new Set(streams.flatMap((s) => Array.from(s.modes))),
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
+
class AsyncBatchedCache extends BaseCache {
|
|
27
|
+
constructor(cache) {
|
|
28
|
+
super();
|
|
29
|
+
Object.defineProperty(this, "cache", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: void 0
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(this, "queue", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
writable: true,
|
|
39
|
+
value: Promise.resolve()
|
|
40
|
+
});
|
|
41
|
+
this.cache = cache;
|
|
42
|
+
}
|
|
43
|
+
async get(keys) {
|
|
44
|
+
return this.enqueueOperation("get", keys);
|
|
45
|
+
}
|
|
46
|
+
async set(pairs) {
|
|
47
|
+
return this.enqueueOperation("set", pairs);
|
|
48
|
+
}
|
|
49
|
+
async clear(namespaces) {
|
|
50
|
+
return this.enqueueOperation("clear", namespaces);
|
|
51
|
+
}
|
|
52
|
+
async stop() {
|
|
53
|
+
await this.queue;
|
|
54
|
+
}
|
|
55
|
+
enqueueOperation(type, ...args) {
|
|
56
|
+
const newPromise = this.queue.then(() => {
|
|
57
|
+
// @ts-expect-error Tuple type warning
|
|
58
|
+
return this.cache[type](...args);
|
|
59
|
+
});
|
|
60
|
+
this.queue = newPromise.then(() => void 0, () => void 0);
|
|
61
|
+
return newPromise;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
26
64
|
export class PregelLoop {
|
|
27
65
|
get isResuming() {
|
|
28
66
|
const hasChannelVersions = Object.keys(this.checkpoint.channel_versions).length !== 0;
|
|
@@ -204,6 +242,12 @@ export class PregelLoop {
|
|
|
204
242
|
writable: true,
|
|
205
243
|
value: void 0
|
|
206
244
|
});
|
|
245
|
+
Object.defineProperty(this, "cache", {
|
|
246
|
+
enumerable: true,
|
|
247
|
+
configurable: true,
|
|
248
|
+
writable: true,
|
|
249
|
+
value: void 0
|
|
250
|
+
});
|
|
207
251
|
Object.defineProperty(this, "manager", {
|
|
208
252
|
enumerable: true,
|
|
209
253
|
configurable: true,
|
|
@@ -234,6 +278,12 @@ export class PregelLoop {
|
|
|
234
278
|
writable: true,
|
|
235
279
|
value: false
|
|
236
280
|
});
|
|
281
|
+
Object.defineProperty(this, "triggerToNodes", {
|
|
282
|
+
enumerable: true,
|
|
283
|
+
configurable: true,
|
|
284
|
+
writable: true,
|
|
285
|
+
value: void 0
|
|
286
|
+
});
|
|
237
287
|
this.input = params.input;
|
|
238
288
|
this.checkpointer = params.checkpointer;
|
|
239
289
|
// TODO: if managed values no longer needs graph we can replace with
|
|
@@ -261,12 +311,14 @@ export class PregelLoop {
|
|
|
261
311
|
this.nodes = params.nodes;
|
|
262
312
|
this.skipDoneTasks = params.skipDoneTasks;
|
|
263
313
|
this.store = params.store;
|
|
314
|
+
this.cache = params.cache ? new AsyncBatchedCache(params.cache) : undefined;
|
|
264
315
|
this.stream = params.stream;
|
|
265
316
|
this.checkpointNamespace = params.checkpointNamespace;
|
|
266
317
|
this.prevCheckpointConfig = params.prevCheckpointConfig;
|
|
267
318
|
this.interruptAfter = params.interruptAfter;
|
|
268
319
|
this.interruptBefore = params.interruptBefore;
|
|
269
320
|
this.debug = params.debug;
|
|
321
|
+
this.triggerToNodes = params.triggerToNodes;
|
|
270
322
|
}
|
|
271
323
|
static async initialize(params) {
|
|
272
324
|
let { config, stream } = params;
|
|
@@ -364,9 +416,11 @@ export class PregelLoop {
|
|
|
364
416
|
nodes: params.nodes,
|
|
365
417
|
stream,
|
|
366
418
|
store,
|
|
419
|
+
cache: params.cache,
|
|
367
420
|
interruptAfter: params.interruptAfter,
|
|
368
421
|
interruptBefore: params.interruptBefore,
|
|
369
422
|
debug: params.debug,
|
|
423
|
+
triggerToNodes: params.triggerToNodes,
|
|
370
424
|
});
|
|
371
425
|
}
|
|
372
426
|
_checkpointerPutAfterPrevious(input) {
|
|
@@ -420,6 +474,25 @@ export class PregelLoop {
|
|
|
420
474
|
if (this.tasks) {
|
|
421
475
|
this._outputWrites(taskId, writesCopy);
|
|
422
476
|
}
|
|
477
|
+
if (!writes.length || !this.cache || !this.tasks) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
// only cache tasks with a cache key
|
|
481
|
+
const task = this.tasks[taskId];
|
|
482
|
+
if (task == null || task.cache_key == null) {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
// only cache successful tasks
|
|
486
|
+
if (writes[0][0] === ERROR || writes[0][0] === INTERRUPT) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
void this.cache.set([
|
|
490
|
+
{
|
|
491
|
+
key: [task.cache_key.ns, task.cache_key.key],
|
|
492
|
+
value: task.writes,
|
|
493
|
+
ttl: task.cache_key.ttl,
|
|
494
|
+
},
|
|
495
|
+
]);
|
|
423
496
|
}
|
|
424
497
|
_outputWrites(taskId, writes, cached = false) {
|
|
425
498
|
const task = this.tasks[taskId];
|
|
@@ -438,6 +511,34 @@ export class PregelLoop {
|
|
|
438
511
|
}
|
|
439
512
|
}
|
|
440
513
|
}
|
|
514
|
+
async _matchCachedWrites() {
|
|
515
|
+
if (!this.cache)
|
|
516
|
+
return [];
|
|
517
|
+
const matched = [];
|
|
518
|
+
const serializeKey = ([ns, key]) => {
|
|
519
|
+
return `ns:${ns.join(",")}|key:${key}`;
|
|
520
|
+
};
|
|
521
|
+
const keys = [];
|
|
522
|
+
const keyMap = {};
|
|
523
|
+
for (const task of Object.values(this.tasks)) {
|
|
524
|
+
if (task.cache_key != null && !task.writes.length) {
|
|
525
|
+
keys.push([task.cache_key.ns, task.cache_key.key]);
|
|
526
|
+
keyMap[serializeKey([task.cache_key.ns, task.cache_key.key])] = task;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (keys.length === 0)
|
|
530
|
+
return [];
|
|
531
|
+
const cache = await this.cache.get(keys);
|
|
532
|
+
for (const { key, value } of cache) {
|
|
533
|
+
const task = keyMap[serializeKey(key)];
|
|
534
|
+
if (task != null) {
|
|
535
|
+
// update the task with the cached writes
|
|
536
|
+
task.writes.push(...value);
|
|
537
|
+
matched.push({ task, result: value });
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return matched;
|
|
541
|
+
}
|
|
441
542
|
/**
|
|
442
543
|
* Execute a single iteration of the Pregel loop.
|
|
443
544
|
* Returns true if more iterations are needed.
|
|
@@ -462,7 +563,7 @@ export class PregelLoop {
|
|
|
462
563
|
// finish superstep
|
|
463
564
|
const writes = Object.values(this.tasks).flatMap((t) => t.writes);
|
|
464
565
|
// All tasks have finished
|
|
465
|
-
const managedValueWrites = _applyWrites(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion);
|
|
566
|
+
const managedValueWrites = _applyWrites(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion, this.triggerToNodes);
|
|
466
567
|
for (const [key, values] of Object.entries(managedValueWrites)) {
|
|
467
568
|
await this.updateManagedValues(key, values);
|
|
468
569
|
}
|
|
@@ -551,25 +652,22 @@ export class PregelLoop {
|
|
|
551
652
|
if (this.tasks !== undefined &&
|
|
552
653
|
this.checkpointPendingWrites.length > 0 &&
|
|
553
654
|
Object.values(this.tasks).some((task) => task.writes.length > 0)) {
|
|
554
|
-
const managedValueWrites = _applyWrites(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion);
|
|
655
|
+
const managedValueWrites = _applyWrites(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion, this.triggerToNodes);
|
|
555
656
|
for (const [key, values] of Object.entries(managedValueWrites)) {
|
|
556
657
|
await this.updateManagedValues(key, values);
|
|
557
658
|
}
|
|
558
659
|
this._emit(gatherIteratorSync(prefixGenerator(mapOutputValues(this.outputKeys, Object.values(this.tasks).flatMap((t) => t.writes), this.channels), "values")));
|
|
559
660
|
}
|
|
560
661
|
// Emit INTERRUPT event
|
|
662
|
+
const interrupts = { [INTERRUPT]: error.interrupts };
|
|
561
663
|
this._emit([
|
|
562
|
-
[
|
|
563
|
-
|
|
564
|
-
{
|
|
565
|
-
[INTERRUPT]: error.interrupts,
|
|
566
|
-
},
|
|
567
|
-
],
|
|
664
|
+
["updates", interrupts],
|
|
665
|
+
["values", interrupts],
|
|
568
666
|
]);
|
|
569
667
|
}
|
|
570
668
|
return suppress;
|
|
571
669
|
}
|
|
572
|
-
acceptPush(task, writeIdx, call) {
|
|
670
|
+
async acceptPush(task, writeIdx, call) {
|
|
573
671
|
if (this.interruptAfter?.length > 0 &&
|
|
574
672
|
shouldInterrupt(this.checkpoint, this.interruptAfter, [task])) {
|
|
575
673
|
this.toInterrupt.push(task);
|
|
@@ -582,22 +680,24 @@ export class PregelLoop {
|
|
|
582
680
|
store: this.store,
|
|
583
681
|
stream: this.stream,
|
|
584
682
|
});
|
|
585
|
-
if (pushed)
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
this._emit(gatherIteratorSync(prefixGenerator(mapDebugTasks(this.step, [pushed]), "debug")));
|
|
592
|
-
if (this.debug) {
|
|
593
|
-
printStepTasks(this.step, [pushed]);
|
|
594
|
-
}
|
|
595
|
-
this.tasks[pushed.id] = pushed;
|
|
596
|
-
if (this.skipDoneTasks) {
|
|
597
|
-
this._matchWrites({ [pushed.id]: pushed });
|
|
598
|
-
}
|
|
599
|
-
return pushed;
|
|
683
|
+
if (!pushed)
|
|
684
|
+
return;
|
|
685
|
+
if (this.interruptBefore?.length > 0 &&
|
|
686
|
+
shouldInterrupt(this.checkpoint, this.interruptBefore, [pushed])) {
|
|
687
|
+
this.toInterrupt.push(pushed);
|
|
688
|
+
return;
|
|
600
689
|
}
|
|
690
|
+
this._emit(gatherIteratorSync(prefixGenerator(mapDebugTasks(this.step, [pushed]), "debug")));
|
|
691
|
+
if (this.debug)
|
|
692
|
+
printStepTasks(this.step, [pushed]);
|
|
693
|
+
this.tasks[pushed.id] = pushed;
|
|
694
|
+
if (this.skipDoneTasks)
|
|
695
|
+
this._matchWrites({ [pushed.id]: pushed });
|
|
696
|
+
const tasks = await this._matchCachedWrites();
|
|
697
|
+
for (const { task } of tasks) {
|
|
698
|
+
this._outputWrites(task.id, task.writes, true);
|
|
699
|
+
}
|
|
700
|
+
return pushed;
|
|
601
701
|
}
|
|
602
702
|
_suppressInterrupt(e) {
|
|
603
703
|
return isGraphInterrupt(e) && !this.isNested;
|
|
@@ -646,7 +746,7 @@ export class PregelLoop {
|
|
|
646
746
|
writes: nullWrites,
|
|
647
747
|
triggers: [],
|
|
648
748
|
},
|
|
649
|
-
], this.checkpointerGetNextVersion);
|
|
749
|
+
], this.checkpointerGetNextVersion, this.triggerToNodes);
|
|
650
750
|
}
|
|
651
751
|
const isCommandUpdateOrGoto = isCommand(this.input) && nullWrites.length > 0;
|
|
652
752
|
if (this.isResuming || isCommandUpdateOrGoto) {
|
|
@@ -684,7 +784,7 @@ export class PregelLoop {
|
|
|
684
784
|
writes: inputWrites,
|
|
685
785
|
triggers: [],
|
|
686
786
|
},
|
|
687
|
-
]), this.checkpointerGetNextVersion);
|
|
787
|
+
]), this.checkpointerGetNextVersion, this.triggerToNodes);
|
|
688
788
|
// save input checkpoint
|
|
689
789
|
await this._putCheckpoint({
|
|
690
790
|
source: "input",
|