@langchain/langgraph 0.2.8 → 0.2.10-rc.0

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 (63) hide show
  1. package/dist/constants.cjs +11 -2
  2. package/dist/constants.d.ts +6 -1
  3. package/dist/constants.js +10 -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 +8 -3
  12. package/dist/graph/state.d.ts +2 -3
  13. package/dist/graph/state.js +9 -4
  14. package/dist/managed/shared_value.cjs +12 -10
  15. package/dist/managed/shared_value.d.ts +6 -5
  16. package/dist/managed/shared_value.js +12 -10
  17. package/dist/pregel/algo.cjs +124 -54
  18. package/dist/pregel/algo.d.ts +13 -3
  19. package/dist/pregel/algo.js +122 -53
  20. package/dist/pregel/debug.cjs +15 -18
  21. package/dist/pregel/debug.d.ts +3 -2
  22. package/dist/pregel/debug.js +16 -19
  23. package/dist/pregel/index.cjs +307 -116
  24. package/dist/pregel/index.d.ts +21 -6
  25. package/dist/pregel/index.js +305 -117
  26. package/dist/pregel/io.cjs +15 -8
  27. package/dist/pregel/io.d.ts +2 -2
  28. package/dist/pregel/io.js +15 -8
  29. package/dist/pregel/loop.cjs +258 -113
  30. package/dist/pregel/loop.d.ts +24 -9
  31. package/dist/pregel/loop.js +258 -111
  32. package/dist/pregel/read.cjs +9 -2
  33. package/dist/pregel/read.d.ts +3 -2
  34. package/dist/pregel/read.js +9 -2
  35. package/dist/pregel/retry.d.ts +1 -1
  36. package/dist/pregel/runnable_types.cjs +2 -0
  37. package/dist/pregel/runnable_types.d.ts +5 -0
  38. package/dist/pregel/runnable_types.js +1 -0
  39. package/dist/pregel/types.d.ts +8 -4
  40. package/dist/pregel/utils/config.cjs +73 -0
  41. package/dist/pregel/utils/config.d.ts +3 -0
  42. package/dist/pregel/utils/config.js +69 -0
  43. package/dist/pregel/{utils.cjs → utils/index.cjs} +33 -10
  44. package/dist/pregel/{utils.d.ts → utils/index.d.ts} +4 -7
  45. package/dist/pregel/{utils.js → utils/index.js} +30 -8
  46. package/dist/utils.cjs +5 -3
  47. package/dist/utils.js +5 -3
  48. package/dist/web.cjs +4 -2
  49. package/dist/web.d.ts +4 -3
  50. package/dist/web.js +1 -2
  51. package/package.json +1 -1
  52. package/dist/store/base.cjs +0 -12
  53. package/dist/store/base.d.ts +0 -7
  54. package/dist/store/base.js +0 -8
  55. package/dist/store/batch.cjs +0 -126
  56. package/dist/store/batch.d.ts +0 -55
  57. package/dist/store/batch.js +0 -122
  58. package/dist/store/index.cjs +0 -19
  59. package/dist/store/index.d.ts +0 -3
  60. package/dist/store/index.js +0 -3
  61. package/dist/store/memory.cjs +0 -43
  62. package/dist/store/memory.d.ts +0 -6
  63. package/dist/store/memory.js +0 -39
@@ -1,10 +1,6 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PregelLoop = void 0;
7
- const double_ended_queue_1 = __importDefault(require("double-ended-queue"));
3
+ exports.PregelLoop = exports.StreamProtocol = void 0;
8
4
  const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
9
5
  const base_js_1 = require("../channels/base.cjs");
10
6
  const constants_js_1 = require("../constants.cjs");
@@ -12,12 +8,40 @@ const algo_js_1 = require("./algo.cjs");
12
8
  const utils_js_1 = require("../utils.cjs");
13
9
  const io_js_1 = require("./io.cjs");
14
10
  const errors_js_1 = require("../errors.cjs");
15
- const utils_js_2 = require("./utils.cjs");
11
+ const index_js_1 = require("./utils/index.cjs");
16
12
  const debug_js_1 = require("./debug.cjs");
17
- const batch_js_1 = require("../store/batch.cjs");
18
13
  const INPUT_DONE = Symbol.for("INPUT_DONE");
19
14
  const INPUT_RESUMING = Symbol.for("INPUT_RESUMING");
20
15
  const DEFAULT_LOOP_LIMIT = 25;
16
+ const SPECIAL_CHANNELS = [constants_js_1.ERROR, constants_js_1.INTERRUPT];
17
+ class StreamProtocol {
18
+ constructor(pushFn, modes) {
19
+ Object.defineProperty(this, "push", {
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true,
23
+ value: void 0
24
+ });
25
+ Object.defineProperty(this, "modes", {
26
+ enumerable: true,
27
+ configurable: true,
28
+ writable: true,
29
+ value: void 0
30
+ });
31
+ this.push = pushFn;
32
+ this.modes = modes;
33
+ }
34
+ }
35
+ exports.StreamProtocol = StreamProtocol;
36
+ function createDuplexStream(...streams) {
37
+ return new StreamProtocol((value) => {
38
+ for (const stream of streams) {
39
+ if (stream.modes.has(value[1])) {
40
+ stream.push(value);
41
+ }
42
+ }
43
+ }, new Set(streams.flatMap((s) => Array.from(s.modes))));
44
+ }
21
45
  class PregelLoop {
22
46
  constructor(params) {
23
47
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -27,6 +51,13 @@ class PregelLoop {
27
51
  writable: true,
28
52
  value: void 0
29
53
  });
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ Object.defineProperty(this, "output", {
56
+ enumerable: true,
57
+ configurable: true,
58
+ writable: true,
59
+ value: void 0
60
+ });
30
61
  Object.defineProperty(this, "config", {
31
62
  enumerable: true,
32
63
  configurable: true,
@@ -75,6 +106,12 @@ class PregelLoop {
75
106
  writable: true,
76
107
  value: void 0
77
108
  });
109
+ Object.defineProperty(this, "checkpointNamespace", {
110
+ enumerable: true,
111
+ configurable: true,
112
+ writable: true,
113
+ value: void 0
114
+ });
78
115
  Object.defineProperty(this, "checkpointPendingWrites", {
79
116
  enumerable: true,
80
117
  configurable: true,
@@ -123,6 +160,12 @@ class PregelLoop {
123
160
  writable: true,
124
161
  value: void 0
125
162
  });
163
+ Object.defineProperty(this, "taskWritesLeft", {
164
+ enumerable: true,
165
+ configurable: true,
166
+ writable: true,
167
+ value: 0
168
+ });
126
169
  Object.defineProperty(this, "status", {
127
170
  enumerable: true,
128
171
  configurable: true,
@@ -134,14 +177,14 @@ class PregelLoop {
134
177
  enumerable: true,
135
178
  configurable: true,
136
179
  writable: true,
137
- value: []
180
+ value: {}
138
181
  });
139
182
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
140
183
  Object.defineProperty(this, "stream", {
141
184
  enumerable: true,
142
185
  configurable: true,
143
186
  writable: true,
144
- value: new double_ended_queue_1.default()
187
+ value: void 0
145
188
  });
146
189
  Object.defineProperty(this, "checkpointerPromises", {
147
190
  enumerable: true,
@@ -168,7 +211,6 @@ class PregelLoop {
168
211
  value: void 0
169
212
  });
170
213
  this.input = params.input;
171
- this.config = params.config;
172
214
  this.checkpointer = params.checkpointer;
173
215
  // TODO: if managed values no longer needs graph we can replace with
174
216
  // managed_specs, channel_specs
@@ -179,7 +221,6 @@ class PregelLoop {
179
221
  this.checkpointerGetNextVersion = algo_js_1.increment;
180
222
  }
181
223
  this.checkpoint = params.checkpoint;
182
- this.checkpointConfig = params.checkpointConfig;
183
224
  this.checkpointMetadata = params.checkpointMetadata;
184
225
  this.checkpointPreviousVersions = params.checkpointPreviousVersions;
185
226
  this.channels = params.channels;
@@ -187,29 +228,58 @@ class PregelLoop {
187
228
  this.checkpointPendingWrites = params.checkpointPendingWrites;
188
229
  this.step = params.step;
189
230
  this.stop = params.stop;
190
- this.isNested = constants_js_1.CONFIG_KEY_READ in (this.config.configurable ?? {});
231
+ this.config = params.config;
232
+ this.checkpointConfig = params.checkpointConfig;
233
+ this.isNested = params.isNested;
191
234
  this.outputKeys = params.outputKeys;
192
235
  this.streamKeys = params.streamKeys;
193
236
  this.nodes = params.nodes;
194
- this.skipDoneTasks = this.config.configurable?.checkpoint_id === undefined;
237
+ this.skipDoneTasks = params.skipDoneTasks;
195
238
  this.store = params.store;
239
+ this.stream = params.stream;
240
+ this.checkpointNamespace = params.checkpointNamespace;
196
241
  }
197
242
  static async initialize(params) {
198
- const saved = (await params.checkpointer?.getTuple(params.config)) ?? {
199
- config: params.config,
243
+ let { config, stream } = params;
244
+ if (stream !== undefined &&
245
+ config.configurable?.[constants_js_1.CONFIG_KEY_STREAM] !== undefined) {
246
+ stream = createDuplexStream(stream, config.configurable[constants_js_1.CONFIG_KEY_STREAM]);
247
+ }
248
+ const skipDoneTasks = config.configurable?.checkpoint_id === undefined;
249
+ const isNested = constants_js_1.CONFIG_KEY_READ in (config.configurable ?? {});
250
+ if (!isNested &&
251
+ config.configurable?.checkpoint_ns !== undefined &&
252
+ config.configurable?.checkpoint_ns !== "") {
253
+ config = (0, index_js_1.patchConfigurable)(config, {
254
+ checkpoint_ns: "",
255
+ checkpoint_id: undefined,
256
+ });
257
+ }
258
+ let checkpointConfig = config;
259
+ if (config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINT_MAP] !== undefined &&
260
+ config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINT_MAP]?.[config.configurable?.checkpoint_ns]) {
261
+ checkpointConfig = (0, index_js_1.patchConfigurable)(config, {
262
+ checkpoint_id: config.configurable[constants_js_1.CONFIG_KEY_CHECKPOINT_MAP][config.configurable?.checkpoint_ns],
263
+ });
264
+ }
265
+ const checkpointNamespace = config.configurable?.checkpoint_ns?.split(constants_js_1.CHECKPOINT_NAMESPACE_SEPARATOR) ?? [];
266
+ const saved = (await params.checkpointer?.getTuple(checkpointConfig)) ?? {
267
+ config,
200
268
  checkpoint: (0, langgraph_checkpoint_1.emptyCheckpoint)(),
201
269
  metadata: {
202
270
  source: "input",
203
271
  step: -2,
204
272
  writes: null,
273
+ parents: {},
205
274
  },
206
275
  pendingWrites: [],
207
276
  };
208
- const checkpointConfig = {
209
- ...params.config,
277
+ checkpointConfig = {
278
+ ...config,
210
279
  ...saved.config,
211
280
  configurable: {
212
- ...params.config.configurable,
281
+ checkpoint_ns: "",
282
+ ...config.configurable,
213
283
  ...saved.config.configurable,
214
284
  },
215
285
  };
@@ -218,10 +288,10 @@ class PregelLoop {
218
288
  const checkpointPendingWrites = saved.pendingWrites ?? [];
219
289
  const channels = (0, base_js_1.emptyChannels)(params.channelSpecs, checkpoint);
220
290
  const step = (checkpointMetadata.step ?? 0) + 1;
221
- const stop = step + (params.config.recursionLimit ?? DEFAULT_LOOP_LIMIT) + 1;
291
+ const stop = step + (config.recursionLimit ?? DEFAULT_LOOP_LIMIT) + 1;
222
292
  const checkpointPreviousVersions = { ...checkpoint.channel_versions };
223
293
  const store = params.store
224
- ? new batch_js_1.AsyncBatchedStore(params.store)
294
+ ? new langgraph_checkpoint_1.AsyncBatchedStore(params.store)
225
295
  : undefined;
226
296
  if (store) {
227
297
  // Start the store. This is a batch store, so it will run continuously
@@ -229,13 +299,16 @@ class PregelLoop {
229
299
  }
230
300
  return new PregelLoop({
231
301
  input: params.input,
232
- config: params.config,
302
+ config,
233
303
  checkpointer: params.checkpointer,
234
304
  checkpoint,
235
305
  checkpointMetadata,
236
306
  checkpointConfig,
307
+ checkpointNamespace,
237
308
  channels,
238
309
  managed: params.managed,
310
+ isNested,
311
+ skipDoneTasks,
239
312
  step,
240
313
  stop,
241
314
  checkpointPreviousVersions,
@@ -243,6 +316,7 @@ class PregelLoop {
243
316
  outputKeys: params.outputKeys ?? [],
244
317
  streamKeys: params.streamKeys ?? [],
245
318
  nodes: params.nodes,
319
+ stream,
246
320
  store,
247
321
  });
248
322
  }
@@ -265,6 +339,22 @@ class PregelLoop {
265
339
  * @param writes
266
340
  */
267
341
  putWrites(taskId, writes) {
342
+ if (writes.length === 0) {
343
+ return;
344
+ }
345
+ // adjust taskWritesLeft
346
+ const firstChannel = writes[0][0];
347
+ const anyChannelIsSend = writes.find(([channel]) => channel === constants_js_1.TASKS);
348
+ const alwaysSave = anyChannelIsSend || SPECIAL_CHANNELS.includes(firstChannel);
349
+ if (!alwaysSave && !this.taskWritesLeft) {
350
+ return this._outputWrites(taskId, writes);
351
+ }
352
+ else if (firstChannel !== constants_js_1.INTERRUPT) {
353
+ // INTERRUPT makes us want to save the last task's writes
354
+ // so we don't decrement tasksWritesLeft in that case
355
+ this.taskWritesLeft -= 1;
356
+ }
357
+ // save writes
268
358
  const pendingWrites = writes.map(([key, value]) => {
269
359
  return [taskId, key, value];
270
360
  });
@@ -280,10 +370,23 @@ class PregelLoop {
280
370
  if (putWritePromise !== undefined) {
281
371
  this.checkpointerPromises.push(putWritePromise);
282
372
  }
283
- const task = this.tasks.find((task) => task.id === taskId);
373
+ this._outputWrites(taskId, writes);
374
+ }
375
+ _outputWrites(taskId, writes, cached = false) {
376
+ const task = this.tasks[taskId];
284
377
  if (task !== undefined) {
285
- this.stream.push(...(0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputUpdates)(this.outputKeys, [task]), "updates")));
286
- this.stream.push(...(0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTaskResults)(this.step, [[task, writes]], this.streamKeys), "debug")));
378
+ if (task.config !== undefined &&
379
+ (task.config.tags ?? []).includes(constants_js_1.TAG_HIDDEN)) {
380
+ return;
381
+ }
382
+ if (writes.length > 0 &&
383
+ writes[0][0] !== constants_js_1.ERROR &&
384
+ writes[0][0] !== constants_js_1.INTERRUPT) {
385
+ this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputUpdates)(this.outputKeys, [[task, writes]], cached), "updates")));
386
+ }
387
+ if (!cached) {
388
+ this._emit((0, utils_js_1.gatherIteratorSync)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTaskResults)(this.step, [[task, writes]], this.streamKeys), "debug")));
389
+ }
287
390
  }
288
391
  }
289
392
  /**
@@ -292,35 +395,99 @@ class PregelLoop {
292
395
  * @param params
293
396
  */
294
397
  async tick(params) {
295
- if (this.store && !this.store.isRunning) {
296
- this.store?.start();
297
- }
298
- const { inputKeys = [], interruptAfter = [], interruptBefore = [], manager, } = params;
299
- if (this.status !== "pending") {
300
- throw new Error(`Cannot tick when status is no longer "pending". Current status: "${this.status}"`);
301
- }
302
- if (![INPUT_DONE, INPUT_RESUMING].includes(this.input)) {
303
- await this._first(inputKeys);
304
- }
305
- else if (this.tasks.every((task) => task.writes.length > 0)) {
306
- const writes = this.tasks.flatMap((t) => t.writes);
307
- // All tasks have finished
308
- const myWrites = (0, algo_js_1._applyWrites)(this.checkpoint, this.channels, this.tasks, this.checkpointerGetNextVersion);
309
- for (const [key, values] of Object.entries(myWrites)) {
310
- await this.updateManagedValues(key, values);
398
+ let tickError;
399
+ try {
400
+ if (this.store && !this.store.isRunning) {
401
+ this.store?.start();
311
402
  }
312
- // produce values output
313
- const valuesOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputValues)(this.outputKeys, writes, this.channels), "values"));
314
- this.stream.push(...valuesOutput);
315
- // clear pending writes
316
- this.checkpointPendingWrites = [];
317
- await this._putCheckpoint({
318
- source: "loop",
319
- writes: (0, io_js_1.mapOutputUpdates)(this.outputKeys, this.tasks).next().value ?? null,
403
+ const { inputKeys = [], interruptAfter = [], interruptBefore = [], manager, } = params;
404
+ if (this.status !== "pending") {
405
+ throw new Error(`Cannot tick when status is no longer "pending". Current status: "${this.status}"`);
406
+ }
407
+ if (![INPUT_DONE, INPUT_RESUMING].includes(this.input)) {
408
+ await this._first(inputKeys);
409
+ }
410
+ else if (Object.values(this.tasks).every((task) => task.writes.length > 0)) {
411
+ const writes = Object.values(this.tasks).flatMap((t) => t.writes);
412
+ // All tasks have finished
413
+ const managedValueWrites = (0, algo_js_1._applyWrites)(this.checkpoint, this.channels, Object.values(this.tasks), this.checkpointerGetNextVersion);
414
+ for (const [key, values] of Object.entries(managedValueWrites)) {
415
+ await this.updateManagedValues(key, values);
416
+ }
417
+ // produce values output
418
+ const valuesOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputValues)(this.outputKeys, writes, this.channels), "values"));
419
+ this._emit(valuesOutput);
420
+ // clear pending writes
421
+ this.checkpointPendingWrites = [];
422
+ await this._putCheckpoint({
423
+ source: "loop",
424
+ writes: (0, io_js_1.mapOutputUpdates)(this.outputKeys, Object.values(this.tasks).map((task) => [task, task.writes])).next().value ?? null,
425
+ });
426
+ // after execution, check if we should interrupt
427
+ if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, interruptAfter, Object.values(this.tasks))) {
428
+ this.status = "interrupt_after";
429
+ if (this.isNested) {
430
+ throw new errors_js_1.GraphInterrupt();
431
+ }
432
+ else {
433
+ return false;
434
+ }
435
+ }
436
+ }
437
+ else {
438
+ return false;
439
+ }
440
+ if (this.step > this.stop) {
441
+ this.status = "out_of_steps";
442
+ return false;
443
+ }
444
+ const nextTasks = (0, algo_js_1._prepareNextTasks)(this.checkpoint, this.nodes, this.channels, this.managed, this.config, true, {
445
+ step: this.step,
446
+ checkpointer: this.checkpointer,
447
+ isResuming: this.input === INPUT_RESUMING,
448
+ manager,
449
+ store: this.store,
320
450
  });
321
- // after execution, check if we should interrupt
322
- if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, interruptAfter, this.tasks)) {
323
- this.status = "interrupt_after";
451
+ this.tasks = nextTasks;
452
+ this.taskWritesLeft = Object.values(this.tasks).length - 1;
453
+ // Produce debug output
454
+ if (this.checkpointer) {
455
+ 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
456
+ this.checkpointConfig, this.channels, this.streamKeys, this.checkpointMetadata, Object.values(this.tasks), this.checkpointPendingWrites), "debug")));
457
+ }
458
+ if (Object.values(this.tasks).length === 0) {
459
+ this.status = "done";
460
+ return false;
461
+ }
462
+ // if there are pending writes from a previous loop, apply them
463
+ if (this.skipDoneTasks && this.checkpointPendingWrites.length > 0) {
464
+ for (const [tid, k, v] of this.checkpointPendingWrites) {
465
+ if (k === constants_js_1.ERROR || k === constants_js_1.INTERRUPT) {
466
+ continue;
467
+ }
468
+ const task = Object.values(this.tasks).find((t) => t.id === tid);
469
+ if (task) {
470
+ task.writes.push([k, v]);
471
+ }
472
+ }
473
+ for (const task of Object.values(this.tasks)) {
474
+ if (task.writes.length > 0) {
475
+ this._outputWrites(task.id, task.writes, true);
476
+ }
477
+ }
478
+ }
479
+ // if all tasks have finished, re-tick
480
+ if (Object.values(this.tasks).every((task) => task.writes.length > 0)) {
481
+ return this.tick({
482
+ inputKeys,
483
+ interruptAfter,
484
+ interruptBefore,
485
+ manager,
486
+ });
487
+ }
488
+ // Before execution, check if we should interrupt
489
+ if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, interruptBefore, Object.values(this.tasks))) {
490
+ this.status = "interrupt_before";
324
491
  if (this.isNested) {
325
492
  throw new errors_js_1.GraphInterrupt();
326
493
  }
@@ -328,65 +495,29 @@ class PregelLoop {
328
495
  return false;
329
496
  }
330
497
  }
498
+ // Produce debug output
499
+ 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"));
500
+ this._emit(debugOutput);
501
+ return true;
331
502
  }
332
- else {
333
- return false;
334
- }
335
- if (this.step > this.stop) {
336
- this.status = "out_of_steps";
337
- return false;
338
- }
339
- const nextTasks = (0, algo_js_1._prepareNextTasks)(this.checkpoint, this.nodes, this.channels, this.managed, this.config, true, {
340
- step: this.step,
341
- checkpointer: this.checkpointer,
342
- isResuming: this.input === INPUT_RESUMING,
343
- manager,
344
- });
345
- this.tasks = nextTasks;
346
- // Produce debug output
347
- if (this.checkpointer) {
348
- this.stream.push(...(await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugCheckpoint)(this.step - 1, // printing checkpoint for previous step
349
- this.checkpointConfig, this.channels, this.streamKeys, this.checkpointMetadata, this.tasks, this.checkpointPendingWrites), "debug"))));
350
- }
351
- if (this.tasks.length === 0) {
352
- this.status = "done";
353
- return false;
354
- }
355
- // if there are pending writes from a previous loop, apply them
356
- if (this.checkpointPendingWrites.length > 0 && this.skipDoneTasks) {
357
- for (const [tid, k, v] of this.checkpointPendingWrites) {
358
- if (k === constants_js_1.ERROR || k === constants_js_1.INTERRUPT) {
359
- continue;
360
- }
361
- const task = this.tasks.find((t) => t.id === tid);
362
- if (task) {
363
- task.writes.push([k, v]);
364
- }
365
- }
366
- }
367
- // if all tasks have finished, re-tick
368
- if (this.tasks.every((task) => task.writes.length > 0)) {
369
- return this.tick({
370
- inputKeys,
371
- interruptAfter,
372
- interruptBefore,
373
- manager,
374
- });
375
- }
376
- // Before execution, check if we should interrupt
377
- if ((0, algo_js_1.shouldInterrupt)(this.checkpoint, interruptBefore, this.tasks)) {
378
- this.status = "interrupt_before";
379
- if (this.isNested) {
380
- throw new errors_js_1.GraphInterrupt();
503
+ catch (e) {
504
+ tickError = e;
505
+ if (!this._suppressInterrupt(tickError)) {
506
+ throw tickError;
381
507
  }
382
508
  else {
383
- return false;
509
+ this.output = (0, io_js_1.readChannels)(this.channels, this.outputKeys);
510
+ }
511
+ return false;
512
+ }
513
+ finally {
514
+ if (tickError === undefined) {
515
+ this.output = (0, io_js_1.readChannels)(this.channels, this.outputKeys);
384
516
  }
385
517
  }
386
- // Produce debug output
387
- const debugOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, debug_js_1.mapDebugTasks)(this.step, this.tasks), "debug"));
388
- this.stream.push(...debugOutput);
389
- return true;
518
+ }
519
+ _suppressInterrupt(e) {
520
+ return (0, errors_js_1.isGraphInterrupt)(e) && !this.isNested;
390
521
  }
391
522
  /**
392
523
  * Resuming from previous checkpoint requires
@@ -394,9 +525,9 @@ class PregelLoop {
394
525
  * - receiving None input (outer graph) or RESUMING flag (subgraph)
395
526
  */
396
527
  async _first(inputKeys) {
397
- const isResuming = (Object.keys(this.checkpoint.channel_versions).length !== 0 &&
398
- this.config.configurable?.[constants_js_1.CONFIG_KEY_RESUMING] !== undefined) ||
399
- this.input === null;
528
+ const isResuming = Object.keys(this.checkpoint.channel_versions).length !== 0 &&
529
+ (this.config.configurable?.[constants_js_1.CONFIG_KEY_RESUMING] !== undefined ||
530
+ this.input === null);
400
531
  if (isResuming) {
401
532
  for (const channelName of Object.keys(this.channels)) {
402
533
  if (this.checkpoint.channel_versions[channelName] !== undefined) {
@@ -407,6 +538,9 @@ class PregelLoop {
407
538
  };
408
539
  }
409
540
  }
541
+ // produce values output
542
+ const valuesOutput = await (0, utils_js_1.gatherIterator)((0, utils_js_1.prefixGenerator)((0, io_js_1.mapOutputValues)(this.outputKeys, true, this.channels), "values"));
543
+ this._emit(valuesOutput);
410
544
  // map inputs to channel updates
411
545
  }
412
546
  else {
@@ -415,7 +549,7 @@ class PregelLoop {
415
549
  throw new errors_js_1.EmptyInputError(`Received no input writes for ${JSON.stringify(inputKeys, null, 2)}`);
416
550
  }
417
551
  const discardTasks = (0, algo_js_1._prepareNextTasks)(this.checkpoint, this.nodes, this.channels, this.managed, this.config, true, { step: this.step });
418
- (0, algo_js_1._applyWrites)(this.checkpoint, this.channels, discardTasks.concat([
552
+ (0, algo_js_1._applyWrites)(this.checkpoint, this.channels, Object.values(discardTasks).concat([
419
553
  {
420
554
  name: constants_js_1.INPUT,
421
555
  writes: inputWrites,
@@ -430,12 +564,25 @@ class PregelLoop {
430
564
  }
431
565
  // done with input
432
566
  this.input = isResuming ? INPUT_RESUMING : INPUT_DONE;
567
+ if (!this.isNested) {
568
+ this.config = (0, index_js_1.patchConfigurable)(this.config, {
569
+ [constants_js_1.CONFIG_KEY_RESUMING]: isResuming,
570
+ });
571
+ }
572
+ }
573
+ _emit(values) {
574
+ for (const chunk of values) {
575
+ if (this.stream.modes.has(chunk[0])) {
576
+ this.stream.push([this.checkpointNamespace, ...chunk]);
577
+ }
578
+ }
433
579
  }
434
580
  async _putCheckpoint(inputMetadata) {
435
581
  // Assign step
436
582
  const metadata = {
437
583
  ...inputMetadata,
438
584
  step: this.step,
585
+ parents: this.config.configurable?.[constants_js_1.CONFIG_KEY_CHECKPOINT_MAP] ?? {},
439
586
  };
440
587
  // Bail if no checkpointer
441
588
  if (this.checkpointer !== undefined) {
@@ -445,9 +592,7 @@ class PregelLoop {
445
592
  // this is achieved by writing child checkpoints as progress is made
446
593
  // (so that error recovery / resuming from interrupt don't lose work)
447
594
  // but doing so always with an id equal to that of the parent checkpoint
448
- this.checkpoint = (0, base_js_1.createCheckpoint)(this.checkpoint, this.channels, this.step
449
- // id: this.isNested ? this.config.configurable?.checkpoint_id : undefined,
450
- );
595
+ this.checkpoint = (0, base_js_1.createCheckpoint)(this.checkpoint, this.channels, this.step);
451
596
  this.checkpointConfig = {
452
597
  ...this.checkpointConfig,
453
598
  configurable: {
@@ -456,7 +601,7 @@ class PregelLoop {
456
601
  },
457
602
  };
458
603
  const channelVersions = { ...this.checkpoint.channel_versions };
459
- const newVersions = (0, utils_js_2.getNewChannelVersions)(this.checkpointPreviousVersions, channelVersions);
604
+ const newVersions = (0, index_js_1.getNewChannelVersions)(this.checkpointPreviousVersions, channelVersions);
460
605
  this.checkpointPreviousVersions = channelVersions;
461
606
  // save it, without blocking
462
607
  // if there's a previous checkpoint save in progress, wait for it
@@ -1,13 +1,12 @@
1
- import Deque from "double-ended-queue";
2
1
  import type { RunnableConfig } from "@langchain/core/runnables";
3
2
  import type { CallbackManagerForChainRun } from "@langchain/core/callbacks/manager";
4
- import { BaseCheckpointSaver, Checkpoint, PendingWrite, CheckpointPendingWrite, CheckpointMetadata, All } from "@langchain/langgraph-checkpoint";
3
+ import { BaseCheckpointSaver, Checkpoint, PendingWrite, CheckpointPendingWrite, CheckpointMetadata, All, BaseStore, AsyncBatchedStore } from "@langchain/langgraph-checkpoint";
5
4
  import { BaseChannel } from "../channels/base.js";
6
5
  import { PregelExecutableTask, StreamMode } from "./types.js";
7
6
  import { PregelNode } from "./read.js";
8
- import { BaseStore } from "../store/base.js";
9
- import { AsyncBatchedStore } from "../store/batch.js";
10
7
  import { ManagedValueMapping } from "../managed/base.js";
8
+ import { LangGraphRunnableConfig } from "./runnable_types.js";
9
+ export type StreamChunk = [string[], StreamMode, unknown];
11
10
  export type PregelLoopInitializeParams = {
12
11
  input?: any;
13
12
  config: RunnableConfig;
@@ -17,6 +16,7 @@ export type PregelLoopInitializeParams = {
17
16
  nodes: Record<string, PregelNode>;
18
17
  channelSpecs: Record<string, BaseChannel>;
19
18
  managed: ManagedValueMapping;
19
+ stream: StreamProtocol;
20
20
  store?: BaseStore;
21
21
  };
22
22
  type PregelLoopParams = {
@@ -35,11 +35,21 @@ type PregelLoopParams = {
35
35
  outputKeys: string | string[];
36
36
  streamKeys: string | string[];
37
37
  nodes: Record<string, PregelNode>;
38
+ checkpointNamespace: string[];
39
+ skipDoneTasks: boolean;
40
+ isNested: boolean;
41
+ stream: StreamProtocol;
38
42
  store?: AsyncBatchedStore;
39
43
  };
44
+ export declare class StreamProtocol {
45
+ push: (chunk: StreamChunk) => void;
46
+ modes: Set<StreamMode>;
47
+ constructor(pushFn: (chunk: StreamChunk) => void, modes: Set<StreamMode>);
48
+ }
40
49
  export declare class PregelLoop {
41
50
  protected input?: any;
42
- config: RunnableConfig;
51
+ output: any;
52
+ config: LangGraphRunnableConfig;
43
53
  protected checkpointer?: BaseCheckpointSaver;
44
54
  protected checkpointerGetNextVersion: (current: number | undefined, channel: BaseChannel) => number;
45
55
  channels: Record<string, BaseChannel>;
@@ -47,6 +57,7 @@ export declare class PregelLoop {
47
57
  protected checkpoint: Checkpoint;
48
58
  protected checkpointConfig: RunnableConfig;
49
59
  checkpointMetadata: CheckpointMetadata;
60
+ protected checkpointNamespace: string[];
50
61
  protected checkpointPendingWrites: CheckpointPendingWrite[];
51
62
  protected checkpointPreviousVersions: Record<string, string | number>;
52
63
  step: number;
@@ -55,11 +66,12 @@ export declare class PregelLoop {
55
66
  protected streamKeys: string | string[];
56
67
  protected nodes: Record<string, PregelNode>;
57
68
  protected skipDoneTasks: boolean;
69
+ protected taskWritesLeft: number;
58
70
  status: "pending" | "done" | "interrupt_before" | "interrupt_after" | "out_of_steps";
59
- tasks: PregelExecutableTask<any, any>[];
60
- stream: Deque<[StreamMode, any]>;
71
+ tasks: Record<string, PregelExecutableTask<any, any>>;
72
+ stream: StreamProtocol;
61
73
  checkpointerPromises: Promise<unknown>[];
62
- protected isNested: boolean;
74
+ isNested: boolean;
63
75
  protected _checkpointerChainedPromise: Promise<unknown>;
64
76
  store?: AsyncBatchedStore;
65
77
  constructor(params: PregelLoopParams);
@@ -77,6 +89,7 @@ export declare class PregelLoop {
77
89
  * @param writes
78
90
  */
79
91
  putWrites(taskId: string, writes: PendingWrite<string>[]): void;
92
+ _outputWrites(taskId: string, writes: [string, unknown][], cached?: boolean): void;
80
93
  /**
81
94
  * Execute a single iteration of the Pregel loop.
82
95
  * Returns true if more iterations are needed.
@@ -88,12 +101,14 @@ export declare class PregelLoop {
88
101
  interruptBefore: string[] | All;
89
102
  manager?: CallbackManagerForChainRun;
90
103
  }): Promise<boolean>;
104
+ protected _suppressInterrupt(e?: Error): boolean;
91
105
  /**
92
106
  * Resuming from previous checkpoint requires
93
107
  * - finding a previous checkpoint
94
108
  * - receiving None input (outer graph) or RESUMING flag (subgraph)
95
109
  */
96
110
  protected _first(inputKeys: string | string[]): Promise<void>;
97
- protected _putCheckpoint(inputMetadata: Omit<CheckpointMetadata, "step">): Promise<void>;
111
+ protected _emit(values: [StreamMode, unknown][]): void;
112
+ protected _putCheckpoint(inputMetadata: Omit<CheckpointMetadata, "step" | "parents">): Promise<void>;
98
113
  }
99
114
  export {};