@langchain/langgraph 0.2.40 → 0.2.42

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 (86) hide show
  1. package/README.md +237 -154
  2. package/dist/channels/any_value.cjs +10 -10
  3. package/dist/channels/any_value.d.ts +1 -1
  4. package/dist/channels/any_value.js +10 -10
  5. package/dist/channels/ephemeral_value.cjs +10 -9
  6. package/dist/channels/ephemeral_value.d.ts +1 -1
  7. package/dist/channels/ephemeral_value.js +10 -9
  8. package/dist/channels/last_value.cjs +8 -7
  9. package/dist/channels/last_value.d.ts +1 -1
  10. package/dist/channels/last_value.js +8 -7
  11. package/dist/constants.cjs +33 -6
  12. package/dist/constants.d.ts +17 -2
  13. package/dist/constants.js +32 -5
  14. package/dist/errors.d.ts +3 -3
  15. package/dist/func/index.cjs +272 -0
  16. package/dist/func/index.d.ts +310 -0
  17. package/dist/func/index.js +267 -0
  18. package/dist/func/types.cjs +15 -0
  19. package/dist/func/types.d.ts +59 -0
  20. package/dist/func/types.js +11 -0
  21. package/dist/graph/graph.cjs +31 -35
  22. package/dist/graph/graph.d.ts +1 -5
  23. package/dist/graph/graph.js +1 -5
  24. package/dist/graph/index.cjs +1 -3
  25. package/dist/graph/index.d.ts +1 -1
  26. package/dist/graph/index.js +1 -1
  27. package/dist/graph/message.d.ts +1 -1
  28. package/dist/graph/state.cjs +17 -17
  29. package/dist/graph/state.d.ts +2 -1
  30. package/dist/graph/state.js +2 -2
  31. package/dist/index.cjs +8 -0
  32. package/dist/index.d.ts +3 -0
  33. package/dist/index.js +3 -0
  34. package/dist/interrupt.cjs +21 -34
  35. package/dist/interrupt.d.ts +1 -1
  36. package/dist/interrupt.js +22 -35
  37. package/dist/prebuilt/agent_executor.cjs +3 -3
  38. package/dist/prebuilt/agent_executor.d.ts +1 -1
  39. package/dist/prebuilt/agent_executor.js +1 -1
  40. package/dist/prebuilt/chat_agent_executor.cjs +3 -3
  41. package/dist/prebuilt/chat_agent_executor.d.ts +1 -1
  42. package/dist/prebuilt/chat_agent_executor.js +1 -1
  43. package/dist/prebuilt/react_agent_executor.cjs +79 -12
  44. package/dist/prebuilt/react_agent_executor.d.ts +35 -4
  45. package/dist/prebuilt/react_agent_executor.js +79 -13
  46. package/dist/prebuilt/tool_node.cjs +1 -2
  47. package/dist/prebuilt/tool_node.d.ts +1 -1
  48. package/dist/prebuilt/tool_node.js +1 -2
  49. package/dist/pregel/algo.cjs +121 -12
  50. package/dist/pregel/algo.d.ts +8 -6
  51. package/dist/pregel/algo.js +122 -13
  52. package/dist/pregel/call.cjs +77 -0
  53. package/dist/pregel/call.d.ts +15 -0
  54. package/dist/pregel/call.js +71 -0
  55. package/dist/pregel/index.cjs +59 -96
  56. package/dist/pregel/index.d.ts +1 -10
  57. package/dist/pregel/index.js +61 -98
  58. package/dist/pregel/io.cjs +6 -1
  59. package/dist/pregel/io.js +7 -2
  60. package/dist/pregel/loop.cjs +109 -75
  61. package/dist/pregel/loop.d.ts +17 -23
  62. package/dist/pregel/loop.js +110 -75
  63. package/dist/pregel/messages.d.ts +1 -1
  64. package/dist/pregel/retry.cjs +22 -50
  65. package/dist/pregel/retry.d.ts +6 -6
  66. package/dist/pregel/retry.js +22 -50
  67. package/dist/pregel/runner.cjs +275 -0
  68. package/dist/pregel/runner.d.ts +64 -0
  69. package/dist/pregel/runner.js +271 -0
  70. package/dist/pregel/stream.cjs +71 -0
  71. package/dist/pregel/stream.d.ts +17 -0
  72. package/dist/pregel/stream.js +67 -0
  73. package/dist/pregel/types.cjs +54 -0
  74. package/dist/pregel/types.d.ts +78 -6
  75. package/dist/pregel/types.js +51 -1
  76. package/dist/pregel/utils/config.cjs +26 -1
  77. package/dist/pregel/utils/config.d.ts +14 -0
  78. package/dist/pregel/utils/config.js +22 -0
  79. package/dist/pregel/write.d.ts +1 -1
  80. package/dist/utils.cjs +15 -1
  81. package/dist/utils.d.ts +3 -1
  82. package/dist/utils.js +12 -0
  83. package/dist/web.cjs +7 -5
  84. package/dist/web.d.ts +4 -4
  85. package/dist/web.js +3 -3
  86. package/package.json +8 -8
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PregelLoop = exports.IterableReadableWritableStream = void 0;
4
- const stream_1 = require("@langchain/core/utils/stream");
3
+ exports.PregelLoop = void 0;
5
4
  const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
6
5
  const base_js_1 = require("../channels/base.cjs");
7
6
  const constants_js_1 = require("../constants.cjs");
@@ -11,66 +10,12 @@ const io_js_1 = require("./io.cjs");
11
10
  const errors_js_1 = require("../errors.cjs");
12
11
  const index_js_1 = require("./utils/index.cjs");
13
12
  const debug_js_1 = require("./debug.cjs");
13
+ const stream_js_1 = require("./stream.cjs");
14
14
  const INPUT_DONE = Symbol.for("INPUT_DONE");
15
15
  const INPUT_RESUMING = Symbol.for("INPUT_RESUMING");
16
16
  const DEFAULT_LOOP_LIMIT = 25;
17
- class IterableReadableWritableStream extends stream_1.IterableReadableStream {
18
- constructor(params) {
19
- let streamControllerPromiseResolver;
20
- const streamControllerPromise = new Promise((resolve) => {
21
- streamControllerPromiseResolver = resolve;
22
- });
23
- super({
24
- start: (controller) => {
25
- streamControllerPromiseResolver(controller);
26
- },
27
- });
28
- Object.defineProperty(this, "modes", {
29
- enumerable: true,
30
- configurable: true,
31
- writable: true,
32
- value: void 0
33
- });
34
- Object.defineProperty(this, "controller", {
35
- enumerable: true,
36
- configurable: true,
37
- writable: true,
38
- value: void 0
39
- });
40
- Object.defineProperty(this, "passthroughFn", {
41
- enumerable: true,
42
- configurable: true,
43
- writable: true,
44
- value: void 0
45
- });
46
- // .start() will always be called before the stream can be interacted
47
- // with anyway
48
- void streamControllerPromise.then((controller) => {
49
- this.controller = controller;
50
- });
51
- this.passthroughFn = params.passthroughFn;
52
- this.modes = params.modes;
53
- }
54
- push(chunk) {
55
- this.passthroughFn?.(chunk);
56
- this.controller.enqueue(chunk);
57
- }
58
- close() {
59
- try {
60
- this.controller.close();
61
- }
62
- catch (e) {
63
- // pass
64
- }
65
- }
66
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
- error(e) {
68
- this.controller.error(e);
69
- }
70
- }
71
- exports.IterableReadableWritableStream = IterableReadableWritableStream;
72
17
  function createDuplexStream(...streams) {
73
- return new IterableReadableWritableStream({
18
+ return new stream_js_1.IterableReadableWritableStream({
74
19
  passthroughFn: (value) => {
75
20
  for (const stream of streams) {
76
21
  if (stream.modes.has(value[1])) {
@@ -249,6 +194,36 @@ class PregelLoop {
249
194
  writable: true,
250
195
  value: void 0
251
196
  });
197
+ Object.defineProperty(this, "manager", {
198
+ enumerable: true,
199
+ configurable: true,
200
+ writable: true,
201
+ value: void 0
202
+ });
203
+ Object.defineProperty(this, "interruptAfter", {
204
+ enumerable: true,
205
+ configurable: true,
206
+ writable: true,
207
+ value: void 0
208
+ });
209
+ Object.defineProperty(this, "interruptBefore", {
210
+ enumerable: true,
211
+ configurable: true,
212
+ writable: true,
213
+ value: void 0
214
+ });
215
+ Object.defineProperty(this, "toInterrupt", {
216
+ enumerable: true,
217
+ configurable: true,
218
+ writable: true,
219
+ value: []
220
+ });
221
+ Object.defineProperty(this, "debug", {
222
+ enumerable: true,
223
+ configurable: true,
224
+ writable: true,
225
+ value: false
226
+ });
252
227
  this.input = params.input;
253
228
  this.checkpointer = params.checkpointer;
254
229
  // TODO: if managed values no longer needs graph we can replace with
@@ -270,6 +245,7 @@ class PregelLoop {
270
245
  this.config = params.config;
271
246
  this.checkpointConfig = params.checkpointConfig;
272
247
  this.isNested = params.isNested;
248
+ this.manager = params.manager;
273
249
  this.outputKeys = params.outputKeys;
274
250
  this.streamKeys = params.streamKeys;
275
251
  this.nodes = params.nodes;
@@ -278,6 +254,9 @@ class PregelLoop {
278
254
  this.stream = params.stream;
279
255
  this.checkpointNamespace = params.checkpointNamespace;
280
256
  this.prevCheckpointConfig = params.prevCheckpointConfig;
257
+ this.interruptAfter = params.interruptAfter;
258
+ this.interruptBefore = params.interruptBefore;
259
+ this.debug = params.debug;
281
260
  }
282
261
  static async initialize(params) {
283
262
  let { config, stream } = params;
@@ -368,6 +347,7 @@ class PregelLoop {
368
347
  channels,
369
348
  managed: params.managed,
370
349
  isNested,
350
+ manager: params.manager,
371
351
  skipDoneTasks,
372
352
  step,
373
353
  stop,
@@ -378,6 +358,9 @@ class PregelLoop {
378
358
  nodes: params.nodes,
379
359
  stream,
380
360
  store,
361
+ interruptAfter: params.interruptAfter,
362
+ interruptBefore: params.interruptBefore,
363
+ debug: params.debug,
381
364
  });
382
365
  }
383
366
  _checkpointerPutAfterPrevious(input) {
@@ -458,7 +441,7 @@ class PregelLoop {
458
441
  if (this.store && !this.store.isRunning) {
459
442
  this.store?.start();
460
443
  }
461
- const { inputKeys = [], interruptAfter = [], interruptBefore = [], manager, } = params;
444
+ const { inputKeys = [] } = params;
462
445
  if (this.status !== "pending") {
463
446
  throw new Error(`Cannot tick when status is no longer "pending". Current status: "${this.status}"`);
464
447
  }
@@ -482,7 +465,7 @@ class PregelLoop {
482
465
  writes: (0, io_js_1.mapOutputUpdates)(this.outputKeys, Object.values(this.tasks).map((task) => [task, task.writes])).next().value ?? null,
483
466
  });
484
467
  // after execution, check if we should interrupt
485
- if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, interruptAfter, Object.values(this.tasks))) {
468
+ if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, this.interruptAfter, Object.values(this.tasks))) {
486
469
  this.status = "interrupt_after";
487
470
  throw new errors_js_1.GraphInterrupt();
488
471
  }
@@ -498,8 +481,9 @@ class PregelLoop {
498
481
  step: this.step,
499
482
  checkpointer: this.checkpointer,
500
483
  isResuming: this.input === INPUT_RESUMING,
501
- manager,
484
+ manager: this.manager,
502
485
  store: this.store,
486
+ stream: this.stream,
503
487
  });
504
488
  this.tasks = nextTasks;
505
489
  // Produce debug output
@@ -530,15 +514,10 @@ class PregelLoop {
530
514
  }
531
515
  // if all tasks have finished, re-tick
532
516
  if (Object.values(this.tasks).every((task) => task.writes.length > 0)) {
533
- return this.tick({
534
- inputKeys,
535
- interruptAfter,
536
- interruptBefore,
537
- manager,
538
- });
517
+ return this.tick({ inputKeys });
539
518
  }
540
519
  // Before execution, check if we should interrupt
541
- if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, interruptBefore, Object.values(this.tasks))) {
520
+ if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, this.interruptBefore, Object.values(this.tasks))) {
542
521
  this.status = "interrupt_before";
543
522
  throw new errors_js_1.GraphInterrupt();
544
523
  }
@@ -575,20 +554,59 @@ class PregelLoop {
575
554
  }
576
555
  return suppress;
577
556
  }
557
+ acceptPush(task, writeIdx, call) {
558
+ if (this.interruptAfter?.length > 0 &&
559
+ (0, algo_js_1.shouldInterrupt)(this.checkpoint, this.interruptAfter, [task])) {
560
+ this.toInterrupt.push(task);
561
+ return;
562
+ }
563
+ const pushed = (0, algo_js_1._prepareSingleTask)([constants_js_1.PUSH, task.path ?? [], writeIdx, task.id, call], this.checkpoint, this.checkpointPendingWrites, this.nodes, this.channels, this.managed, this.config, true, {
564
+ step: this.step,
565
+ checkpointer: this.checkpointer,
566
+ manager: this.manager,
567
+ store: this.store,
568
+ stream: this.stream,
569
+ });
570
+ if (pushed) {
571
+ if (this.interruptBefore?.length > 0 &&
572
+ (0, algo_js_1.shouldInterrupt)(this.checkpoint, this.interruptBefore, [pushed])) {
573
+ this.toInterrupt.push(pushed);
574
+ return;
575
+ }
576
+ this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTasks)(this.step, [pushed]), "debug")));
577
+ if (this.debug) {
578
+ (0, debug_js_1.printStepTasks)(this.step, [pushed]);
579
+ }
580
+ this.tasks[pushed.id] = pushed;
581
+ if (this.skipDoneTasks) {
582
+ this._matchWrites({ [pushed.id]: pushed });
583
+ }
584
+ return pushed;
585
+ }
586
+ }
578
587
  _suppressInterrupt(e) {
579
588
  return (0, errors_js_1.isGraphInterrupt)(e) && !this.isNested;
580
589
  }
581
- /**
582
- * Resuming from previous checkpoint requires
583
- * - finding a previous checkpoint
584
- * - receiving null input (outer graph) or RESUMING flag (subgraph)
585
- */
586
590
  async _first(inputKeys) {
591
+ /*
592
+ * Resuming from previous checkpoint requires
593
+ * - finding a previous checkpoint
594
+ * - receiving null input (outer graph) or RESUMING flag (subgraph)
595
+ */
596
+ const { configurable } = this.config;
587
597
  const isResuming = Object.keys(this.checkpoint.channel_versions).length !== 0 &&
588
598
  (this.config.configurable?.[constants_js_1.CONFIG_KEY_RESUMING] !== undefined ||
589
599
  this.input === null ||
590
600
  (0, constants_js_1.isCommand)(this.input));
601
+ // take resume value from parent
602
+ const scratchpad = configurable?.[constants_js_1.CONFIG_KEY_SCRATCHPAD];
603
+ if (scratchpad && scratchpad.nullResume !== undefined) {
604
+ this.putWrites(constants_js_1.NULL_TASK_ID, [[constants_js_1.RESUME, scratchpad.nullResume]]);
605
+ }
591
606
  if ((0, constants_js_1.isCommand)(this.input)) {
607
+ if (this.input.resume != null && this.checkpointer == null) {
608
+ throw new Error("Cannot use Command(resume=...) without checkpointer");
609
+ }
592
610
  const writes = {};
593
611
  // group writes by task id
594
612
  for (const [tid, key, value] of (0, io_js_1.mapCommand)(this.input, this.checkpointPendingWrites)) {
@@ -653,10 +671,10 @@ class PregelLoop {
653
671
  });
654
672
  }
655
673
  // done with input
656
- this.input = isResuming ? INPUT_RESUMING : INPUT_DONE;
674
+ this.input = this.input === INPUT_RESUMING ? INPUT_RESUMING : INPUT_DONE;
657
675
  if (!this.isNested) {
658
676
  this.config = (0, index_js_1.patchConfigurable)(this.config, {
659
- [constants_js_1.CONFIG_KEY_RESUMING]: isResuming,
677
+ [constants_js_1.CONFIG_KEY_RESUMING]: this.input === INPUT_RESUMING,
660
678
  });
661
679
  }
662
680
  }
@@ -717,5 +735,21 @@ class PregelLoop {
717
735
  }
718
736
  this.step += 1;
719
737
  }
738
+ _matchWrites(tasks) {
739
+ for (const [tid, k, v] of this.checkpointPendingWrites) {
740
+ if (k === constants_js_1.ERROR || k === constants_js_1.INTERRUPT || k === constants_js_1.RESUME) {
741
+ continue;
742
+ }
743
+ const task = Object.values(tasks).find((t) => t.id === tid);
744
+ if (task) {
745
+ task.writes.push([k, v]);
746
+ }
747
+ }
748
+ for (const task of Object.values(tasks)) {
749
+ if (task.writes.length > 0) {
750
+ this._outputWrites(task.id, task.writes, true);
751
+ }
752
+ }
753
+ }
720
754
  }
721
755
  exports.PregelLoop = PregelLoop;
@@ -1,14 +1,13 @@
1
1
  import type { RunnableConfig } from "@langchain/core/runnables";
2
2
  import type { CallbackManagerForChainRun } from "@langchain/core/callbacks/manager";
3
- import { IterableReadableStream } from "@langchain/core/utils/stream";
4
3
  import { BaseCheckpointSaver, Checkpoint, PendingWrite, CheckpointPendingWrite, CheckpointMetadata, All, BaseStore, AsyncBatchedStore } from "@langchain/langgraph-checkpoint";
5
4
  import { BaseChannel } from "../channels/base.js";
6
- import { PregelExecutableTask, StreamMode } from "./types.js";
5
+ import { Call, PregelExecutableTask, StreamMode } from "./types.js";
7
6
  import { Command } from "../constants.js";
8
7
  import { PregelNode } from "./read.js";
9
8
  import { ManagedValueMapping } from "../managed/base.js";
10
9
  import { LangGraphRunnableConfig } from "./runnable_types.js";
11
- export type StreamChunk = [string[], StreamMode, unknown];
10
+ import { IterableReadableWritableStream } from "./stream.js";
12
11
  export type PregelLoopInitializeParams = {
13
12
  input?: any | Command;
14
13
  config: RunnableConfig;
@@ -21,6 +20,10 @@ export type PregelLoopInitializeParams = {
21
20
  stream: IterableReadableWritableStream;
22
21
  store?: BaseStore;
23
22
  checkSubgraphs?: boolean;
23
+ interruptAfter: string[] | All;
24
+ interruptBefore: string[] | All;
25
+ manager?: CallbackManagerForChainRun;
26
+ debug: boolean;
24
27
  };
25
28
  type PregelLoopParams = {
26
29
  input?: any | Command;
@@ -41,22 +44,14 @@ type PregelLoopParams = {
41
44
  checkpointNamespace: string[];
42
45
  skipDoneTasks: boolean;
43
46
  isNested: boolean;
47
+ manager?: CallbackManagerForChainRun;
44
48
  stream: IterableReadableWritableStream;
45
49
  store?: AsyncBatchedStore;
46
50
  prevCheckpointConfig: RunnableConfig | undefined;
51
+ interruptAfter: string[] | All;
52
+ interruptBefore: string[] | All;
53
+ debug: boolean;
47
54
  };
48
- export declare class IterableReadableWritableStream extends IterableReadableStream<StreamChunk> {
49
- modes: Set<StreamMode>;
50
- private controller;
51
- private passthroughFn?;
52
- constructor(params: {
53
- passthroughFn?: (chunk: StreamChunk) => void;
54
- modes: Set<StreamMode>;
55
- });
56
- push(chunk: StreamChunk): void;
57
- close(): void;
58
- error(e: any): void;
59
- }
60
55
  export declare class PregelLoop {
61
56
  protected input?: any | Command;
62
57
  output: any;
@@ -85,6 +80,11 @@ export declare class PregelLoop {
85
80
  isNested: boolean;
86
81
  protected _checkpointerChainedPromise: Promise<unknown>;
87
82
  store?: AsyncBatchedStore;
83
+ manager?: CallbackManagerForChainRun;
84
+ interruptAfter: string[] | All;
85
+ interruptBefore: string[] | All;
86
+ toInterrupt: PregelExecutableTask<string, string>[];
87
+ debug: boolean;
88
88
  constructor(params: PregelLoopParams);
89
89
  static initialize(params: PregelLoopInitializeParams): Promise<PregelLoop>;
90
90
  protected _checkpointerPutAfterPrevious(input: {
@@ -108,19 +108,13 @@ export declare class PregelLoop {
108
108
  */
109
109
  tick(params: {
110
110
  inputKeys?: string | string[];
111
- interruptAfter: string[] | All;
112
- interruptBefore: string[] | All;
113
- manager?: CallbackManagerForChainRun;
114
111
  }): Promise<boolean>;
115
112
  finishAndHandleError(error?: Error): Promise<boolean>;
113
+ acceptPush(task: PregelExecutableTask<string, string>, writeIdx: number, call?: Call): PregelExecutableTask<string, string> | void;
116
114
  protected _suppressInterrupt(e?: Error): boolean;
117
- /**
118
- * Resuming from previous checkpoint requires
119
- * - finding a previous checkpoint
120
- * - receiving null input (outer graph) or RESUMING flag (subgraph)
121
- */
122
115
  protected _first(inputKeys: string | string[]): Promise<void>;
123
116
  protected _emit(values: [StreamMode, unknown][]): void;
124
117
  protected _putCheckpoint(inputMetadata: Omit<CheckpointMetadata, "step" | "parents">): Promise<void>;
118
+ protected _matchWrites(tasks: Record<string, PregelExecutableTask<string, string>>): void;
125
119
  }
126
120
  export {};
@@ -1,70 +1,16 @@
1
- import { IterableReadableStream } from "@langchain/core/utils/stream";
2
1
  import { copyCheckpoint, emptyCheckpoint, AsyncBatchedStore, WRITES_IDX_MAP, } from "@langchain/langgraph-checkpoint";
3
2
  import { createCheckpoint, emptyChannels, } from "../channels/base.js";
4
- 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, } from "../constants.js";
5
- import { _applyWrites, _prepareNextTasks, increment, shouldInterrupt, } from "./algo.js";
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, } from "../constants.js";
4
+ import { _applyWrites, _prepareNextTasks, _prepareSingleTask, increment, shouldInterrupt, } from "./algo.js";
6
5
  import { gatherIterator, gatherIteratorSync, prefixGenerator, } from "../utils.js";
7
6
  import { mapCommand, mapInput, mapOutputUpdates, mapOutputValues, readChannels, } from "./io.js";
8
7
  import { getSubgraphsSeenSet, EmptyInputError, GraphInterrupt, isGraphInterrupt, MultipleSubgraphsError, } from "../errors.js";
9
8
  import { getNewChannelVersions, patchConfigurable } from "./utils/index.js";
10
- import { mapDebugTasks, mapDebugCheckpoint, mapDebugTaskResults, } from "./debug.js";
9
+ import { mapDebugTasks, mapDebugCheckpoint, mapDebugTaskResults, printStepTasks, } from "./debug.js";
10
+ import { IterableReadableWritableStream } from "./stream.js";
11
11
  const INPUT_DONE = Symbol.for("INPUT_DONE");
12
12
  const INPUT_RESUMING = Symbol.for("INPUT_RESUMING");
13
13
  const DEFAULT_LOOP_LIMIT = 25;
14
- export class IterableReadableWritableStream extends IterableReadableStream {
15
- constructor(params) {
16
- let streamControllerPromiseResolver;
17
- const streamControllerPromise = new Promise((resolve) => {
18
- streamControllerPromiseResolver = resolve;
19
- });
20
- super({
21
- start: (controller) => {
22
- streamControllerPromiseResolver(controller);
23
- },
24
- });
25
- Object.defineProperty(this, "modes", {
26
- enumerable: true,
27
- configurable: true,
28
- writable: true,
29
- value: void 0
30
- });
31
- Object.defineProperty(this, "controller", {
32
- enumerable: true,
33
- configurable: true,
34
- writable: true,
35
- value: void 0
36
- });
37
- Object.defineProperty(this, "passthroughFn", {
38
- enumerable: true,
39
- configurable: true,
40
- writable: true,
41
- value: void 0
42
- });
43
- // .start() will always be called before the stream can be interacted
44
- // with anyway
45
- void streamControllerPromise.then((controller) => {
46
- this.controller = controller;
47
- });
48
- this.passthroughFn = params.passthroughFn;
49
- this.modes = params.modes;
50
- }
51
- push(chunk) {
52
- this.passthroughFn?.(chunk);
53
- this.controller.enqueue(chunk);
54
- }
55
- close() {
56
- try {
57
- this.controller.close();
58
- }
59
- catch (e) {
60
- // pass
61
- }
62
- }
63
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
- error(e) {
65
- this.controller.error(e);
66
- }
67
- }
68
14
  function createDuplexStream(...streams) {
69
15
  return new IterableReadableWritableStream({
70
16
  passthroughFn: (value) => {
@@ -245,6 +191,36 @@ export class PregelLoop {
245
191
  writable: true,
246
192
  value: void 0
247
193
  });
194
+ Object.defineProperty(this, "manager", {
195
+ enumerable: true,
196
+ configurable: true,
197
+ writable: true,
198
+ value: void 0
199
+ });
200
+ Object.defineProperty(this, "interruptAfter", {
201
+ enumerable: true,
202
+ configurable: true,
203
+ writable: true,
204
+ value: void 0
205
+ });
206
+ Object.defineProperty(this, "interruptBefore", {
207
+ enumerable: true,
208
+ configurable: true,
209
+ writable: true,
210
+ value: void 0
211
+ });
212
+ Object.defineProperty(this, "toInterrupt", {
213
+ enumerable: true,
214
+ configurable: true,
215
+ writable: true,
216
+ value: []
217
+ });
218
+ Object.defineProperty(this, "debug", {
219
+ enumerable: true,
220
+ configurable: true,
221
+ writable: true,
222
+ value: false
223
+ });
248
224
  this.input = params.input;
249
225
  this.checkpointer = params.checkpointer;
250
226
  // TODO: if managed values no longer needs graph we can replace with
@@ -266,6 +242,7 @@ export class PregelLoop {
266
242
  this.config = params.config;
267
243
  this.checkpointConfig = params.checkpointConfig;
268
244
  this.isNested = params.isNested;
245
+ this.manager = params.manager;
269
246
  this.outputKeys = params.outputKeys;
270
247
  this.streamKeys = params.streamKeys;
271
248
  this.nodes = params.nodes;
@@ -274,6 +251,9 @@ export class PregelLoop {
274
251
  this.stream = params.stream;
275
252
  this.checkpointNamespace = params.checkpointNamespace;
276
253
  this.prevCheckpointConfig = params.prevCheckpointConfig;
254
+ this.interruptAfter = params.interruptAfter;
255
+ this.interruptBefore = params.interruptBefore;
256
+ this.debug = params.debug;
277
257
  }
278
258
  static async initialize(params) {
279
259
  let { config, stream } = params;
@@ -364,6 +344,7 @@ export class PregelLoop {
364
344
  channels,
365
345
  managed: params.managed,
366
346
  isNested,
347
+ manager: params.manager,
367
348
  skipDoneTasks,
368
349
  step,
369
350
  stop,
@@ -374,6 +355,9 @@ export class PregelLoop {
374
355
  nodes: params.nodes,
375
356
  stream,
376
357
  store,
358
+ interruptAfter: params.interruptAfter,
359
+ interruptBefore: params.interruptBefore,
360
+ debug: params.debug,
377
361
  });
378
362
  }
379
363
  _checkpointerPutAfterPrevious(input) {
@@ -454,7 +438,7 @@ export class PregelLoop {
454
438
  if (this.store && !this.store.isRunning) {
455
439
  this.store?.start();
456
440
  }
457
- const { inputKeys = [], interruptAfter = [], interruptBefore = [], manager, } = params;
441
+ const { inputKeys = [] } = params;
458
442
  if (this.status !== "pending") {
459
443
  throw new Error(`Cannot tick when status is no longer "pending". Current status: "${this.status}"`);
460
444
  }
@@ -478,7 +462,7 @@ export class PregelLoop {
478
462
  writes: mapOutputUpdates(this.outputKeys, Object.values(this.tasks).map((task) => [task, task.writes])).next().value ?? null,
479
463
  });
480
464
  // after execution, check if we should interrupt
481
- if (shouldInterrupt(this.checkpoint, interruptAfter, Object.values(this.tasks))) {
465
+ if (shouldInterrupt(this.checkpoint, this.interruptAfter, Object.values(this.tasks))) {
482
466
  this.status = "interrupt_after";
483
467
  throw new GraphInterrupt();
484
468
  }
@@ -494,8 +478,9 @@ export class PregelLoop {
494
478
  step: this.step,
495
479
  checkpointer: this.checkpointer,
496
480
  isResuming: this.input === INPUT_RESUMING,
497
- manager,
481
+ manager: this.manager,
498
482
  store: this.store,
483
+ stream: this.stream,
499
484
  });
500
485
  this.tasks = nextTasks;
501
486
  // Produce debug output
@@ -526,15 +511,10 @@ export class PregelLoop {
526
511
  }
527
512
  // if all tasks have finished, re-tick
528
513
  if (Object.values(this.tasks).every((task) => task.writes.length > 0)) {
529
- return this.tick({
530
- inputKeys,
531
- interruptAfter,
532
- interruptBefore,
533
- manager,
534
- });
514
+ return this.tick({ inputKeys });
535
515
  }
536
516
  // Before execution, check if we should interrupt
537
- if (shouldInterrupt(this.checkpoint, interruptBefore, Object.values(this.tasks))) {
517
+ if (shouldInterrupt(this.checkpoint, this.interruptBefore, Object.values(this.tasks))) {
538
518
  this.status = "interrupt_before";
539
519
  throw new GraphInterrupt();
540
520
  }
@@ -571,20 +551,59 @@ export class PregelLoop {
571
551
  }
572
552
  return suppress;
573
553
  }
554
+ acceptPush(task, writeIdx, call) {
555
+ if (this.interruptAfter?.length > 0 &&
556
+ shouldInterrupt(this.checkpoint, this.interruptAfter, [task])) {
557
+ this.toInterrupt.push(task);
558
+ return;
559
+ }
560
+ const pushed = _prepareSingleTask([PUSH, task.path ?? [], writeIdx, task.id, call], this.checkpoint, this.checkpointPendingWrites, this.nodes, this.channels, this.managed, this.config, true, {
561
+ step: this.step,
562
+ checkpointer: this.checkpointer,
563
+ manager: this.manager,
564
+ store: this.store,
565
+ stream: this.stream,
566
+ });
567
+ if (pushed) {
568
+ if (this.interruptBefore?.length > 0 &&
569
+ shouldInterrupt(this.checkpoint, this.interruptBefore, [pushed])) {
570
+ this.toInterrupt.push(pushed);
571
+ return;
572
+ }
573
+ this._emit(gatherIteratorSync(prefixGenerator(mapDebugTasks(this.step, [pushed]), "debug")));
574
+ if (this.debug) {
575
+ printStepTasks(this.step, [pushed]);
576
+ }
577
+ this.tasks[pushed.id] = pushed;
578
+ if (this.skipDoneTasks) {
579
+ this._matchWrites({ [pushed.id]: pushed });
580
+ }
581
+ return pushed;
582
+ }
583
+ }
574
584
  _suppressInterrupt(e) {
575
585
  return isGraphInterrupt(e) && !this.isNested;
576
586
  }
577
- /**
578
- * Resuming from previous checkpoint requires
579
- * - finding a previous checkpoint
580
- * - receiving null input (outer graph) or RESUMING flag (subgraph)
581
- */
582
587
  async _first(inputKeys) {
588
+ /*
589
+ * Resuming from previous checkpoint requires
590
+ * - finding a previous checkpoint
591
+ * - receiving null input (outer graph) or RESUMING flag (subgraph)
592
+ */
593
+ const { configurable } = this.config;
583
594
  const isResuming = Object.keys(this.checkpoint.channel_versions).length !== 0 &&
584
595
  (this.config.configurable?.[CONFIG_KEY_RESUMING] !== undefined ||
585
596
  this.input === null ||
586
597
  isCommand(this.input));
598
+ // take resume value from parent
599
+ const scratchpad = configurable?.[CONFIG_KEY_SCRATCHPAD];
600
+ if (scratchpad && scratchpad.nullResume !== undefined) {
601
+ this.putWrites(NULL_TASK_ID, [[RESUME, scratchpad.nullResume]]);
602
+ }
587
603
  if (isCommand(this.input)) {
604
+ if (this.input.resume != null && this.checkpointer == null) {
605
+ throw new Error("Cannot use Command(resume=...) without checkpointer");
606
+ }
588
607
  const writes = {};
589
608
  // group writes by task id
590
609
  for (const [tid, key, value] of mapCommand(this.input, this.checkpointPendingWrites)) {
@@ -649,10 +668,10 @@ export class PregelLoop {
649
668
  });
650
669
  }
651
670
  // done with input
652
- this.input = isResuming ? INPUT_RESUMING : INPUT_DONE;
671
+ this.input = this.input === INPUT_RESUMING ? INPUT_RESUMING : INPUT_DONE;
653
672
  if (!this.isNested) {
654
673
  this.config = patchConfigurable(this.config, {
655
- [CONFIG_KEY_RESUMING]: isResuming,
674
+ [CONFIG_KEY_RESUMING]: this.input === INPUT_RESUMING,
656
675
  });
657
676
  }
658
677
  }
@@ -713,4 +732,20 @@ export class PregelLoop {
713
732
  }
714
733
  this.step += 1;
715
734
  }
735
+ _matchWrites(tasks) {
736
+ for (const [tid, k, v] of this.checkpointPendingWrites) {
737
+ if (k === ERROR || k === INTERRUPT || k === RESUME) {
738
+ continue;
739
+ }
740
+ const task = Object.values(tasks).find((t) => t.id === tid);
741
+ if (task) {
742
+ task.writes.push([k, v]);
743
+ }
744
+ }
745
+ for (const task of Object.values(tasks)) {
746
+ if (task.writes.length > 0) {
747
+ this._outputWrites(task.id, task.writes, true);
748
+ }
749
+ }
750
+ }
716
751
  }
@@ -3,7 +3,7 @@ import { BaseMessage } from "@langchain/core/messages";
3
3
  import { Serialized } from "@langchain/core/load/serializable";
4
4
  import { LLMResult } from "@langchain/core/outputs";
5
5
  import { ChainValues } from "@langchain/core/utils/types";
6
- import { StreamChunk } from "./loop.js";
6
+ import { StreamChunk } from "./stream.js";
7
7
  type Meta = [string[], Record<string, any>];
8
8
  /**
9
9
  * A callback handler that implements stream_mode=messages.