@langchain/langgraph 0.3.5 → 0.3.6

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 (52) hide show
  1. package/dist/channels/base.cjs +2 -2
  2. package/dist/channels/base.d.ts +3 -1
  3. package/dist/channels/base.js +2 -2
  4. package/dist/channels/base.js.map +1 -1
  5. package/dist/constants.cjs +3 -1
  6. package/dist/constants.d.ts +3 -0
  7. package/dist/constants.js +2 -0
  8. package/dist/constants.js.map +1 -1
  9. package/dist/errors.cjs +1 -6
  10. package/dist/errors.js +1 -6
  11. package/dist/errors.js.map +1 -1
  12. package/dist/graph/state.d.ts +1 -1
  13. package/dist/hash.cjs +333 -0
  14. package/dist/hash.d.ts +2 -0
  15. package/dist/hash.js +329 -0
  16. package/dist/hash.js.map +1 -0
  17. package/dist/interrupt.cjs +8 -1
  18. package/dist/interrupt.js +8 -1
  19. package/dist/interrupt.js.map +1 -1
  20. package/dist/prebuilt/react_agent_executor.d.ts +2 -2
  21. package/dist/pregel/algo.cjs +29 -14
  22. package/dist/pregel/algo.js +30 -15
  23. package/dist/pregel/algo.js.map +1 -1
  24. package/dist/pregel/debug.cjs +18 -42
  25. package/dist/pregel/debug.d.ts +18 -33
  26. package/dist/pregel/debug.js +18 -42
  27. package/dist/pregel/debug.js.map +1 -1
  28. package/dist/pregel/index.cjs +76 -22
  29. package/dist/pregel/index.d.ts +7 -2
  30. package/dist/pregel/index.js +77 -23
  31. package/dist/pregel/index.js.map +1 -1
  32. package/dist/pregel/loop.cjs +157 -43
  33. package/dist/pregel/loop.d.ts +7 -1
  34. package/dist/pregel/loop.js +158 -44
  35. package/dist/pregel/loop.js.map +1 -1
  36. package/dist/pregel/remote.cjs +2 -0
  37. package/dist/pregel/remote.d.ts +1 -0
  38. package/dist/pregel/remote.js +2 -0
  39. package/dist/pregel/remote.js.map +1 -1
  40. package/dist/pregel/types.d.ts +49 -4
  41. package/dist/pregel/types.js.map +1 -1
  42. package/dist/pregel/utils/config.cjs +2 -1
  43. package/dist/pregel/utils/config.js +2 -1
  44. package/dist/pregel/utils/config.js.map +1 -1
  45. package/dist/pregel/validate.cjs +1 -1
  46. package/dist/pregel/validate.js +1 -1
  47. package/dist/pregel/validate.js.map +1 -1
  48. package/dist/pregel/validate.test.cjs +1 -1
  49. package/dist/pregel/validate.test.js +1 -1
  50. package/dist/pregel/validate.test.js.map +1 -1
  51. package/dist/utils.js.map +1 -1
  52. package/package.json +4 -4
@@ -862,7 +862,7 @@ class Pregel extends PartialRunnable {
862
862
  // tasks for this checkpoint
863
863
  const nextTasks = (0, algo_js_1._prepareNextTasks)(checkpoint, saved.pendingWrites || [], this.nodes, channels, managed, saved.config, true, {
864
864
  step: (saved.metadata?.step ?? -1) + 1,
865
- checkpointer: this.checkpointer || undefined,
865
+ checkpointer,
866
866
  store: this.store,
867
867
  });
868
868
  // apply null writes
@@ -870,13 +870,13 @@ class Pregel extends PartialRunnable {
870
870
  .filter((w) => w[0] === constants_js_1.NULL_TASK_ID)
871
871
  .map((w) => w.slice(1));
872
872
  if (nullWrites.length > 0) {
873
- (0, algo_js_1._applyWrites)(saved.checkpoint, channels, [
873
+ (0, algo_js_1._applyWrites)(checkpoint, channels, [
874
874
  {
875
875
  name: constants_js_1.INPUT,
876
876
  writes: nullWrites,
877
877
  triggers: [],
878
878
  },
879
- ], undefined, this.triggerToNodes);
879
+ ], checkpointer.getNextVersion.bind(checkpointer), this.triggerToNodes);
880
880
  }
881
881
  // apply writes from tasks that already ran
882
882
  for (const [taskId, k, v] of saved.pendingWrites || []) {
@@ -889,29 +889,61 @@ class Pregel extends PartialRunnable {
889
889
  nextTasks[taskId].writes.push([k, v]);
890
890
  }
891
891
  // clear all current tasks
892
- (0, algo_js_1._applyWrites)(checkpoint, channels, Object.values(nextTasks), undefined, this.triggerToNodes);
892
+ (0, algo_js_1._applyWrites)(checkpoint, channels, Object.values(nextTasks), checkpointer.getNextVersion.bind(checkpointer), this.triggerToNodes);
893
893
  }
894
894
  // save checkpoint
895
- const nextConfig = await checkpointer.put(checkpointConfig, (0, base_js_1.createCheckpoint)(checkpoint, undefined, step), {
895
+ const nextConfig = await checkpointer.put(checkpointConfig, (0, base_js_1.createCheckpoint)(checkpoint, channels, step), {
896
896
  ...checkpointMetadata,
897
897
  source: "update",
898
898
  step: step + 1,
899
899
  writes: {},
900
900
  parents: saved?.metadata?.parents ?? {},
901
- }, {});
901
+ }, (0, index_js_1.getNewChannelVersions)(checkpointPreviousVersions, checkpoint.channel_versions));
902
902
  return (0, index_js_1.patchCheckpointMap)(nextConfig, saved ? saved.metadata : undefined);
903
903
  }
904
- if (values == null && asNode === constants_js_1.COPY) {
904
+ if (asNode === constants_js_1.COPY) {
905
905
  if (updates.length > 1) {
906
906
  throw new errors_js_1.InvalidUpdateError(`Cannot copy checkpoint with multiple updates`);
907
907
  }
908
- const nextConfig = await checkpointer.put(saved?.parentConfig ?? checkpointConfig, (0, base_js_1.createCheckpoint)(checkpoint, undefined, step), {
908
+ if (saved == null) {
909
+ throw new errors_js_1.InvalidUpdateError(`Cannot copy a non-existent checkpoint`);
910
+ }
911
+ const isCopyWithUpdates = (values) => {
912
+ if (!Array.isArray(values))
913
+ return false;
914
+ if (values.length === 0)
915
+ return false;
916
+ return values.every((v) => Array.isArray(v) && v.length === 2);
917
+ };
918
+ const nextCheckpoint = (0, base_js_1.createCheckpoint)(checkpoint, undefined, step);
919
+ const nextConfig = await checkpointer.put(saved.parentConfig ??
920
+ (0, utils_js_1.patchConfigurable)(saved.config, { checkpoint_id: undefined }), nextCheckpoint, {
909
921
  source: "fork",
910
922
  step: step + 1,
911
923
  writes: {},
912
- parents: saved?.metadata?.parents ?? {},
924
+ parents: saved.metadata?.parents ?? {},
913
925
  }, {});
914
- return (0, index_js_1.patchCheckpointMap)(nextConfig, saved ? saved.metadata : undefined);
926
+ // We want to both clone a checkpoint and update state in one go.
927
+ // Reuse the same task ID if possible.
928
+ if (isCopyWithUpdates(values)) {
929
+ // figure out the task IDs for the next update checkpoint
930
+ const nextTasks = (0, algo_js_1._prepareNextTasks)(nextCheckpoint, saved.pendingWrites, this.nodes, channels, managed, nextConfig, false, { step: step + 2 });
931
+ const tasksGroupBy = Object.values(nextTasks).reduce((acc, { name, id }) => {
932
+ acc[name] ??= [];
933
+ acc[name].push({ id });
934
+ return acc;
935
+ }, {});
936
+ const userGroupBy = values.reduce((acc, item) => {
937
+ const [values, asNode] = item;
938
+ acc[asNode] ??= [];
939
+ const targetIdx = acc[asNode].length;
940
+ const taskId = tasksGroupBy[asNode]?.[targetIdx]?.id;
941
+ acc[asNode].push({ values, asNode, taskId });
942
+ return acc;
943
+ }, {});
944
+ return updateSuperStep((0, index_js_1.patchCheckpointMap)(nextConfig, saved.metadata), Object.values(userGroupBy).flat());
945
+ }
946
+ return (0, index_js_1.patchCheckpointMap)(nextConfig, saved.metadata);
915
947
  }
916
948
  if (asNode === constants_js_1.INPUT) {
917
949
  if (updates.length > 1) {
@@ -983,7 +1015,7 @@ class Pregel extends PartialRunnable {
983
1015
  const validUpdates = [];
984
1016
  if (updates.length === 1) {
985
1017
  // eslint-disable-next-line prefer-const
986
- let { values, asNode } = updates[0];
1018
+ let { values, asNode, taskId } = updates[0];
987
1019
  if (asNode === undefined && Object.keys(this.nodes).length === 1) {
988
1020
  // if only one node, use it
989
1021
  [asNode] = Object.keys(this.nodes);
@@ -1019,18 +1051,18 @@ class Pregel extends PartialRunnable {
1019
1051
  if (asNode === undefined) {
1020
1052
  throw new errors_js_1.InvalidUpdateError(`Ambiguous update, specify "asNode"`);
1021
1053
  }
1022
- validUpdates.push({ values, asNode });
1054
+ validUpdates.push({ values, asNode, taskId });
1023
1055
  }
1024
1056
  else {
1025
- for (const { asNode, values } of updates) {
1057
+ for (const { asNode, values, taskId } of updates) {
1026
1058
  if (asNode == null) {
1027
1059
  throw new errors_js_1.InvalidUpdateError(`"asNode" is required when applying multiple updates`);
1028
1060
  }
1029
- validUpdates.push({ values, asNode });
1061
+ validUpdates.push({ values, asNode, taskId });
1030
1062
  }
1031
1063
  }
1032
1064
  const tasks = [];
1033
- for (const { asNode, values } of validUpdates) {
1065
+ for (const { asNode, values, taskId } of validUpdates) {
1034
1066
  if (this.nodes[asNode] === undefined) {
1035
1067
  throw new errors_js_1.InvalidUpdateError(`Node "${asNode.toString()}" does not exist`);
1036
1068
  }
@@ -1050,7 +1082,7 @@ class Pregel extends PartialRunnable {
1050
1082
  : writers[0],
1051
1083
  writes: [],
1052
1084
  triggers: [constants_js_1.INTERRUPT],
1053
- id: (0, langgraph_checkpoint_1.uuid5)(constants_js_1.INTERRUPT, checkpoint.id),
1085
+ id: taskId ?? (0, langgraph_checkpoint_1.uuid5)(constants_js_1.INTERRUPT, checkpoint.id),
1054
1086
  writers: [],
1055
1087
  });
1056
1088
  }
@@ -1139,6 +1171,8 @@ class Pregel extends PartialRunnable {
1139
1171
  * - checkpointer
1140
1172
  * - store
1141
1173
  * - whether stream mode is single
1174
+ * - node cache
1175
+ * - whether checkpoint during is enabled
1142
1176
  * @internal
1143
1177
  */
1144
1178
  _defaults(config) {
@@ -1167,13 +1201,16 @@ class Pregel extends PartialRunnable {
1167
1201
  streamModeSingle = typeof streamMode === "string";
1168
1202
  }
1169
1203
  else {
1170
- defaultStreamMode = this.streamMode;
1204
+ // if being called as a node in another graph, default to values mode
1205
+ // but don't overwrite `streamMode`if provided
1206
+ if (config.configurable?.[constants_js_1.CONFIG_KEY_TASK_ID] !== undefined) {
1207
+ defaultStreamMode = ["values"];
1208
+ }
1209
+ else {
1210
+ defaultStreamMode = this.streamMode;
1211
+ }
1171
1212
  streamModeSingle = true;
1172
1213
  }
1173
- // if being called as a node in another graph, always use values mode
1174
- if (config.configurable?.[constants_js_1.CONFIG_KEY_TASK_ID] !== undefined) {
1175
- defaultStreamMode = ["values"];
1176
- }
1177
1214
  let defaultCheckpointer;
1178
1215
  if (this.checkpointer === false) {
1179
1216
  defaultCheckpointer = undefined;
@@ -1182,11 +1219,17 @@ class Pregel extends PartialRunnable {
1182
1219
  config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINTER] !== undefined) {
1183
1220
  defaultCheckpointer = config.configurable[constants_js_1.CONFIG_KEY_CHECKPOINTER];
1184
1221
  }
1222
+ else if (this.checkpointer === true) {
1223
+ throw new Error("checkpointer: true cannot be used for root graphs.");
1224
+ }
1185
1225
  else {
1186
1226
  defaultCheckpointer = this.checkpointer;
1187
1227
  }
1188
1228
  const defaultStore = config.store ?? this.store;
1189
1229
  const defaultCache = config.cache ?? this.cache;
1230
+ const defaultCheckpointDuring = config.checkpointDuring ??
1231
+ config?.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINT_DURING] ??
1232
+ true;
1190
1233
  return [
1191
1234
  defaultDebug,
1192
1235
  defaultStreamMode,
@@ -1199,6 +1242,7 @@ class Pregel extends PartialRunnable {
1199
1242
  defaultStore,
1200
1243
  streamModeSingle,
1201
1244
  defaultCache,
1245
+ defaultCheckpointDuring,
1202
1246
  ];
1203
1247
  }
1204
1248
  /**
@@ -1345,11 +1389,20 @@ class Pregel extends PartialRunnable {
1345
1389
  const validInput = await this._validateInput(input);
1346
1390
  const { runId, ...restConfig } = inputConfig;
1347
1391
  // assign defaults
1348
- const [debug, streamMode, , outputKeys, config, interruptBefore, interruptAfter, checkpointer, store, streamModeSingle, cache,] = this._defaults(restConfig);
1392
+ const [debug, streamMode, , outputKeys, config, interruptBefore, interruptAfter, checkpointer, store, streamModeSingle, cache, checkpointDuring,] = this._defaults(restConfig);
1349
1393
  config.configurable = await this._validateConfigurable(config.configurable);
1350
1394
  const stream = new stream_js_1.IterableReadableWritableStream({
1351
1395
  modes: new Set(streamMode),
1352
1396
  });
1397
+ // set up subgraph checkpointing
1398
+ if (this.checkpointer === true) {
1399
+ config.configurable ??= {};
1400
+ const ns = config.configurable[constants_js_1.CONFIG_KEY_CHECKPOINT_NS] ?? "";
1401
+ config.configurable[constants_js_1.CONFIG_KEY_CHECKPOINT_NS] = ns
1402
+ .split(constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR)
1403
+ .map((part) => part.split(constants_js_1.CHECKPOINT_NAMESPACE_END)[0])
1404
+ .join(constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR);
1405
+ }
1353
1406
  // set up messages stream mode
1354
1407
  if (streamMode.includes("messages")) {
1355
1408
  const messageStreamer = new messages_js_1.StreamMessagesHandler((chunk) => stream.push(chunk));
@@ -1409,6 +1462,7 @@ class Pregel extends PartialRunnable {
1409
1462
  manager: runManager,
1410
1463
  debug: this.debug,
1411
1464
  triggerToNodes: this.triggerToNodes,
1465
+ checkpointDuring,
1412
1466
  });
1413
1467
  const runner = new runner_js_1.PregelRunner({
1414
1468
  loop,
@@ -222,7 +222,7 @@ export declare class Pregel<Nodes extends StrRecord<string, PregelNode>, Channel
222
222
  * When provided, saves a checkpoint of the graph state at every superstep.
223
223
  * When false or undefined, checkpointing is disabled, and the graph will not be able to save or restore state.
224
224
  */
225
- checkpointer?: BaseCheckpointSaver | false;
225
+ checkpointer?: BaseCheckpointSaver | boolean;
226
226
  /** Optional retry policy for handling failures in node execution */
227
227
  retryPolicy?: RetryPolicy;
228
228
  /** The default configuration for graph execution, can be overridden on a per-invocation basis */
@@ -412,6 +412,8 @@ export declare class Pregel<Nodes extends StrRecord<string, PregelNode>, Channel
412
412
  * - checkpointer
413
413
  * - store
414
414
  * - whether stream mode is single
415
+ * - node cache
416
+ * - whether checkpoint during is enabled
415
417
  * @internal
416
418
  */
417
419
  _defaults(config: PregelOptions<Nodes, Channels>): [
@@ -428,9 +430,12 @@ export declare class Pregel<Nodes extends StrRecord<string, PregelNode>, Channel
428
430
  All | string[],
429
431
  // interrupt after
430
432
  BaseCheckpointSaver | undefined,
433
+ // checkpointer
431
434
  BaseStore | undefined,
432
435
  boolean,
433
- BaseCache | undefined
436
+ // stream mode single
437
+ BaseCache | undefined,
438
+ boolean
434
439
  ];
435
440
  /**
436
441
  * Streams the execution of the graph, emitting state updates as they occur.
@@ -2,7 +2,7 @@
2
2
  import { _coerceToRunnable, getCallbackManagerForConfig, mergeConfigs, patchConfig, Runnable, RunnableSequence, } from "@langchain/core/runnables";
3
3
  import { compareChannelVersions, copyCheckpoint, emptyCheckpoint, SCHEDULED, uuid5, } from "@langchain/langgraph-checkpoint";
4
4
  import { createCheckpoint, emptyChannels, isBaseChannel, } from "../channels/base.js";
5
- import { CHECKPOINT_NAMESPACE_END, CHECKPOINT_NAMESPACE_SEPARATOR, CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_NODE_FINISHED, CONFIG_KEY_READ, CONFIG_KEY_SEND, CONFIG_KEY_STREAM, CONFIG_KEY_TASK_ID, COPY, END, ERROR, INPUT, INTERRUPT, isInterrupted, NULL_TASK_ID, PUSH, } from "../constants.js";
5
+ import { CHECKPOINT_NAMESPACE_END, CHECKPOINT_NAMESPACE_SEPARATOR, CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_NODE_FINISHED, CONFIG_KEY_READ, CONFIG_KEY_SEND, CONFIG_KEY_STREAM, CONFIG_KEY_TASK_ID, COPY, END, ERROR, INPUT, INTERRUPT, isInterrupted, NULL_TASK_ID, PUSH, CONFIG_KEY_CHECKPOINT_DURING, CONFIG_KEY_CHECKPOINT_NS, } from "../constants.js";
6
6
  import { GraphRecursionError, GraphValueError, InvalidUpdateError, } from "../errors.js";
7
7
  import { ChannelKeyPlaceholder, isConfiguredManagedValue, ManagedValueMapping, NoopManagedValue, } from "../managed/base.js";
8
8
  import { gatherIterator, patchConfigurable } from "../utils.js";
@@ -858,7 +858,7 @@ export class Pregel extends PartialRunnable {
858
858
  // tasks for this checkpoint
859
859
  const nextTasks = _prepareNextTasks(checkpoint, saved.pendingWrites || [], this.nodes, channels, managed, saved.config, true, {
860
860
  step: (saved.metadata?.step ?? -1) + 1,
861
- checkpointer: this.checkpointer || undefined,
861
+ checkpointer,
862
862
  store: this.store,
863
863
  });
864
864
  // apply null writes
@@ -866,13 +866,13 @@ export class Pregel extends PartialRunnable {
866
866
  .filter((w) => w[0] === NULL_TASK_ID)
867
867
  .map((w) => w.slice(1));
868
868
  if (nullWrites.length > 0) {
869
- _applyWrites(saved.checkpoint, channels, [
869
+ _applyWrites(checkpoint, channels, [
870
870
  {
871
871
  name: INPUT,
872
872
  writes: nullWrites,
873
873
  triggers: [],
874
874
  },
875
- ], undefined, this.triggerToNodes);
875
+ ], checkpointer.getNextVersion.bind(checkpointer), this.triggerToNodes);
876
876
  }
877
877
  // apply writes from tasks that already ran
878
878
  for (const [taskId, k, v] of saved.pendingWrites || []) {
@@ -885,29 +885,61 @@ export class Pregel extends PartialRunnable {
885
885
  nextTasks[taskId].writes.push([k, v]);
886
886
  }
887
887
  // clear all current tasks
888
- _applyWrites(checkpoint, channels, Object.values(nextTasks), undefined, this.triggerToNodes);
888
+ _applyWrites(checkpoint, channels, Object.values(nextTasks), checkpointer.getNextVersion.bind(checkpointer), this.triggerToNodes);
889
889
  }
890
890
  // save checkpoint
891
- const nextConfig = await checkpointer.put(checkpointConfig, createCheckpoint(checkpoint, undefined, step), {
891
+ const nextConfig = await checkpointer.put(checkpointConfig, createCheckpoint(checkpoint, channels, step), {
892
892
  ...checkpointMetadata,
893
893
  source: "update",
894
894
  step: step + 1,
895
895
  writes: {},
896
896
  parents: saved?.metadata?.parents ?? {},
897
- }, {});
897
+ }, getNewChannelVersions(checkpointPreviousVersions, checkpoint.channel_versions));
898
898
  return patchCheckpointMap(nextConfig, saved ? saved.metadata : undefined);
899
899
  }
900
- if (values == null && asNode === COPY) {
900
+ if (asNode === COPY) {
901
901
  if (updates.length > 1) {
902
902
  throw new InvalidUpdateError(`Cannot copy checkpoint with multiple updates`);
903
903
  }
904
- const nextConfig = await checkpointer.put(saved?.parentConfig ?? checkpointConfig, createCheckpoint(checkpoint, undefined, step), {
904
+ if (saved == null) {
905
+ throw new InvalidUpdateError(`Cannot copy a non-existent checkpoint`);
906
+ }
907
+ const isCopyWithUpdates = (values) => {
908
+ if (!Array.isArray(values))
909
+ return false;
910
+ if (values.length === 0)
911
+ return false;
912
+ return values.every((v) => Array.isArray(v) && v.length === 2);
913
+ };
914
+ const nextCheckpoint = createCheckpoint(checkpoint, undefined, step);
915
+ const nextConfig = await checkpointer.put(saved.parentConfig ??
916
+ patchConfigurable(saved.config, { checkpoint_id: undefined }), nextCheckpoint, {
905
917
  source: "fork",
906
918
  step: step + 1,
907
919
  writes: {},
908
- parents: saved?.metadata?.parents ?? {},
920
+ parents: saved.metadata?.parents ?? {},
909
921
  }, {});
910
- return patchCheckpointMap(nextConfig, saved ? saved.metadata : undefined);
922
+ // We want to both clone a checkpoint and update state in one go.
923
+ // Reuse the same task ID if possible.
924
+ if (isCopyWithUpdates(values)) {
925
+ // figure out the task IDs for the next update checkpoint
926
+ const nextTasks = _prepareNextTasks(nextCheckpoint, saved.pendingWrites, this.nodes, channels, managed, nextConfig, false, { step: step + 2 });
927
+ const tasksGroupBy = Object.values(nextTasks).reduce((acc, { name, id }) => {
928
+ acc[name] ??= [];
929
+ acc[name].push({ id });
930
+ return acc;
931
+ }, {});
932
+ const userGroupBy = values.reduce((acc, item) => {
933
+ const [values, asNode] = item;
934
+ acc[asNode] ??= [];
935
+ const targetIdx = acc[asNode].length;
936
+ const taskId = tasksGroupBy[asNode]?.[targetIdx]?.id;
937
+ acc[asNode].push({ values, asNode, taskId });
938
+ return acc;
939
+ }, {});
940
+ return updateSuperStep(patchCheckpointMap(nextConfig, saved.metadata), Object.values(userGroupBy).flat());
941
+ }
942
+ return patchCheckpointMap(nextConfig, saved.metadata);
911
943
  }
912
944
  if (asNode === INPUT) {
913
945
  if (updates.length > 1) {
@@ -979,7 +1011,7 @@ export class Pregel extends PartialRunnable {
979
1011
  const validUpdates = [];
980
1012
  if (updates.length === 1) {
981
1013
  // eslint-disable-next-line prefer-const
982
- let { values, asNode } = updates[0];
1014
+ let { values, asNode, taskId } = updates[0];
983
1015
  if (asNode === undefined && Object.keys(this.nodes).length === 1) {
984
1016
  // if only one node, use it
985
1017
  [asNode] = Object.keys(this.nodes);
@@ -1015,18 +1047,18 @@ export class Pregel extends PartialRunnable {
1015
1047
  if (asNode === undefined) {
1016
1048
  throw new InvalidUpdateError(`Ambiguous update, specify "asNode"`);
1017
1049
  }
1018
- validUpdates.push({ values, asNode });
1050
+ validUpdates.push({ values, asNode, taskId });
1019
1051
  }
1020
1052
  else {
1021
- for (const { asNode, values } of updates) {
1053
+ for (const { asNode, values, taskId } of updates) {
1022
1054
  if (asNode == null) {
1023
1055
  throw new InvalidUpdateError(`"asNode" is required when applying multiple updates`);
1024
1056
  }
1025
- validUpdates.push({ values, asNode });
1057
+ validUpdates.push({ values, asNode, taskId });
1026
1058
  }
1027
1059
  }
1028
1060
  const tasks = [];
1029
- for (const { asNode, values } of validUpdates) {
1061
+ for (const { asNode, values, taskId } of validUpdates) {
1030
1062
  if (this.nodes[asNode] === undefined) {
1031
1063
  throw new InvalidUpdateError(`Node "${asNode.toString()}" does not exist`);
1032
1064
  }
@@ -1046,7 +1078,7 @@ export class Pregel extends PartialRunnable {
1046
1078
  : writers[0],
1047
1079
  writes: [],
1048
1080
  triggers: [INTERRUPT],
1049
- id: uuid5(INTERRUPT, checkpoint.id),
1081
+ id: taskId ?? uuid5(INTERRUPT, checkpoint.id),
1050
1082
  writers: [],
1051
1083
  });
1052
1084
  }
@@ -1135,6 +1167,8 @@ export class Pregel extends PartialRunnable {
1135
1167
  * - checkpointer
1136
1168
  * - store
1137
1169
  * - whether stream mode is single
1170
+ * - node cache
1171
+ * - whether checkpoint during is enabled
1138
1172
  * @internal
1139
1173
  */
1140
1174
  _defaults(config) {
@@ -1163,13 +1197,16 @@ export class Pregel extends PartialRunnable {
1163
1197
  streamModeSingle = typeof streamMode === "string";
1164
1198
  }
1165
1199
  else {
1166
- defaultStreamMode = this.streamMode;
1200
+ // if being called as a node in another graph, default to values mode
1201
+ // but don't overwrite `streamMode`if provided
1202
+ if (config.configurable?.[CONFIG_KEY_TASK_ID] !== undefined) {
1203
+ defaultStreamMode = ["values"];
1204
+ }
1205
+ else {
1206
+ defaultStreamMode = this.streamMode;
1207
+ }
1167
1208
  streamModeSingle = true;
1168
1209
  }
1169
- // if being called as a node in another graph, always use values mode
1170
- if (config.configurable?.[CONFIG_KEY_TASK_ID] !== undefined) {
1171
- defaultStreamMode = ["values"];
1172
- }
1173
1210
  let defaultCheckpointer;
1174
1211
  if (this.checkpointer === false) {
1175
1212
  defaultCheckpointer = undefined;
@@ -1178,11 +1215,17 @@ export class Pregel extends PartialRunnable {
1178
1215
  config.configurable?.[CONFIG_KEY_CHECKPOINTER] !== undefined) {
1179
1216
  defaultCheckpointer = config.configurable[CONFIG_KEY_CHECKPOINTER];
1180
1217
  }
1218
+ else if (this.checkpointer === true) {
1219
+ throw new Error("checkpointer: true cannot be used for root graphs.");
1220
+ }
1181
1221
  else {
1182
1222
  defaultCheckpointer = this.checkpointer;
1183
1223
  }
1184
1224
  const defaultStore = config.store ?? this.store;
1185
1225
  const defaultCache = config.cache ?? this.cache;
1226
+ const defaultCheckpointDuring = config.checkpointDuring ??
1227
+ config?.configurable?.[CONFIG_KEY_CHECKPOINT_DURING] ??
1228
+ true;
1186
1229
  return [
1187
1230
  defaultDebug,
1188
1231
  defaultStreamMode,
@@ -1195,6 +1238,7 @@ export class Pregel extends PartialRunnable {
1195
1238
  defaultStore,
1196
1239
  streamModeSingle,
1197
1240
  defaultCache,
1241
+ defaultCheckpointDuring,
1198
1242
  ];
1199
1243
  }
1200
1244
  /**
@@ -1341,11 +1385,20 @@ export class Pregel extends PartialRunnable {
1341
1385
  const validInput = await this._validateInput(input);
1342
1386
  const { runId, ...restConfig } = inputConfig;
1343
1387
  // assign defaults
1344
- const [debug, streamMode, , outputKeys, config, interruptBefore, interruptAfter, checkpointer, store, streamModeSingle, cache,] = this._defaults(restConfig);
1388
+ const [debug, streamMode, , outputKeys, config, interruptBefore, interruptAfter, checkpointer, store, streamModeSingle, cache, checkpointDuring,] = this._defaults(restConfig);
1345
1389
  config.configurable = await this._validateConfigurable(config.configurable);
1346
1390
  const stream = new IterableReadableWritableStream({
1347
1391
  modes: new Set(streamMode),
1348
1392
  });
1393
+ // set up subgraph checkpointing
1394
+ if (this.checkpointer === true) {
1395
+ config.configurable ??= {};
1396
+ const ns = config.configurable[CONFIG_KEY_CHECKPOINT_NS] ?? "";
1397
+ config.configurable[CONFIG_KEY_CHECKPOINT_NS] = ns
1398
+ .split(CHECKPOINT_NAMESPACE_SEPARATOR)
1399
+ .map((part) => part.split(CHECKPOINT_NAMESPACE_END)[0])
1400
+ .join(CHECKPOINT_NAMESPACE_SEPARATOR);
1401
+ }
1349
1402
  // set up messages stream mode
1350
1403
  if (streamMode.includes("messages")) {
1351
1404
  const messageStreamer = new StreamMessagesHandler((chunk) => stream.push(chunk));
@@ -1405,6 +1458,7 @@ export class Pregel extends PartialRunnable {
1405
1458
  manager: runManager,
1406
1459
  debug: this.debug,
1407
1460
  triggerToNodes: this.triggerToNodes,
1461
+ checkpointDuring,
1408
1462
  });
1409
1463
  const runner = new PregelRunner({
1410
1464
  loop,