@langchain/langgraph 0.2.7 → 0.2.9

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 (45) hide show
  1. package/dist/constants.cjs +12 -2
  2. package/dist/constants.d.ts +7 -1
  3. package/dist/constants.js +11 -1
  4. package/dist/errors.cjs +5 -4
  5. package/dist/errors.d.ts +1 -1
  6. package/dist/errors.js +5 -4
  7. package/dist/graph/graph.cjs +7 -2
  8. package/dist/graph/graph.js +8 -3
  9. package/dist/graph/message.cjs +2 -0
  10. package/dist/graph/message.js +2 -0
  11. package/dist/graph/state.cjs +9 -7
  12. package/dist/graph/state.d.ts +2 -2
  13. package/dist/graph/state.js +10 -8
  14. package/dist/managed/shared_value.cjs +1 -1
  15. package/dist/managed/shared_value.js +1 -1
  16. package/dist/pregel/algo.cjs +119 -54
  17. package/dist/pregel/algo.d.ts +5 -2
  18. package/dist/pregel/algo.js +117 -53
  19. package/dist/pregel/debug.cjs +15 -18
  20. package/dist/pregel/debug.d.ts +3 -2
  21. package/dist/pregel/debug.js +16 -19
  22. package/dist/pregel/index.cjs +295 -123
  23. package/dist/pregel/index.d.ts +16 -5
  24. package/dist/pregel/index.js +292 -123
  25. package/dist/pregel/io.cjs +15 -8
  26. package/dist/pregel/io.d.ts +2 -2
  27. package/dist/pregel/io.js +15 -8
  28. package/dist/pregel/loop.cjs +256 -111
  29. package/dist/pregel/loop.d.ts +21 -5
  30. package/dist/pregel/loop.js +256 -109
  31. package/dist/pregel/read.cjs +9 -2
  32. package/dist/pregel/read.d.ts +2 -1
  33. package/dist/pregel/read.js +9 -2
  34. package/dist/pregel/retry.d.ts +1 -1
  35. package/dist/pregel/types.d.ts +5 -2
  36. package/dist/pregel/utils/config.cjs +72 -0
  37. package/dist/pregel/utils/config.d.ts +2 -0
  38. package/dist/pregel/utils/config.js +68 -0
  39. package/dist/pregel/{utils.cjs → utils/index.cjs} +33 -10
  40. package/dist/pregel/{utils.d.ts → utils/index.d.ts} +4 -7
  41. package/dist/pregel/{utils.js → utils/index.js} +30 -8
  42. package/dist/utils.cjs +5 -3
  43. package/dist/utils.js +5 -3
  44. package/dist/web.d.ts +2 -1
  45. package/package.json +1 -1
@@ -1,20 +1,22 @@
1
1
  /* eslint-disable no-param-reassign */
2
- import { Runnable, RunnableSequence, _coerceToRunnable, ensureConfig, getCallbackManagerForConfig, patchConfig, } from "@langchain/core/runnables";
2
+ import { Runnable, RunnableSequence, _coerceToRunnable, getCallbackManagerForConfig, mergeConfigs, patchConfig, } from "@langchain/core/runnables";
3
3
  import { compareChannelVersions, copyCheckpoint, emptyCheckpoint, uuid5, } from "@langchain/langgraph-checkpoint";
4
+ import Deque from "double-ended-queue";
4
5
  import { createCheckpoint, emptyChannels, isBaseChannel, } from "../channels/base.js";
5
6
  import { PregelNode } from "./read.js";
6
7
  import { validateGraph, validateKeys } from "./validate.js";
7
8
  import { readChannels } from "./io.js";
8
9
  import { printStepCheckpoint, printStepTasks, printStepWrites, tasksWithWrites, } from "./debug.js";
9
10
  import { ChannelWrite, PASSTHROUGH } from "./write.js";
10
- import { CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_READ, CONFIG_KEY_SEND, CONFIG_KEY_STORE, ERROR, INTERRUPT, } from "../constants.js";
11
+ import { CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_READ, CONFIG_KEY_SEND, CONFIG_KEY_STORE, ERROR, INTERRUPT, CHECKPOINT_NAMESPACE_SEPARATOR, CHECKPOINT_NAMESPACE_END, CONFIG_KEY_STREAM, CONFIG_KEY_TASK_ID, } from "../constants.js";
11
12
  import { GraphRecursionError, GraphValueError, InvalidUpdateError, isGraphInterrupt, } from "../errors.js";
12
13
  import { _prepareNextTasks, _localRead, _applyWrites, } from "./algo.js";
13
- import { _coerceToDict, getNewChannelVersions } from "./utils.js";
14
- import { PregelLoop } from "./loop.js";
14
+ import { _coerceToDict, getNewChannelVersions, patchCheckpointMap, } from "./utils/index.js";
15
+ import { PregelLoop, StreamProtocol } from "./loop.js";
15
16
  import { executeTasksWithRetry } from "./retry.js";
16
17
  import { ChannelKeyPlaceholder, isConfiguredManagedValue, ManagedValueMapping, NoopManagedValue, } from "../managed/base.js";
17
- import { patchConfigurable } from "../utils.js";
18
+ import { gatherIterator, patchConfigurable } from "../utils.js";
19
+ import { ensureLangGraphConfig } from "./utils/config.js";
18
20
  function isString(value) {
19
21
  return typeof value === "string";
20
22
  }
@@ -72,6 +74,16 @@ export class Channel {
72
74
  return new ChannelWrite(channelWriteEntries);
73
75
  }
74
76
  }
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ function isPregel(x) {
79
+ return ("inputChannels" in x &&
80
+ x.inputChannels !== undefined &&
81
+ "outputChannels" &&
82
+ x.outputChannels !== undefined);
83
+ }
84
+ function isRunnableSequence(x) {
85
+ return "steps" in x && Array.isArray(x.steps);
86
+ }
75
87
  export class Pregel extends Runnable {
76
88
  static lc_name() {
77
89
  return "LangGraph";
@@ -109,12 +121,6 @@ export class Pregel extends Runnable {
109
121
  writable: true,
110
122
  value: void 0
111
123
  });
112
- Object.defineProperty(this, "configKeys", {
113
- enumerable: true,
114
- configurable: true,
115
- writable: true,
116
- value: void 0
117
- });
118
124
  Object.defineProperty(this, "autoValidate", {
119
125
  enumerable: true,
120
126
  configurable: true,
@@ -169,6 +175,12 @@ export class Pregel extends Runnable {
169
175
  writable: true,
170
176
  value: void 0
171
177
  });
178
+ Object.defineProperty(this, "config", {
179
+ enumerable: true,
180
+ configurable: true,
181
+ writable: true,
182
+ value: void 0
183
+ });
172
184
  Object.defineProperty(this, "store", {
173
185
  enumerable: true,
174
186
  configurable: true,
@@ -185,7 +197,6 @@ export class Pregel extends Runnable {
185
197
  this.streamMode = streamMode ?? this.streamMode;
186
198
  this.inputChannels = fields.inputChannels;
187
199
  this.outputChannels = fields.outputChannels;
188
- this.configKeys = fields.configKeys;
189
200
  this.streamChannels = fields.streamChannels ?? this.streamChannels;
190
201
  this.interruptAfter = fields.interruptAfter;
191
202
  this.interruptBefore = fields.interruptBefore;
@@ -193,11 +204,18 @@ export class Pregel extends Runnable {
193
204
  this.debug = fields.debug ?? this.debug;
194
205
  this.checkpointer = fields.checkpointer;
195
206
  this.retryPolicy = fields.retryPolicy;
207
+ this.config = fields.config;
196
208
  this.store = fields.store;
197
209
  if (this.autoValidate) {
198
210
  this.validate();
199
211
  }
200
212
  }
213
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
214
+ // @ts-ignore Remove ignore when we remove support for 0.2 versions of core
215
+ withConfig(config) {
216
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
217
+ return new this.constructor({ ...this, config });
218
+ }
201
219
  validate() {
202
220
  validateGraph({
203
221
  nodes: this.nodes,
@@ -229,50 +247,181 @@ export class Pregel extends Runnable {
229
247
  return Object.keys(this.channels);
230
248
  }
231
249
  }
232
- /**
233
- * Get the current state of the graph.
234
- */
235
- async getState(config) {
236
- if (!this.checkpointer) {
237
- throw new GraphValueError("No checkpointer set");
250
+ *getSubgraphs(namespace, recurse
251
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
252
+ ) {
253
+ for (const [name, node] of Object.entries(this.nodes)) {
254
+ // filter by prefix
255
+ if (namespace !== undefined) {
256
+ if (!namespace.startsWith(name)) {
257
+ continue;
258
+ }
259
+ }
260
+ // find the subgraph if any
261
+ let graph;
262
+ const candidates = [node.bound];
263
+ for (const candidate of candidates) {
264
+ if (isPregel(candidate)) {
265
+ graph = candidate;
266
+ break;
267
+ }
268
+ else if (isRunnableSequence(candidate)) {
269
+ candidates.push(...candidate.steps);
270
+ }
271
+ }
272
+ // if found, yield recursively
273
+ if (graph !== undefined) {
274
+ if (name === namespace) {
275
+ yield [name, graph];
276
+ return;
277
+ }
278
+ if (namespace === undefined) {
279
+ yield [name, graph];
280
+ }
281
+ if (recurse) {
282
+ let newNamespace = namespace;
283
+ if (namespace !== undefined) {
284
+ newNamespace = namespace.slice(name.length + 1);
285
+ }
286
+ for (const [subgraphName, subgraph] of graph.getSubgraphs(newNamespace, recurse)) {
287
+ yield [
288
+ `${name}${CHECKPOINT_NAMESPACE_SEPARATOR}${subgraphName}`,
289
+ subgraph,
290
+ ];
291
+ }
292
+ }
293
+ }
294
+ }
295
+ }
296
+ async _prepareStateSnapshot({ config, saved, subgraphCheckpointer, }) {
297
+ if (saved === undefined) {
298
+ return {
299
+ values: {},
300
+ next: [],
301
+ config,
302
+ tasks: [],
303
+ };
238
304
  }
239
- const saved = await this.checkpointer.getTuple(config);
240
- const checkpoint = saved ? saved.checkpoint : emptyCheckpoint();
241
- const channels = emptyChannels(this.channels, checkpoint);
242
305
  // Pass `skipManaged: true` as managed values should not be returned in get state calls.
243
306
  const { managed } = await this.prepareSpecs(config, { skipManaged: true });
244
- const nextTasks = _prepareNextTasks(checkpoint, this.nodes, channels, managed, saved !== undefined ? saved.config : config, false, { step: saved ? (saved.metadata?.step ?? -1) + 1 : -1 });
307
+ const channels = emptyChannels(this.channels, saved.checkpoint);
308
+ const nextTasks = Object.values(_prepareNextTasks(saved.checkpoint, this.nodes, channels, managed, saved.config, false, { step: (saved.metadata?.step ?? -1) + 1 }));
309
+ const subgraphs = await gatherIterator(this.getSubgraphs());
310
+ const parentNamespace = saved.config.configurable?.checkpoint_ns ?? "";
311
+ const taskStates = {};
312
+ for (const task of nextTasks) {
313
+ const matchingSubgraph = subgraphs.find(([name]) => name === task.name);
314
+ if (!matchingSubgraph) {
315
+ continue;
316
+ }
317
+ // assemble checkpoint_ns for this task
318
+ let taskNs = `${task.name}${CHECKPOINT_NAMESPACE_END}${task.id}`;
319
+ if (parentNamespace) {
320
+ taskNs = `${parentNamespace}${CHECKPOINT_NAMESPACE_SEPARATOR}${taskNs}`;
321
+ }
322
+ if (subgraphCheckpointer === undefined) {
323
+ // set config as signal that subgraph checkpoints exist
324
+ const config = {
325
+ configurable: {
326
+ thread_id: saved.config.configurable?.thread_id,
327
+ checkpoint_ns: taskNs,
328
+ },
329
+ };
330
+ taskStates[task.id] = config;
331
+ }
332
+ else {
333
+ // get the state of the subgraph
334
+ const config = {
335
+ configurable: {
336
+ [CONFIG_KEY_CHECKPOINTER]: subgraphCheckpointer,
337
+ thread_id: saved.config.configurable?.thread_id,
338
+ checkpoint_ns: taskNs,
339
+ },
340
+ };
341
+ taskStates[task.id] = await matchingSubgraph[1].getState(config, {
342
+ subgraphs: true,
343
+ });
344
+ }
345
+ }
346
+ // assemble the state snapshot
245
347
  return {
246
348
  values: readChannels(channels, this.streamChannelsAsIs),
247
349
  next: nextTasks.map((task) => task.name),
248
- tasks: tasksWithWrites(nextTasks, saved?.pendingWrites ?? []),
249
- metadata: saved?.metadata,
250
- config: saved ? saved.config : config,
251
- createdAt: saved?.checkpoint.ts,
252
- parentConfig: saved?.parentConfig,
350
+ tasks: tasksWithWrites(nextTasks, saved?.pendingWrites ?? [], taskStates),
351
+ metadata: saved.metadata,
352
+ config: patchCheckpointMap(saved.config, saved.metadata),
353
+ createdAt: saved.checkpoint.ts,
354
+ parentConfig: saved.parentConfig,
253
355
  };
254
356
  }
357
+ /**
358
+ * Get the current state of the graph.
359
+ */
360
+ async getState(config, options) {
361
+ const checkpointer = config.configurable?.[CONFIG_KEY_CHECKPOINTER] ?? this.checkpointer;
362
+ if (!checkpointer) {
363
+ throw new GraphValueError("No checkpointer set");
364
+ }
365
+ const checkpointNamespace = config.configurable?.checkpoint_ns ?? "";
366
+ if (checkpointNamespace !== "" &&
367
+ config.configurable?.[CONFIG_KEY_CHECKPOINTER] === undefined) {
368
+ // remove task_ids from checkpoint_ns
369
+ const recastCheckpointNamespace = checkpointNamespace
370
+ .split(CHECKPOINT_NAMESPACE_SEPARATOR)
371
+ .map((part) => part.split(CHECKPOINT_NAMESPACE_END)[0])
372
+ .join(CHECKPOINT_NAMESPACE_SEPARATOR);
373
+ for (const [name, subgraph] of this.getSubgraphs(recastCheckpointNamespace, true)) {
374
+ if (name === recastCheckpointNamespace) {
375
+ return await subgraph.getState(patchConfigurable(config, {
376
+ [CONFIG_KEY_CHECKPOINTER]: checkpointer,
377
+ }), { subgraphs: options?.subgraphs });
378
+ }
379
+ }
380
+ throw new Error(`Subgraph with namespace "${recastCheckpointNamespace}" not found.`);
381
+ }
382
+ const mergedConfig = mergeConfigs(this.config, config);
383
+ const saved = await checkpointer.getTuple(config);
384
+ const snapshot = await this._prepareStateSnapshot({
385
+ config: mergedConfig,
386
+ saved,
387
+ subgraphCheckpointer: options?.subgraphs ? checkpointer : undefined,
388
+ });
389
+ return snapshot;
390
+ }
255
391
  /**
256
392
  * Get the history of the state of the graph.
257
393
  */
258
394
  async *getStateHistory(config, options) {
259
- if (!this.checkpointer) {
260
- throw new GraphValueError("No checkpointer set");
395
+ const checkpointer = config.configurable?.[CONFIG_KEY_CHECKPOINTER] ?? this.checkpointer;
396
+ if (!checkpointer) {
397
+ throw new Error("No checkpointer set");
398
+ }
399
+ const checkpointNamespace = config.configurable?.checkpoint_ns ?? "";
400
+ if (checkpointNamespace !== "" &&
401
+ config.configurable?.[CONFIG_KEY_CHECKPOINTER] === undefined) {
402
+ const recastCheckpointNamespace = checkpointNamespace
403
+ .split(CHECKPOINT_NAMESPACE_SEPARATOR)
404
+ .map((part) => part.split(CHECKPOINT_NAMESPACE_END)[0])
405
+ .join(CHECKPOINT_NAMESPACE_SEPARATOR);
406
+ // find the subgraph with the matching name
407
+ for (const [name, pregel] of this.getSubgraphs(recastCheckpointNamespace, true)) {
408
+ if (name === recastCheckpointNamespace) {
409
+ yield* pregel.getStateHistory(patchConfigurable(config, {
410
+ [CONFIG_KEY_CHECKPOINTER]: checkpointer,
411
+ }), options);
412
+ return;
413
+ }
414
+ }
415
+ throw new Error(`Subgraph with namespace "${recastCheckpointNamespace}" not found.`);
261
416
  }
262
- // Pass `skipManaged: true` as managed values should not be returned in get state calls.
263
- const { managed } = await this.prepareSpecs(config, { skipManaged: true });
264
- for await (const saved of this.checkpointer.list(config, options)) {
265
- const channels = emptyChannels(this.channels, saved.checkpoint);
266
- const nextTasks = _prepareNextTasks(saved.checkpoint, this.nodes, channels, managed, saved.config, false, { step: -1 });
267
- yield {
268
- values: readChannels(channels, this.streamChannelsAsIs),
269
- next: nextTasks.map((task) => task.name),
270
- tasks: tasksWithWrites(nextTasks, saved.pendingWrites ?? []),
271
- metadata: saved.metadata,
272
- config: saved.config,
273
- createdAt: saved.checkpoint.ts,
274
- parentConfig: saved.parentConfig,
275
- };
417
+ const mergedConfig = mergeConfigs(this.config, config, {
418
+ configurable: { checkpoint_ns: checkpointNamespace },
419
+ });
420
+ for await (const checkpointTuple of checkpointer.list(mergedConfig, options)) {
421
+ yield this._prepareStateSnapshot({
422
+ config: checkpointTuple.config,
423
+ saved: checkpointTuple,
424
+ });
276
425
  }
277
426
  }
278
427
  /**
@@ -280,34 +429,59 @@ export class Pregel extends Runnable {
280
429
  * node `as_node`. If `as_node` is not provided, it will be set to the last node
281
430
  * that updated the state, if not ambiguous.
282
431
  */
283
- async updateState(config, values, asNode) {
284
- if (!this.checkpointer) {
432
+ async updateState(inputConfig, values, asNode) {
433
+ const checkpointer = inputConfig.configurable?.[CONFIG_KEY_CHECKPOINTER] ?? this.checkpointer;
434
+ if (!checkpointer) {
285
435
  throw new GraphValueError("No checkpointer set");
286
436
  }
287
- // Get the latest checkpoint
288
- const saved = await this.checkpointer.getTuple(config);
289
- const checkpoint = saved
437
+ // delegate to subgraph
438
+ const checkpointNamespace = inputConfig.configurable?.checkpoint_ns ?? "";
439
+ if (checkpointNamespace !== "" &&
440
+ inputConfig.configurable?.[CONFIG_KEY_CHECKPOINTER] === undefined) {
441
+ // remove task_ids from checkpoint_ns
442
+ const recastCheckpointNamespace = checkpointNamespace
443
+ .split(CHECKPOINT_NAMESPACE_SEPARATOR)
444
+ .map((part) => {
445
+ return part.split(CHECKPOINT_NAMESPACE_END)[0];
446
+ })
447
+ .join(CHECKPOINT_NAMESPACE_SEPARATOR);
448
+ // find the subgraph with the matching name
449
+ // eslint-disable-next-line no-unreachable-loop
450
+ for (const [, pregel] of this.getSubgraphs(recastCheckpointNamespace, true)) {
451
+ return await pregel.updateState(patchConfigurable(inputConfig, {
452
+ [CONFIG_KEY_CHECKPOINTER]: checkpointer,
453
+ }), values, asNode);
454
+ }
455
+ throw new Error(`Subgraph "${recastCheckpointNamespace}" not found`);
456
+ }
457
+ // get last checkpoint
458
+ const config = this.config
459
+ ? mergeConfigs(this.config, inputConfig)
460
+ : inputConfig;
461
+ const saved = await checkpointer.getTuple(config);
462
+ const checkpoint = saved !== undefined
290
463
  ? copyCheckpoint(saved.checkpoint)
291
464
  : emptyCheckpoint();
292
- const checkpointPreviousVersions = saved?.checkpoint.channel_versions ?? {};
465
+ const checkpointPreviousVersions = {
466
+ ...saved?.checkpoint.channel_versions,
467
+ };
293
468
  const step = saved?.metadata?.step ?? -1;
294
469
  // merge configurable fields with previous checkpoint config
295
- const checkpointConfig = {
296
- ...config,
297
- configurable: {
298
- ...config.configurable,
299
- // TODO: add proper support for updating nested subgraph state
300
- checkpoint_ns: "",
301
- ...saved?.config.configurable,
302
- },
303
- };
470
+ let checkpointConfig = patchConfigurable(config, {
471
+ checkpoint_ns: config.configurable?.checkpoint_ns ?? "",
472
+ });
473
+ if (saved) {
474
+ checkpointConfig = patchConfigurable(config, saved.config.configurable);
475
+ }
304
476
  // Find last node that updated the state, if not provided
305
477
  if (values == null && asNode === undefined) {
306
- return await this.checkpointer.put(checkpointConfig, createCheckpoint(checkpoint, undefined, step), {
478
+ const nextConfig = await checkpointer.put(checkpointConfig, createCheckpoint(checkpoint, undefined, step), {
307
479
  source: "update",
308
480
  step,
309
481
  writes: {},
482
+ parents: saved?.metadata?.parents ?? {},
310
483
  }, {});
484
+ return patchCheckpointMap(nextConfig, saved ? saved.metadata : undefined);
311
485
  }
312
486
  const nonNullVersion = Object.values(checkpoint.versions_seen)
313
487
  .map((seenVersions) => {
@@ -322,7 +496,6 @@ export class Pregel extends Runnable {
322
496
  }
323
497
  }
324
498
  else if (asNode === undefined) {
325
- // TODO: Double check
326
499
  const lastSeenByNode = Object.entries(checkpoint.versions_seen)
327
500
  .map(([n, seen]) => {
328
501
  return Object.values(seen).map((v) => {
@@ -379,18 +552,21 @@ export class Pregel extends Runnable {
379
552
  task, select_, fresh_),
380
553
  },
381
554
  }));
555
+ // save task writes
382
556
  if (saved !== undefined) {
383
- await this.checkpointer.putWrites(checkpointConfig, task.writes, task.id);
557
+ await checkpointer.putWrites(checkpointConfig, task.writes, task.id);
384
558
  }
385
559
  // apply to checkpoint
386
560
  // TODO: Why does keyof StrRecord allow number and symbol?
387
- _applyWrites(checkpoint, channels, [task], this.checkpointer.getNextVersion.bind(this.checkpointer));
561
+ _applyWrites(checkpoint, channels, [task], checkpointer.getNextVersion.bind(this.checkpointer));
388
562
  const newVersions = getNewChannelVersions(checkpointPreviousVersions, checkpoint.channel_versions);
389
- return await this.checkpointer.put(checkpointConfig, createCheckpoint(checkpoint, channels, step + 1), {
563
+ const nextConfig = await checkpointer.put(checkpointConfig, createCheckpoint(checkpoint, channels, step + 1), {
390
564
  source: "update",
391
565
  step: step + 1,
392
566
  writes: { [asNode]: values },
567
+ parents: saved?.metadata?.parents ?? {},
393
568
  }, newVersions);
569
+ return patchCheckpointMap(nextConfig, saved ? saved.metadata : undefined);
394
570
  }
395
571
  _defaults(config) {
396
572
  const { debug, streamMode, inputKeys, outputKeys, interruptAfter, interruptBefore, ...rest } = config;
@@ -418,25 +594,18 @@ export class Pregel extends Runnable {
418
594
  else {
419
595
  defaultStreamMode = this.streamMode;
420
596
  }
421
- let defaultCheckpointer;
422
- if (config.configurable !== undefined &&
423
- config.configurable[CONFIG_KEY_READ] !== undefined) {
597
+ // if being called as a node in another graph, always use values mode
598
+ if (config.configurable?.[CONFIG_KEY_TASK_ID] !== undefined) {
424
599
  defaultStreamMode = ["values"];
425
600
  }
601
+ let defaultCheckpointer;
426
602
  if (config !== undefined &&
427
- config.configurable?.[CONFIG_KEY_CHECKPOINTER] !== undefined &&
428
- (defaultInterruptAfter.length > 0 || defaultInterruptBefore.length > 0)) {
603
+ config.configurable?.[CONFIG_KEY_CHECKPOINTER] !== undefined) {
429
604
  defaultCheckpointer = config.configurable[CONFIG_KEY_CHECKPOINTER];
430
605
  }
431
606
  else {
432
607
  defaultCheckpointer = this.checkpointer;
433
608
  }
434
- if (this.configKeys !== undefined) {
435
- const newConfigurable = Object.fromEntries(Object.entries(rest.configurable ?? {}).filter(([key]) => {
436
- return this.configKeys?.includes(key);
437
- }));
438
- rest.configurable = newConfigurable;
439
- }
440
609
  return [
441
610
  defaultDebug,
442
611
  defaultStreamMode,
@@ -510,7 +679,8 @@ export class Pregel extends Runnable {
510
679
  };
511
680
  }
512
681
  async *_streamIterator(input, options) {
513
- const inputConfig = ensureConfig(options);
682
+ const streamSubgraphs = options?.subgraphs;
683
+ const inputConfig = ensureLangGraphConfig(this.config, options);
514
684
  if (inputConfig.recursionLimit === undefined ||
515
685
  inputConfig.recursionLimit < 1) {
516
686
  throw new Error(`Passed "recursionLimit" must be at least 1.`);
@@ -526,6 +696,30 @@ export class Pregel extends Runnable {
526
696
  const [debug, streamMode, , outputKeys, config, interruptBefore, interruptAfter, checkpointer,] = this._defaults(inputConfig);
527
697
  const { channelSpecs, managed } = await this.prepareSpecs(config);
528
698
  let loop;
699
+ const stream = new Deque();
700
+ function* emitCurrentLoopOutputs() {
701
+ while (loop !== undefined && stream.length > 0) {
702
+ const nextItem = stream.shift();
703
+ if (nextItem === undefined) {
704
+ throw new Error("Data structure error.");
705
+ }
706
+ const [namespace, mode, payload] = nextItem;
707
+ if (streamMode.includes(mode)) {
708
+ if (streamSubgraphs && streamMode.length > 1) {
709
+ yield [namespace, mode, payload];
710
+ }
711
+ else if (streamMode.length > 1) {
712
+ yield [mode, payload];
713
+ }
714
+ else if (streamSubgraphs) {
715
+ yield [namespace, payload];
716
+ }
717
+ else {
718
+ yield payload;
719
+ }
720
+ }
721
+ }
722
+ }
529
723
  try {
530
724
  loop = await PregelLoop.initialize({
531
725
  input,
@@ -537,7 +731,14 @@ export class Pregel extends Runnable {
537
731
  outputKeys,
538
732
  streamKeys: this.streamChannelsAsIs,
539
733
  store: this.store,
734
+ stream: new StreamProtocol((chunk) => stream.push(chunk), new Set(streamMode)),
540
735
  });
736
+ if (options?.subgraphs) {
737
+ loop.config.configurable = {
738
+ ...loop.config.configurable,
739
+ [CONFIG_KEY_STREAM]: loop.stream,
740
+ };
741
+ }
541
742
  while (await loop.tick({
542
743
  inputKeys: this.inputChannels,
543
744
  interruptAfter,
@@ -547,27 +748,14 @@ export class Pregel extends Runnable {
547
748
  if (debug) {
548
749
  printStepCheckpoint(loop.checkpointMetadata.step, loop.channels, this.streamChannelsList);
549
750
  }
550
- while (loop.stream.length > 0) {
551
- const nextItem = loop.stream.shift();
552
- if (nextItem === undefined) {
553
- throw new Error("Data structure error.");
554
- }
555
- if (streamMode.includes(nextItem[0])) {
556
- if (streamMode.length === 1) {
557
- yield nextItem[1];
558
- }
559
- else {
560
- yield nextItem;
561
- }
562
- }
563
- }
751
+ yield* emitCurrentLoopOutputs();
564
752
  if (debug) {
565
- printStepTasks(loop.step, loop.tasks);
753
+ printStepTasks(loop.step, Object.values(loop.tasks));
566
754
  }
567
755
  // execute tasks, and wait for one to fail or all to finish.
568
756
  // each task is independent from all other concurrent tasks
569
757
  // yield updates/debug output as each task finishes
570
- const taskStream = executeTasksWithRetry(loop.tasks.filter((task) => task.writes.length === 0), {
758
+ const taskStream = executeTasksWithRetry(Object.values(loop.tasks).filter((task) => task.writes.length === 0), {
571
759
  stepTimeout: this.stepTimeout,
572
760
  signal: config.signal,
573
761
  retryPolicy: this.retryPolicy,
@@ -576,7 +764,12 @@ export class Pregel extends Runnable {
576
764
  for await (const { task, error } of taskStream) {
577
765
  if (error !== undefined) {
578
766
  if (isGraphInterrupt(error)) {
579
- loop.putWrites(task.id, error.interrupts.map((interrupt) => [INTERRUPT, interrupt]));
767
+ if (loop.isNested) {
768
+ throw error;
769
+ }
770
+ if (error.interrupts.length) {
771
+ loop.putWrites(task.id, error.interrupts.map((interrupt) => [INTERRUPT, interrupt]));
772
+ }
580
773
  }
581
774
  else {
582
775
  loop.putWrites(task.id, [
@@ -587,42 +780,18 @@ export class Pregel extends Runnable {
587
780
  else {
588
781
  loop.putWrites(task.id, task.writes);
589
782
  }
590
- while (loop.stream.length > 0) {
591
- const nextItem = loop.stream.shift();
592
- if (nextItem === undefined) {
593
- throw new Error("Data structure error.");
594
- }
595
- if (streamMode.includes(nextItem[0])) {
596
- if (streamMode.length === 1) {
597
- yield nextItem[1];
598
- }
599
- else {
600
- yield nextItem;
601
- }
602
- }
603
- }
783
+ yield* emitCurrentLoopOutputs();
604
784
  if (error !== undefined && !isGraphInterrupt(error)) {
605
785
  throw error;
606
786
  }
607
787
  }
608
788
  if (debug) {
609
- printStepWrites(loop.step, loop.tasks.map((task) => task.writes).flat(), this.streamChannelsList);
610
- }
611
- }
612
- while (loop.stream.length > 0) {
613
- const nextItem = loop.stream.shift();
614
- if (nextItem === undefined) {
615
- throw new Error("Data structure error.");
616
- }
617
- if (streamMode.includes(nextItem[0])) {
618
- if (streamMode.length === 1) {
619
- yield nextItem[1];
620
- }
621
- else {
622
- yield nextItem;
623
- }
789
+ printStepWrites(loop.step, Object.values(loop.tasks)
790
+ .map((task) => task.writes)
791
+ .flat(), this.streamChannelsList);
624
792
  }
625
793
  }
794
+ yield* emitCurrentLoopOutputs();
626
795
  if (loop.status === "out_of_steps") {
627
796
  throw new GraphRecursionError([
628
797
  `Recursion limit of ${config.recursionLimit} reached`,
@@ -631,7 +800,7 @@ export class Pregel extends Runnable {
631
800
  ].join(" "));
632
801
  }
633
802
  await Promise.all(loop?.checkpointerPromises ?? []);
634
- await runManager?.handleChainEnd(readChannels(loop.channels, outputKeys));
803
+ await runManager?.handleChainEnd(loop.output);
635
804
  }
636
805
  catch (e) {
637
806
  await runManager?.handleChainError(e);
@@ -666,7 +835,7 @@ export class Pregel extends Runnable {
666
835
  async invoke(input, options) {
667
836
  const streamMode = options?.streamMode ?? "values";
668
837
  const config = {
669
- ...ensureConfig(options),
838
+ ...options,
670
839
  outputKeys: options?.outputKeys ?? this.outputChannels,
671
840
  streamMode,
672
841
  };