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