@langchain/langgraph 0.2.1 → 0.2.2-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.
@@ -50,7 +50,6 @@ class Branch {
50
50
  }
51
51
  let destinations;
52
52
  if (this.ends) {
53
- // destinations = [r if isinstance(r, Send) else self.ends[r] for r in result]
54
53
  destinations = result.map((r) => ((0, constants_js_1._isSend)(r) ? r : this.ends[r]));
55
54
  }
56
55
  else {
@@ -47,7 +47,6 @@ export class Branch {
47
47
  }
48
48
  let destinations;
49
49
  if (this.ends) {
50
- // destinations = [r if isinstance(r, Send) else self.ends[r] for r in result]
51
50
  destinations = result.map((r) => (_isSend(r) ? r : this.ends[r]));
52
51
  }
53
52
  else {
@@ -361,7 +361,10 @@ class Pregel extends runnables_1.Runnable {
361
361
  task),
362
362
  },
363
363
  }));
364
- // apply to checkpoint and save
364
+ if (saved !== undefined) {
365
+ await this.checkpointer.putWrites(checkpointConfig, task.writes, task.id);
366
+ }
367
+ // apply to checkpoint
365
368
  // TODO: Why does keyof StrRecord allow number and symbol?
366
369
  (0, algo_js_1._applyWrites)(checkpoint, channels, [task], this.checkpointer.getNextVersion.bind(this.checkpointer));
367
370
  const newVersions = (0, utils_js_1.getNewChannelVersions)(checkpointPreviousVersions, checkpoint.channel_versions);
@@ -455,10 +458,6 @@ class Pregel extends runnables_1.Runnable {
455
458
  // assign defaults
456
459
  const [debug, streamMode, , outputKeys, config, interruptBefore, interruptAfter, checkpointer,] = this._defaults(inputConfig);
457
460
  let loop;
458
- let backgroundError;
459
- const onBackgroundError = (e) => {
460
- backgroundError = e;
461
- };
462
461
  try {
463
462
  loop = await loop_js_1.PregelLoop.initialize({
464
463
  input,
@@ -466,17 +465,15 @@ class Pregel extends runnables_1.Runnable {
466
465
  checkpointer,
467
466
  nodes: this.nodes,
468
467
  channelSpecs: this.channels,
469
- onBackgroundError,
470
468
  outputKeys,
471
469
  streamKeys: this.streamChannelsAsIs,
472
470
  });
473
- while (backgroundError === undefined &&
474
- (await loop.tick({
475
- inputKeys: this.inputChannels,
476
- interruptAfter,
477
- interruptBefore,
478
- manager: runManager,
479
- }))) {
471
+ while (await loop.tick({
472
+ inputKeys: this.inputChannels,
473
+ interruptAfter,
474
+ interruptBefore,
475
+ manager: runManager,
476
+ })) {
480
477
  if (debug) {
481
478
  (0, debug_js_1.printStepCheckpoint)(loop.checkpointMetadata.step, loop.channels, this.streamChannelsList);
482
479
  }
@@ -542,10 +539,6 @@ class Pregel extends runnables_1.Runnable {
542
539
  (0, debug_js_1.printStepWrites)(loop.step, loop.tasks.map((task) => task.writes).flat(), this.streamChannelsList);
543
540
  }
544
541
  }
545
- // Checkpointing failures
546
- if (backgroundError !== undefined) {
547
- throw backgroundError;
548
- }
549
542
  while (loop.stream.length > 0) {
550
543
  const nextItem = loop.stream.shift();
551
544
  if (nextItem === undefined) {
@@ -567,6 +560,7 @@ class Pregel extends runnables_1.Runnable {
567
560
  `limit by setting the "recursionLimit" config key.`,
568
561
  ].join(" "));
569
562
  }
563
+ await Promise.all(loop?.checkpointerPromises ?? []);
570
564
  await runManager?.handleChainEnd((0, io_js_1.readChannels)(loop.channels, outputKeys));
571
565
  }
572
566
  catch (e) {
@@ -574,7 +568,7 @@ class Pregel extends runnables_1.Runnable {
574
568
  throw e;
575
569
  }
576
570
  finally {
577
- await loop?.backgroundTasksPromise;
571
+ await Promise.all(loop?.checkpointerPromises ?? []);
578
572
  }
579
573
  }
580
574
  /**
@@ -357,7 +357,10 @@ export class Pregel extends Runnable {
357
357
  task),
358
358
  },
359
359
  }));
360
- // apply to checkpoint and save
360
+ if (saved !== undefined) {
361
+ await this.checkpointer.putWrites(checkpointConfig, task.writes, task.id);
362
+ }
363
+ // apply to checkpoint
361
364
  // TODO: Why does keyof StrRecord allow number and symbol?
362
365
  _applyWrites(checkpoint, channels, [task], this.checkpointer.getNextVersion.bind(this.checkpointer));
363
366
  const newVersions = getNewChannelVersions(checkpointPreviousVersions, checkpoint.channel_versions);
@@ -451,10 +454,6 @@ export class Pregel extends Runnable {
451
454
  // assign defaults
452
455
  const [debug, streamMode, , outputKeys, config, interruptBefore, interruptAfter, checkpointer,] = this._defaults(inputConfig);
453
456
  let loop;
454
- let backgroundError;
455
- const onBackgroundError = (e) => {
456
- backgroundError = e;
457
- };
458
457
  try {
459
458
  loop = await PregelLoop.initialize({
460
459
  input,
@@ -462,17 +461,15 @@ export class Pregel extends Runnable {
462
461
  checkpointer,
463
462
  nodes: this.nodes,
464
463
  channelSpecs: this.channels,
465
- onBackgroundError,
466
464
  outputKeys,
467
465
  streamKeys: this.streamChannelsAsIs,
468
466
  });
469
- while (backgroundError === undefined &&
470
- (await loop.tick({
471
- inputKeys: this.inputChannels,
472
- interruptAfter,
473
- interruptBefore,
474
- manager: runManager,
475
- }))) {
467
+ while (await loop.tick({
468
+ inputKeys: this.inputChannels,
469
+ interruptAfter,
470
+ interruptBefore,
471
+ manager: runManager,
472
+ })) {
476
473
  if (debug) {
477
474
  printStepCheckpoint(loop.checkpointMetadata.step, loop.channels, this.streamChannelsList);
478
475
  }
@@ -538,10 +535,6 @@ export class Pregel extends Runnable {
538
535
  printStepWrites(loop.step, loop.tasks.map((task) => task.writes).flat(), this.streamChannelsList);
539
536
  }
540
537
  }
541
- // Checkpointing failures
542
- if (backgroundError !== undefined) {
543
- throw backgroundError;
544
- }
545
538
  while (loop.stream.length > 0) {
546
539
  const nextItem = loop.stream.shift();
547
540
  if (nextItem === undefined) {
@@ -563,6 +556,7 @@ export class Pregel extends Runnable {
563
556
  `limit by setting the "recursionLimit" config key.`,
564
557
  ].join(" "));
565
558
  }
559
+ await Promise.all(loop?.checkpointerPromises ?? []);
566
560
  await runManager?.handleChainEnd(readChannels(loop.channels, outputKeys));
567
561
  }
568
562
  catch (e) {
@@ -570,7 +564,7 @@ export class Pregel extends Runnable {
570
564
  throw e;
571
565
  }
572
566
  finally {
573
- await loop?.backgroundTasksPromise;
567
+ await Promise.all(loop?.checkpointerPromises ?? []);
574
568
  }
575
569
  }
576
570
  /**
@@ -18,9 +18,6 @@ const INPUT_DONE = Symbol.for("INPUT_DONE");
18
18
  const INPUT_RESUMING = Symbol.for("INPUT_RESUMING");
19
19
  const DEFAULT_LOOP_LIMIT = 25;
20
20
  class PregelLoop {
21
- get backgroundTasksPromise() {
22
- return this._putCheckpointPromise;
23
- }
24
21
  constructor(params) {
25
22
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
23
  Object.defineProperty(this, "input", {
@@ -113,6 +110,12 @@ class PregelLoop {
113
110
  writable: true,
114
111
  value: void 0
115
112
  });
113
+ Object.defineProperty(this, "skipDoneTasks", {
114
+ enumerable: true,
115
+ configurable: true,
116
+ writable: true,
117
+ value: void 0
118
+ });
116
119
  Object.defineProperty(this, "status", {
117
120
  enumerable: true,
118
121
  configurable: true,
@@ -133,23 +136,23 @@ class PregelLoop {
133
136
  writable: true,
134
137
  value: new double_ended_queue_1.default()
135
138
  });
136
- Object.defineProperty(this, "isNested", {
139
+ Object.defineProperty(this, "checkpointerPromises", {
137
140
  enumerable: true,
138
141
  configurable: true,
139
142
  writable: true,
140
- value: void 0
143
+ value: []
141
144
  });
142
- Object.defineProperty(this, "_putCheckpointPromise", {
145
+ Object.defineProperty(this, "isNested", {
143
146
  enumerable: true,
144
147
  configurable: true,
145
148
  writable: true,
146
- value: Promise.resolve()
149
+ value: void 0
147
150
  });
148
- Object.defineProperty(this, "onBackgroundError", {
151
+ Object.defineProperty(this, "_checkpointerChainedPromise", {
149
152
  enumerable: true,
150
153
  configurable: true,
151
154
  writable: true,
152
- value: void 0
155
+ value: Promise.resolve()
153
156
  });
154
157
  this.input = params.input;
155
158
  this.config = params.config;
@@ -174,7 +177,7 @@ class PregelLoop {
174
177
  this.outputKeys = params.outputKeys;
175
178
  this.streamKeys = params.streamKeys;
176
179
  this.nodes = params.nodes;
177
- this.onBackgroundError = params.onBackgroundError;
180
+ this.skipDoneTasks = this.config.configurable?.checkpoint_id === undefined;
178
181
  }
179
182
  static async initialize(params) {
180
183
  const saved = (await params.checkpointer?.getTuple(params.config)) ?? {
@@ -217,19 +220,13 @@ class PregelLoop {
217
220
  outputKeys: params.outputKeys ?? [],
218
221
  streamKeys: params.streamKeys ?? [],
219
222
  nodes: params.nodes,
220
- onBackgroundError: params.onBackgroundError,
221
223
  });
222
224
  }
223
- async _checkpointerPutAfterPrevious(input) {
224
- try {
225
- await this._putCheckpointPromise;
226
- }
227
- finally {
228
- this._putCheckpointPromise =
229
- this.checkpointer
230
- ?.put(input.config, input.checkpoint, input.metadata, input.newVersions)
231
- .catch(this.onBackgroundError) ?? Promise.resolve();
232
- }
225
+ _checkpointerPutAfterPrevious(input) {
226
+ this._checkpointerChainedPromise = this._checkpointerChainedPromise.then(() => {
227
+ return this.checkpointer?.put(input.config, input.checkpoint, input.metadata, input.newVersions);
228
+ });
229
+ this.checkpointerPromises.push(this._checkpointerChainedPromise);
233
230
  }
234
231
  /**
235
232
  * Put writes for a task, to be read by the next tick.
@@ -241,17 +238,16 @@ class PregelLoop {
241
238
  return [taskId, key, value];
242
239
  });
243
240
  this.checkpointPendingWrites.push(...pendingWrites);
244
- if (this.checkpointer !== undefined) {
245
- void this.checkpointer
246
- .putWrites({
247
- ...this.checkpointConfig,
248
- configurable: {
249
- ...this.checkpointConfig.configurable,
250
- checkpoint_ns: this.config.configurable?.checkpoint_ns ?? "",
251
- checkpoint_id: this.checkpoint.id,
252
- },
253
- }, writes, taskId)
254
- .catch(this.onBackgroundError);
241
+ const putWritePromise = this.checkpointer?.putWrites({
242
+ ...this.checkpointConfig,
243
+ configurable: {
244
+ ...this.checkpointConfig.configurable,
245
+ checkpoint_ns: this.config.configurable?.checkpoint_ns ?? "",
246
+ checkpoint_id: this.checkpoint.id,
247
+ },
248
+ }, writes, taskId);
249
+ if (putWritePromise !== undefined) {
250
+ this.checkpointerPromises.push(putWritePromise);
255
251
  }
256
252
  const task = this.tasks.find((task) => task.id === taskId);
257
253
  if (task !== undefined) {
@@ -320,7 +316,7 @@ class PregelLoop {
320
316
  return false;
321
317
  }
322
318
  // if there are pending writes from a previous loop, apply them
323
- if (this.checkpointPendingWrites.length > 0) {
319
+ if (this.checkpointPendingWrites.length > 0 && this.skipDoneTasks) {
324
320
  for (const [tid, k, v] of this.checkpointPendingWrites) {
325
321
  if (k === constants_js_1.ERROR || k === constants_js_1.INTERRUPT) {
326
322
  continue;
@@ -392,7 +388,7 @@ class PregelLoop {
392
388
  // save input checkpoint
393
389
  await this._putCheckpoint({
394
390
  source: "input",
395
- writes: this.input ?? null,
391
+ writes: Object.fromEntries(inputWrites),
396
392
  });
397
393
  }
398
394
  // done with input
@@ -9,7 +9,6 @@ export type PregelLoopInitializeParams = {
9
9
  input?: any;
10
10
  config: RunnableConfig;
11
11
  checkpointer?: BaseCheckpointSaver;
12
- onBackgroundError: (e: Error) => void;
13
12
  outputKeys: string | string[];
14
13
  streamKeys: string | string[];
15
14
  nodes: Record<string, PregelNode>;
@@ -29,7 +28,6 @@ type PregelLoopParams = {
29
28
  stop: number;
30
29
  outputKeys: string | string[];
31
30
  streamKeys: string | string[];
32
- onBackgroundError: (e: Error) => void;
33
31
  nodes: Record<string, PregelNode>;
34
32
  };
35
33
  export declare class PregelLoop {
@@ -48,13 +46,13 @@ export declare class PregelLoop {
48
46
  protected outputKeys: string | string[];
49
47
  protected streamKeys: string | string[];
50
48
  protected nodes: Record<string, PregelNode>;
49
+ protected skipDoneTasks: boolean;
51
50
  status: "pending" | "done" | "interrupt_before" | "interrupt_after" | "out_of_steps";
52
51
  tasks: PregelExecutableTask<any, any>[];
53
52
  stream: Deque<[StreamMode, any]>;
53
+ checkpointerPromises: Promise<unknown>[];
54
54
  protected isNested: boolean;
55
- protected _putCheckpointPromise: Promise<unknown>;
56
- onBackgroundError: (e: Error) => void;
57
- get backgroundTasksPromise(): Promise<unknown>;
55
+ protected _checkpointerChainedPromise: Promise<unknown>;
58
56
  constructor(params: PregelLoopParams);
59
57
  static initialize(params: PregelLoopInitializeParams): Promise<PregelLoop>;
60
58
  protected _checkpointerPutAfterPrevious(input: {
@@ -62,7 +60,7 @@ export declare class PregelLoop {
62
60
  checkpoint: Checkpoint;
63
61
  metadata: CheckpointMetadata;
64
62
  newVersions: Record<string, string | number>;
65
- }): Promise<void>;
63
+ }): void;
66
64
  /**
67
65
  * Put writes for a task, to be read by the next tick.
68
66
  * @param taskId
@@ -12,9 +12,6 @@ const INPUT_DONE = Symbol.for("INPUT_DONE");
12
12
  const INPUT_RESUMING = Symbol.for("INPUT_RESUMING");
13
13
  const DEFAULT_LOOP_LIMIT = 25;
14
14
  export class PregelLoop {
15
- get backgroundTasksPromise() {
16
- return this._putCheckpointPromise;
17
- }
18
15
  constructor(params) {
19
16
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
17
  Object.defineProperty(this, "input", {
@@ -107,6 +104,12 @@ export class PregelLoop {
107
104
  writable: true,
108
105
  value: void 0
109
106
  });
107
+ Object.defineProperty(this, "skipDoneTasks", {
108
+ enumerable: true,
109
+ configurable: true,
110
+ writable: true,
111
+ value: void 0
112
+ });
110
113
  Object.defineProperty(this, "status", {
111
114
  enumerable: true,
112
115
  configurable: true,
@@ -127,23 +130,23 @@ export class PregelLoop {
127
130
  writable: true,
128
131
  value: new Deque()
129
132
  });
130
- Object.defineProperty(this, "isNested", {
133
+ Object.defineProperty(this, "checkpointerPromises", {
131
134
  enumerable: true,
132
135
  configurable: true,
133
136
  writable: true,
134
- value: void 0
137
+ value: []
135
138
  });
136
- Object.defineProperty(this, "_putCheckpointPromise", {
139
+ Object.defineProperty(this, "isNested", {
137
140
  enumerable: true,
138
141
  configurable: true,
139
142
  writable: true,
140
- value: Promise.resolve()
143
+ value: void 0
141
144
  });
142
- Object.defineProperty(this, "onBackgroundError", {
145
+ Object.defineProperty(this, "_checkpointerChainedPromise", {
143
146
  enumerable: true,
144
147
  configurable: true,
145
148
  writable: true,
146
- value: void 0
149
+ value: Promise.resolve()
147
150
  });
148
151
  this.input = params.input;
149
152
  this.config = params.config;
@@ -168,7 +171,7 @@ export class PregelLoop {
168
171
  this.outputKeys = params.outputKeys;
169
172
  this.streamKeys = params.streamKeys;
170
173
  this.nodes = params.nodes;
171
- this.onBackgroundError = params.onBackgroundError;
174
+ this.skipDoneTasks = this.config.configurable?.checkpoint_id === undefined;
172
175
  }
173
176
  static async initialize(params) {
174
177
  const saved = (await params.checkpointer?.getTuple(params.config)) ?? {
@@ -211,19 +214,13 @@ export class PregelLoop {
211
214
  outputKeys: params.outputKeys ?? [],
212
215
  streamKeys: params.streamKeys ?? [],
213
216
  nodes: params.nodes,
214
- onBackgroundError: params.onBackgroundError,
215
217
  });
216
218
  }
217
- async _checkpointerPutAfterPrevious(input) {
218
- try {
219
- await this._putCheckpointPromise;
220
- }
221
- finally {
222
- this._putCheckpointPromise =
223
- this.checkpointer
224
- ?.put(input.config, input.checkpoint, input.metadata, input.newVersions)
225
- .catch(this.onBackgroundError) ?? Promise.resolve();
226
- }
219
+ _checkpointerPutAfterPrevious(input) {
220
+ this._checkpointerChainedPromise = this._checkpointerChainedPromise.then(() => {
221
+ return this.checkpointer?.put(input.config, input.checkpoint, input.metadata, input.newVersions);
222
+ });
223
+ this.checkpointerPromises.push(this._checkpointerChainedPromise);
227
224
  }
228
225
  /**
229
226
  * Put writes for a task, to be read by the next tick.
@@ -235,17 +232,16 @@ export class PregelLoop {
235
232
  return [taskId, key, value];
236
233
  });
237
234
  this.checkpointPendingWrites.push(...pendingWrites);
238
- if (this.checkpointer !== undefined) {
239
- void this.checkpointer
240
- .putWrites({
241
- ...this.checkpointConfig,
242
- configurable: {
243
- ...this.checkpointConfig.configurable,
244
- checkpoint_ns: this.config.configurable?.checkpoint_ns ?? "",
245
- checkpoint_id: this.checkpoint.id,
246
- },
247
- }, writes, taskId)
248
- .catch(this.onBackgroundError);
235
+ const putWritePromise = this.checkpointer?.putWrites({
236
+ ...this.checkpointConfig,
237
+ configurable: {
238
+ ...this.checkpointConfig.configurable,
239
+ checkpoint_ns: this.config.configurable?.checkpoint_ns ?? "",
240
+ checkpoint_id: this.checkpoint.id,
241
+ },
242
+ }, writes, taskId);
243
+ if (putWritePromise !== undefined) {
244
+ this.checkpointerPromises.push(putWritePromise);
249
245
  }
250
246
  const task = this.tasks.find((task) => task.id === taskId);
251
247
  if (task !== undefined) {
@@ -314,7 +310,7 @@ export class PregelLoop {
314
310
  return false;
315
311
  }
316
312
  // if there are pending writes from a previous loop, apply them
317
- if (this.checkpointPendingWrites.length > 0) {
313
+ if (this.checkpointPendingWrites.length > 0 && this.skipDoneTasks) {
318
314
  for (const [tid, k, v] of this.checkpointPendingWrites) {
319
315
  if (k === ERROR || k === INTERRUPT) {
320
316
  continue;
@@ -386,7 +382,7 @@ export class PregelLoop {
386
382
  // save input checkpoint
387
383
  await this._putCheckpoint({
388
384
  source: "input",
389
- writes: this.input ?? null,
385
+ writes: Object.fromEntries(inputWrites),
390
386
  });
391
387
  }
392
388
  // done with input
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph",
3
- "version": "0.2.1",
3
+ "version": "0.2.2-rc.0",
4
4
  "description": "LangGraph",
5
5
  "type": "module",
6
6
  "engines": {
@@ -31,7 +31,7 @@
31
31
  "author": "LangChain",
32
32
  "license": "MIT",
33
33
  "dependencies": {
34
- "@langchain/langgraph-checkpoint": "~0.0.4",
34
+ "@langchain/langgraph-checkpoint": "~0.0.5",
35
35
  "double-ended-queue": "^2.1.0-0",
36
36
  "uuid": "^10.0.0",
37
37
  "zod": "^3.23.8"