@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.
Files changed (105) hide show
  1. package/dist/channels/any_value.cjs +3 -0
  2. package/dist/channels/any_value.d.ts +1 -0
  3. package/dist/channels/any_value.js +3 -0
  4. package/dist/channels/any_value.js.map +1 -1
  5. package/dist/channels/base.cjs +31 -2
  6. package/dist/channels/base.d.ts +17 -2
  7. package/dist/channels/base.js +31 -2
  8. package/dist/channels/base.js.map +1 -1
  9. package/dist/channels/binop.cjs +3 -0
  10. package/dist/channels/binop.d.ts +1 -0
  11. package/dist/channels/binop.js +3 -0
  12. package/dist/channels/binop.js.map +1 -1
  13. package/dist/channels/dynamic_barrier_value.cjs +119 -9
  14. package/dist/channels/dynamic_barrier_value.d.ts +29 -0
  15. package/dist/channels/dynamic_barrier_value.js +117 -8
  16. package/dist/channels/dynamic_barrier_value.js.map +1 -1
  17. package/dist/channels/ephemeral_value.cjs +3 -0
  18. package/dist/channels/ephemeral_value.d.ts +1 -0
  19. package/dist/channels/ephemeral_value.js +3 -0
  20. package/dist/channels/ephemeral_value.js.map +1 -1
  21. package/dist/channels/last_value.cjs +82 -4
  22. package/dist/channels/last_value.d.ts +21 -0
  23. package/dist/channels/last_value.js +80 -3
  24. package/dist/channels/last_value.js.map +1 -1
  25. package/dist/channels/named_barrier_value.cjs +94 -1
  26. package/dist/channels/named_barrier_value.d.ts +23 -0
  27. package/dist/channels/named_barrier_value.js +92 -0
  28. package/dist/channels/named_barrier_value.js.map +1 -1
  29. package/dist/channels/topic.cjs +3 -0
  30. package/dist/channels/topic.d.ts +1 -0
  31. package/dist/channels/topic.js +3 -0
  32. package/dist/channels/topic.js.map +1 -1
  33. package/dist/constants.cjs +29 -8
  34. package/dist/constants.d.ts +47 -26
  35. package/dist/constants.js +27 -7
  36. package/dist/constants.js.map +1 -1
  37. package/dist/func/index.cjs +17 -4
  38. package/dist/func/index.d.ts +14 -5
  39. package/dist/func/index.js +17 -4
  40. package/dist/func/index.js.map +1 -1
  41. package/dist/func/types.d.ts +1 -1
  42. package/dist/graph/graph.d.ts +4 -2
  43. package/dist/graph/graph.js.map +1 -1
  44. package/dist/graph/index.cjs +2 -1
  45. package/dist/graph/index.d.ts +1 -1
  46. package/dist/graph/index.js +1 -1
  47. package/dist/graph/index.js.map +1 -1
  48. package/dist/graph/messages_annotation.cjs +3 -0
  49. package/dist/graph/messages_annotation.js +3 -0
  50. package/dist/graph/messages_annotation.js.map +1 -1
  51. package/dist/graph/state.cjs +67 -22
  52. package/dist/graph/state.d.ts +22 -8
  53. package/dist/graph/state.js +68 -24
  54. package/dist/graph/state.js.map +1 -1
  55. package/dist/graph/zod/schema.cjs +12 -61
  56. package/dist/graph/zod/schema.js +12 -61
  57. package/dist/graph/zod/schema.js.map +1 -1
  58. package/dist/graph/zod/state.cjs +63 -0
  59. package/dist/graph/zod/state.d.ts +10 -1
  60. package/dist/graph/zod/state.js +61 -0
  61. package/dist/graph/zod/state.js.map +1 -1
  62. package/dist/prebuilt/react_agent_executor.cjs +99 -45
  63. package/dist/prebuilt/react_agent_executor.d.ts +20 -4
  64. package/dist/prebuilt/react_agent_executor.js +99 -45
  65. package/dist/prebuilt/react_agent_executor.js.map +1 -1
  66. package/dist/pregel/algo.cjs +60 -20
  67. package/dist/pregel/algo.d.ts +1 -1
  68. package/dist/pregel/algo.js +61 -21
  69. package/dist/pregel/algo.js.map +1 -1
  70. package/dist/pregel/call.cjs +2 -1
  71. package/dist/pregel/call.d.ts +3 -2
  72. package/dist/pregel/call.js +2 -1
  73. package/dist/pregel/call.js.map +1 -1
  74. package/dist/pregel/debug.test.cjs +6 -0
  75. package/dist/pregel/debug.test.js +6 -0
  76. package/dist/pregel/debug.test.js.map +1 -1
  77. package/dist/pregel/index.cjs +99 -29
  78. package/dist/pregel/index.d.ts +19 -6
  79. package/dist/pregel/index.js +100 -30
  80. package/dist/pregel/index.js.map +1 -1
  81. package/dist/pregel/loop.cjs +126 -26
  82. package/dist/pregel/loop.d.ts +29 -2
  83. package/dist/pregel/loop.js +127 -27
  84. package/dist/pregel/loop.js.map +1 -1
  85. package/dist/pregel/read.cjs +12 -1
  86. package/dist/pregel/read.d.ts +3 -1
  87. package/dist/pregel/read.js +12 -1
  88. package/dist/pregel/read.js.map +1 -1
  89. package/dist/pregel/retry.cjs +2 -6
  90. package/dist/pregel/retry.js +2 -6
  91. package/dist/pregel/retry.js.map +1 -1
  92. package/dist/pregel/runner.cjs +1 -0
  93. package/dist/pregel/runner.js +1 -0
  94. package/dist/pregel/runner.js.map +1 -1
  95. package/dist/pregel/types.cjs +8 -1
  96. package/dist/pregel/types.d.ts +64 -8
  97. package/dist/pregel/types.js +8 -1
  98. package/dist/pregel/types.js.map +1 -1
  99. package/dist/pregel/utils/index.d.ts +15 -0
  100. package/dist/pregel/utils/index.js.map +1 -1
  101. package/dist/web.cjs +4 -1
  102. package/dist/web.d.ts +3 -3
  103. package/dist/web.js +2 -2
  104. package/dist/web.js.map +1 -1
  105. package/package.json +5 -5
@@ -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
- "updates",
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
- if (this.interruptBefore?.length > 0 &&
590
- (0, algo_js_1.shouldInterrupt)(this.checkpoint, this.interruptBefore, [pushed])) {
591
- this.toInterrupt.push(pushed);
592
- return;
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",
@@ -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;
@@ -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
- "updates",
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
- if (this.interruptBefore?.length > 0 &&
587
- shouldInterrupt(this.checkpoint, this.interruptBefore, [pushed])) {
588
- this.toInterrupt.push(pushed);
589
- return;
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",