@langchain/langgraph 0.3.5 → 0.3.7

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 (66) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/channels/base.cjs +2 -2
  3. package/dist/channels/base.d.ts +3 -1
  4. package/dist/channels/base.js +2 -2
  5. package/dist/channels/base.js.map +1 -1
  6. package/dist/constants.cjs +3 -1
  7. package/dist/constants.d.ts +3 -0
  8. package/dist/constants.js +2 -0
  9. package/dist/constants.js.map +1 -1
  10. package/dist/errors.cjs +1 -6
  11. package/dist/errors.js +1 -6
  12. package/dist/errors.js.map +1 -1
  13. package/dist/graph/state.d.ts +1 -1
  14. package/dist/hash.cjs +333 -0
  15. package/dist/hash.d.ts +2 -0
  16. package/dist/hash.js +329 -0
  17. package/dist/hash.js.map +1 -0
  18. package/dist/interrupt.cjs +11 -1
  19. package/dist/interrupt.js +13 -3
  20. package/dist/interrupt.js.map +1 -1
  21. package/dist/prebuilt/react_agent_executor.cjs +78 -12
  22. package/dist/prebuilt/react_agent_executor.d.ts +4 -3
  23. package/dist/prebuilt/react_agent_executor.js +77 -12
  24. package/dist/prebuilt/react_agent_executor.js.map +1 -1
  25. package/dist/pregel/algo.cjs +29 -14
  26. package/dist/pregel/algo.js +30 -15
  27. package/dist/pregel/algo.js.map +1 -1
  28. package/dist/pregel/debug.cjs +18 -42
  29. package/dist/pregel/debug.d.ts +18 -33
  30. package/dist/pregel/debug.js +18 -42
  31. package/dist/pregel/debug.js.map +1 -1
  32. package/dist/pregel/index.cjs +77 -23
  33. package/dist/pregel/index.d.ts +7 -2
  34. package/dist/pregel/index.js +78 -24
  35. package/dist/pregel/index.js.map +1 -1
  36. package/dist/pregel/loop.cjs +157 -43
  37. package/dist/pregel/loop.d.ts +7 -1
  38. package/dist/pregel/loop.js +158 -44
  39. package/dist/pregel/loop.js.map +1 -1
  40. package/dist/pregel/messages.cjs +3 -0
  41. package/dist/pregel/messages.d.ts +1 -1
  42. package/dist/pregel/messages.js +3 -0
  43. package/dist/pregel/messages.js.map +1 -1
  44. package/dist/pregel/messages.test.cjs +18 -0
  45. package/dist/pregel/messages.test.js +18 -0
  46. package/dist/pregel/messages.test.js.map +1 -1
  47. package/dist/pregel/remote.cjs +2 -0
  48. package/dist/pregel/remote.d.ts +1 -0
  49. package/dist/pregel/remote.js +2 -0
  50. package/dist/pregel/remote.js.map +1 -1
  51. package/dist/pregel/retry.cjs +4 -0
  52. package/dist/pregel/retry.js +4 -0
  53. package/dist/pregel/retry.js.map +1 -1
  54. package/dist/pregel/types.d.ts +49 -4
  55. package/dist/pregel/types.js.map +1 -1
  56. package/dist/pregel/utils/config.cjs +2 -1
  57. package/dist/pregel/utils/config.js +2 -1
  58. package/dist/pregel/utils/config.js.map +1 -1
  59. package/dist/pregel/validate.cjs +1 -1
  60. package/dist/pregel/validate.js +1 -1
  61. package/dist/pregel/validate.js.map +1 -1
  62. package/dist/pregel/validate.test.cjs +1 -1
  63. package/dist/pregel/validate.test.js +1 -1
  64. package/dist/pregel/validate.test.js.map +1 -1
  65. package/dist/utils.js.map +1 -1
  66. package/package.json +6 -5
@@ -11,6 +11,7 @@ const errors_js_1 = require("../errors.cjs");
11
11
  const index_js_1 = require("./utils/index.cjs");
12
12
  const debug_js_1 = require("./debug.cjs");
13
13
  const stream_js_1 = require("./stream.cjs");
14
+ const hash_js_1 = require("../hash.cjs");
14
15
  const INPUT_DONE = Symbol.for("INPUT_DONE");
15
16
  const INPUT_RESUMING = Symbol.for("INPUT_RESUMING");
16
17
  const DEFAULT_LOOP_LIMIT = 25;
@@ -129,6 +130,12 @@ class PregelLoop {
129
130
  writable: true,
130
131
  value: void 0
131
132
  });
133
+ Object.defineProperty(this, "checkpointIdSaved", {
134
+ enumerable: true,
135
+ configurable: true,
136
+ writable: true,
137
+ value: void 0
138
+ });
132
139
  Object.defineProperty(this, "checkpointConfig", {
133
140
  enumerable: true,
134
141
  configurable: true,
@@ -171,6 +178,12 @@ class PregelLoop {
171
178
  writable: true,
172
179
  value: void 0
173
180
  });
181
+ Object.defineProperty(this, "checkpointDuring", {
182
+ enumerable: true,
183
+ configurable: true,
184
+ writable: true,
185
+ value: void 0
186
+ });
174
187
  Object.defineProperty(this, "outputKeys", {
175
188
  enumerable: true,
176
189
  configurable: true,
@@ -201,6 +214,12 @@ class PregelLoop {
201
214
  writable: true,
202
215
  value: void 0
203
216
  });
217
+ Object.defineProperty(this, "prevCheckpoint", {
218
+ enumerable: true,
219
+ configurable: true,
220
+ writable: true,
221
+ value: void 0
222
+ });
204
223
  Object.defineProperty(this, "status", {
205
224
  enumerable: true,
206
225
  configurable: true,
@@ -320,6 +339,7 @@ class PregelLoop {
320
339
  this.prevCheckpointConfig = params.prevCheckpointConfig;
321
340
  this.interruptAfter = params.interruptAfter;
322
341
  this.interruptBefore = params.interruptBefore;
342
+ this.checkpointDuring = params.checkpointDuring;
323
343
  this.debug = params.debug;
324
344
  this.triggerToNodes = params.triggerToNodes;
325
345
  }
@@ -422,6 +442,7 @@ class PregelLoop {
422
442
  cache: params.cache,
423
443
  interruptAfter: params.interruptAfter,
424
444
  interruptBefore: params.interruptBefore,
445
+ checkpointDuring: params.checkpointDuring,
425
446
  debug: params.debug,
426
447
  triggerToNodes: params.triggerToNodes,
427
448
  });
@@ -446,22 +467,17 @@ class PregelLoop {
446
467
  */
447
468
  putWrites(taskId, writes) {
448
469
  let writesCopy = writes;
449
- if (writesCopy.length === 0) {
470
+ if (writesCopy.length === 0)
450
471
  return;
451
- }
452
472
  // deduplicate writes to special channels, last write wins
453
473
  if (writesCopy.every(([key]) => key in langgraph_checkpoint_1.WRITES_IDX_MAP)) {
454
474
  writesCopy = Array.from(new Map(writesCopy.map((w) => [w[0], w])).values());
455
475
  }
476
+ // remove existing writes for this task
477
+ this.checkpointPendingWrites = this.checkpointPendingWrites.filter((w) => w[0] !== taskId);
456
478
  // save writes
457
479
  for (const [c, v] of writesCopy) {
458
- const idx = this.checkpointPendingWrites.findIndex((w) => w[0] === taskId && w[1] === c);
459
- if (c in langgraph_checkpoint_1.WRITES_IDX_MAP && idx !== -1) {
460
- this.checkpointPendingWrites[idx] = [taskId, c, v];
461
- }
462
- else {
463
- this.checkpointPendingWrites.push([taskId, c, v]);
464
- }
480
+ this.checkpointPendingWrites.push([taskId, c, v]);
465
481
  }
466
482
  const putWritePromise = this.checkpointer?.putWrites({
467
483
  ...this.checkpointConfig,
@@ -504,13 +520,27 @@ class PregelLoop {
504
520
  (task.config.tags ?? []).includes(constants_js_1.TAG_HIDDEN)) {
505
521
  return;
506
522
  }
507
- if (writes.length > 0 &&
508
- writes[0][0] !== constants_js_1.ERROR &&
509
- writes[0][0] !== constants_js_1.INTERRUPT) {
510
- this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputUpdates)(this.outputKeys, [[task, writes]], cached), "updates")));
523
+ if (writes.length > 0) {
524
+ if (writes[0][0] === constants_js_1.INTERRUPT) {
525
+ // in `algo.ts` we append a bool to the task path to indicate
526
+ // whether or not a call was present. If so, we don't emit the
527
+ // the interrupt as it'll be emitted by the parent.
528
+ if (task.path?.[0] === constants_js_1.PUSH && task.path?.at(-1) === true)
529
+ return;
530
+ const interruptWrites = writes
531
+ .filter((w) => w[0] === constants_js_1.INTERRUPT)
532
+ .flatMap((w) => w[1]);
533
+ this._emit([
534
+ ["updates", { [constants_js_1.INTERRUPT]: interruptWrites }],
535
+ ["values", { [constants_js_1.INTERRUPT]: interruptWrites }],
536
+ ]);
537
+ }
538
+ else if (writes[0][0] !== constants_js_1.ERROR) {
539
+ this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputUpdates)(this.outputKeys, [[task, writes]], cached), "updates")));
540
+ }
511
541
  }
512
542
  if (!cached) {
513
- this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTaskResults)(this.step, [[task, writes]], this.streamKeys), "debug")));
543
+ this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTaskResults)([[task, writes]], this.streamKeys), "tasks")));
514
544
  }
515
545
  }
516
546
  }
@@ -607,8 +637,7 @@ class PregelLoop {
607
637
  this.tasks = nextTasks;
608
638
  // Produce debug output
609
639
  if (this.checkpointer) {
610
- this._emit(await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugCheckpoint)(this.step - 1, // printing checkpoint for previous step
611
- this.checkpointConfig, this.channels, this.streamKeys, this.checkpointMetadata, Object.values(this.tasks), this.checkpointPendingWrites, this.prevCheckpointConfig, this.outputKeys), "debug")));
640
+ this._emit(await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugCheckpoint)(this.checkpointConfig, this.channels, this.streamKeys, this.checkpointMetadata, Object.values(this.tasks), this.checkpointPendingWrites, this.prevCheckpointConfig, this.outputKeys), "checkpoints")));
612
641
  }
613
642
  if (Object.values(this.tasks).length === 0) {
614
643
  this.status = "done";
@@ -641,11 +670,22 @@ class PregelLoop {
641
670
  throw new errors_js_1.GraphInterrupt();
642
671
  }
643
672
  // Produce debug output
644
- const debugOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTasks)(this.step, Object.values(this.tasks)), "debug"));
673
+ const debugOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTasks)(Object.values(this.tasks)), "tasks"));
645
674
  this._emit(debugOutput);
646
675
  return true;
647
676
  }
648
677
  async finishAndHandleError(error) {
678
+ // persist current checkpoint and writes
679
+ if (!this.checkpointDuring &&
680
+ // if it's a top graph
681
+ (!this.isNested ||
682
+ // or a nested graph with error or interrupt
683
+ typeof error !== "undefined" ||
684
+ // or a nested graph with checkpointer: true
685
+ this.checkpointNamespace.every((part) => !part.includes(constants_js_1.CHECKPOINT_NAMESPACE_END)))) {
686
+ this._putCheckpoint(this.checkpointMetadata);
687
+ this._flushPendingWrites();
688
+ }
649
689
  const suppress = this._suppressInterrupt(error);
650
690
  if (suppress || error === undefined) {
651
691
  this.output = (0, io_js_1.readChannels)(this.channels, this.outputKeys);
@@ -662,11 +702,12 @@ class PregelLoop {
662
702
  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")));
663
703
  }
664
704
  // Emit INTERRUPT event
665
- const interrupts = { [constants_js_1.INTERRUPT]: error.interrupts };
666
- this._emit([
667
- ["updates", interrupts],
668
- ["values", interrupts],
669
- ]);
705
+ if ((0, errors_js_1.isGraphInterrupt)(error) && !error.interrupts.length) {
706
+ this._emit([
707
+ ["updates", { [constants_js_1.INTERRUPT]: [] }],
708
+ ["values", { [constants_js_1.INTERRUPT]: [] }],
709
+ ]);
710
+ }
670
711
  }
671
712
  return suppress;
672
713
  }
@@ -690,7 +731,7 @@ class PregelLoop {
690
731
  this.toInterrupt.push(pushed);
691
732
  return;
692
733
  }
693
- this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTasks)(this.step, [pushed]), "debug")));
734
+ this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTasks)([pushed]), "tasks")));
694
735
  if (this.debug)
695
736
  (0, debug_js_1.printStepTasks)(this.step, [pushed]);
696
737
  this.tasks[pushed.id] = pushed;
@@ -717,17 +758,22 @@ class PregelLoop {
717
758
  if (scratchpad && scratchpad.nullResume !== undefined) {
718
759
  this.putWrites(constants_js_1.NULL_TASK_ID, [[constants_js_1.RESUME, scratchpad.nullResume]]);
719
760
  }
761
+ // map command to writes
720
762
  if ((0, constants_js_1.isCommand)(this.input)) {
721
763
  const hasResume = this.input.resume != null;
764
+ if (this.input.resume != null &&
765
+ typeof this.input.resume === "object" &&
766
+ Object.keys(this.input.resume).every(hash_js_1.isXXH3)) {
767
+ this.config.configurable ??= {};
768
+ this.config.configurable[constants_js_1.CONFIG_KEY_RESUME_MAP] = this.input.resume;
769
+ }
722
770
  if (hasResume && this.checkpointer == null) {
723
771
  throw new Error("Cannot use Command(resume=...) without checkpointer");
724
772
  }
725
773
  const writes = {};
726
774
  // group writes by task id
727
775
  for (const [tid, key, value] of (0, io_js_1.mapCommand)(this.input, this.checkpointPendingWrites)) {
728
- if (writes[tid] === undefined) {
729
- writes[tid] = [];
730
- }
776
+ writes[tid] ??= [];
731
777
  writes[tid].push([key, value]);
732
778
  }
733
779
  if (Object.keys(writes).length === 0) {
@@ -810,33 +856,50 @@ class PregelLoop {
810
856
  }
811
857
  }
812
858
  _emit(values) {
813
- for (const chunk of values) {
814
- if (this.stream.modes.has(chunk[0])) {
815
- this.stream.push([this.checkpointNamespace, ...chunk]);
859
+ for (const [mode, payload] of values) {
860
+ if (this.stream.modes.has(mode)) {
861
+ this.stream.push([this.checkpointNamespace, mode, payload]);
862
+ }
863
+ // debug mode is a "checkpoints" or "tasks" wrapped in an object
864
+ // TODO: consider deprecating this in 1.x
865
+ if ((mode === "checkpoints" || mode === "tasks") &&
866
+ this.stream.modes.has("debug")) {
867
+ const step = mode === "checkpoints" ? this.step - 1 : this.step;
868
+ const timestamp = new Date().toISOString();
869
+ const type = (() => {
870
+ if (mode === "checkpoints") {
871
+ return "checkpoint";
872
+ }
873
+ else if (typeof payload === "object" &&
874
+ payload != null &&
875
+ "result" in payload) {
876
+ return "task_result";
877
+ }
878
+ else {
879
+ return "task";
880
+ }
881
+ })();
882
+ this.stream.push([
883
+ this.checkpointNamespace,
884
+ "debug",
885
+ { step, type, timestamp, payload },
886
+ ]);
816
887
  }
817
888
  }
818
889
  }
819
- async _putCheckpoint(inputMetadata) {
820
- // Assign step
821
- const metadata = {
822
- ...inputMetadata,
823
- step: this.step,
824
- parents: this.config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINT_MAP] ?? {},
825
- };
826
- // Bail if no checkpointer
827
- if (this.checkpointer !== undefined) {
890
+ _putCheckpoint(inputMetadata) {
891
+ const exiting = this.checkpointMetadata === inputMetadata;
892
+ const doCheckpoint = this.checkpointer != null && (this.checkpointDuring || exiting);
893
+ const storeCheckpoint = (checkpoint) => {
828
894
  // store the previous checkpoint config for debug events
829
895
  this.prevCheckpointConfig = this.checkpointConfig?.configurable
830
896
  ?.checkpoint_id
831
897
  ? this.checkpointConfig
832
898
  : undefined;
833
- // create new checkpoint
834
- this.checkpointMetadata = metadata;
835
899
  // child graphs keep at most one checkpoint per parent checkpoint
836
900
  // this is achieved by writing child checkpoints as progress is made
837
901
  // (so that error recovery / resuming from interrupt don't lose work)
838
902
  // but doing so always with an id equal to that of the parent checkpoint
839
- this.checkpoint = (0, base_js_1.createCheckpoint)(this.checkpoint, this.channels, this.step);
840
903
  this.checkpointConfig = {
841
904
  ...this.checkpointConfig,
842
905
  configurable: {
@@ -852,7 +915,7 @@ class PregelLoop {
852
915
  // ensuring checkpointers receive checkpoints in order
853
916
  void this._checkpointerPutAfterPrevious({
854
917
  config: { ...this.checkpointConfig },
855
- checkpoint: (0, langgraph_checkpoint_1.copyCheckpoint)(this.checkpoint),
918
+ checkpoint: (0, langgraph_checkpoint_1.copyCheckpoint)(checkpoint),
856
919
  metadata: { ...this.checkpointMetadata },
857
920
  newVersions,
858
921
  });
@@ -863,8 +926,59 @@ class PregelLoop {
863
926
  checkpoint_id: this.checkpoint.id,
864
927
  },
865
928
  };
929
+ };
930
+ // We need to retroactively store the previous checkpoint
931
+ // if it turns out that pending sends are scheduled.
932
+ // TODO: remove when `pending_sends` is removed from checkpoints
933
+ if (!exiting &&
934
+ !this.checkpointDuring &&
935
+ this.checkpointer != null &&
936
+ this.prevCheckpoint != null &&
937
+ this.checkpoint.pending_sends.length > 0) {
938
+ storeCheckpoint(this.prevCheckpoint);
939
+ }
940
+ if (!exiting) {
941
+ this.checkpointMetadata = {
942
+ ...inputMetadata,
943
+ step: this.step,
944
+ parents: this.config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINT_MAP] ?? {},
945
+ };
946
+ }
947
+ // Store previous checkpoint in case pending_sends are scheduled
948
+ // for next checkpoint.
949
+ if (!this.checkpointDuring) {
950
+ this.prevCheckpoint = this.checkpoint;
951
+ }
952
+ // create new checkpoint
953
+ this.checkpoint = (0, base_js_1.createCheckpoint)(this.checkpoint, doCheckpoint ? this.channels : undefined, this.step, exiting ? { id: this.checkpoint.id } : undefined);
954
+ // Bail if no checkpointer
955
+ if (doCheckpoint)
956
+ storeCheckpoint(this.checkpoint);
957
+ if (!exiting) {
958
+ // increment step
959
+ this.step += 1;
960
+ }
961
+ }
962
+ _flushPendingWrites() {
963
+ if (this.checkpointer == null)
964
+ return;
965
+ if (this.checkpointPendingWrites.length === 0)
966
+ return;
967
+ // patch config
968
+ const config = (0, index_js_1.patchConfigurable)(this.checkpointConfig, {
969
+ [constants_js_1.CONFIG_KEY_CHECKPOINT_NS]: this.config.configurable?.checkpoint_ns ?? "",
970
+ [constants_js_1.CONFIG_KEY_CHECKPOINT_ID]: this.checkpoint.id,
971
+ });
972
+ // group writes by task id
973
+ const byTask = {};
974
+ for (const [tid, key, value] of this.checkpointPendingWrites) {
975
+ byTask[tid] ??= [];
976
+ byTask[tid].push([key, value]);
977
+ }
978
+ // submit writes to checkpointer
979
+ for (const [tid, ws] of Object.entries(byTask)) {
980
+ this.checkpointerPromises.push(this.checkpointer.putWrites(config, ws, tid));
866
981
  }
867
- this.step += 1;
868
982
  }
869
983
  _matchWrites(tasks) {
870
984
  for (const [tid, k, v] of this.checkpointPendingWrites) {
@@ -22,6 +22,7 @@ export type PregelLoopInitializeParams = {
22
22
  cache?: BaseCache<PendingWrite<string>[]>;
23
23
  interruptAfter: string[] | All;
24
24
  interruptBefore: string[] | All;
25
+ checkpointDuring: boolean;
25
26
  manager?: CallbackManagerForChainRun;
26
27
  debug: boolean;
27
28
  triggerToNodes: Record<string, string[]>;
@@ -52,6 +53,7 @@ type PregelLoopParams = {
52
53
  prevCheckpointConfig: RunnableConfig | undefined;
53
54
  interruptAfter: string[] | All;
54
55
  interruptBefore: string[] | All;
56
+ checkpointDuring: boolean;
55
57
  debug: boolean;
56
58
  triggerToNodes: Record<string, string[]>;
57
59
  };
@@ -81,6 +83,7 @@ export declare class PregelLoop {
81
83
  channels: Record<string, BaseChannel>;
82
84
  managed: ManagedValueMapping;
83
85
  protected checkpoint: Checkpoint;
86
+ protected checkpointIdSaved: string | undefined;
84
87
  protected checkpointConfig: RunnableConfig;
85
88
  checkpointMetadata: CheckpointMetadata;
86
89
  protected checkpointNamespace: string[];
@@ -88,11 +91,13 @@ export declare class PregelLoop {
88
91
  protected checkpointPreviousVersions: Record<string, string | number>;
89
92
  step: number;
90
93
  protected stop: number;
94
+ protected checkpointDuring: boolean;
91
95
  protected outputKeys: string | string[];
92
96
  protected streamKeys: string | string[];
93
97
  protected nodes: Record<string, PregelNode>;
94
98
  protected skipDoneTasks: boolean;
95
99
  protected prevCheckpointConfig: RunnableConfig | undefined;
100
+ protected prevCheckpoint: Checkpoint | undefined;
96
101
  status: "pending" | "done" | "interrupt_before" | "interrupt_after" | "out_of_steps";
97
102
  tasks: Record<string, PregelExecutableTask<any, any>>;
98
103
  stream: IterableReadableWritableStream;
@@ -141,7 +146,8 @@ export declare class PregelLoop {
141
146
  protected _suppressInterrupt(e?: Error): boolean;
142
147
  protected _first(inputKeys: string | string[]): Promise<void>;
143
148
  protected _emit(values: [StreamMode, unknown][]): void;
144
- protected _putCheckpoint(inputMetadata: Omit<CheckpointMetadata, "step" | "parents">): Promise<void>;
149
+ protected _putCheckpoint(inputMetadata: Omit<CheckpointMetadata, "step" | "parents">): void;
150
+ protected _flushPendingWrites(): void;
145
151
  protected _matchWrites(tasks: Record<string, PregelExecutableTask<string, string>>): void;
146
152
  }
147
153
  export {};