@langchain/langgraph 0.0.15 → 0.0.17

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.
@@ -1,8 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BaseCheckpointSaver = exports.copyCheckpoint = exports.emptyCheckpoint = exports.deepCopy = void 0;
3
+ exports.BaseCheckpointSaver = exports.copyCheckpoint = exports.emptyCheckpoint = exports.deepCopy = exports.getVersionSeen = exports.getChannelVersion = void 0;
4
4
  const base_js_1 = require("../serde/base.cjs");
5
5
  const id_js_1 = require("./id.cjs");
6
+ function getChannelVersion(checkpoint, channel) {
7
+ return checkpoint.channel_versions[channel] ?? 0;
8
+ }
9
+ exports.getChannelVersion = getChannelVersion;
10
+ function getVersionSeen(checkpoint, node, channel) {
11
+ return checkpoint.versions_seen[node]?.[channel] ?? 0;
12
+ }
13
+ exports.getVersionSeen = getVersionSeen;
6
14
  function deepCopy(obj) {
7
15
  if (typeof obj !== "object" || obj === null) {
8
16
  return obj;
@@ -15,7 +15,7 @@ export interface CheckpointMetadata {
15
15
  * ... for the nth checkpoint afterwards. */
16
16
  writes?: Record<string, unknown>;
17
17
  }
18
- export interface Checkpoint {
18
+ export interface Checkpoint<N extends string = string, C extends string = string> {
19
19
  /**
20
20
  * Version number
21
21
  */
@@ -31,21 +31,23 @@ export interface Checkpoint {
31
31
  /**
32
32
  * @default {}
33
33
  */
34
- channel_values: Record<string, unknown>;
34
+ channel_values: Record<C, unknown>;
35
35
  /**
36
36
  * @default {}
37
37
  */
38
- channel_versions: Record<string, number>;
38
+ channel_versions: Record<C, number>;
39
39
  /**
40
40
  * @default {}
41
41
  */
42
- versions_seen: Record<string, Record<string, number>>;
42
+ versions_seen: Record<N, Record<C, number>>;
43
43
  }
44
44
  export interface ReadonlyCheckpoint extends Readonly<Checkpoint> {
45
45
  readonly channel_values: Readonly<Record<string, unknown>>;
46
46
  readonly channel_versions: Readonly<Record<string, number>>;
47
47
  readonly versions_seen: Readonly<Record<string, Readonly<Record<string, number>>>>;
48
48
  }
49
+ export declare function getChannelVersion(checkpoint: ReadonlyCheckpoint, channel: string): number;
50
+ export declare function getVersionSeen(checkpoint: ReadonlyCheckpoint, node: string, channel: string): number;
49
51
  export declare function deepCopy<T>(obj: T): T;
50
52
  export declare function emptyCheckpoint(): Checkpoint;
51
53
  export declare function copyCheckpoint(checkpoint: ReadonlyCheckpoint): Checkpoint;
@@ -1,5 +1,11 @@
1
1
  import { DefaultSerializer } from "../serde/base.js";
2
2
  import { uuid6 } from "./id.js";
3
+ export function getChannelVersion(checkpoint, channel) {
4
+ return checkpoint.channel_versions[channel] ?? 0;
5
+ }
6
+ export function getVersionSeen(checkpoint, node, channel) {
7
+ return checkpoint.versions_seen[node]?.[channel] ?? 0;
8
+ }
3
9
  export function deepCopy(obj) {
4
10
  if (typeof obj !== "object" || obj === null) {
5
11
  return obj;
@@ -406,7 +406,10 @@ class Pregel extends runnables_1.Runnable {
406
406
  else {
407
407
  checkpoint = (0, base_js_2.copyCheckpoint)(checkpoint);
408
408
  for (const k of this.streamChannelsList) {
409
- const version = checkpoint.channel_versions[k];
409
+ const version = checkpoint.channel_versions[k] ?? 0;
410
+ if (!checkpoint.versions_seen[constants_js_1.INTERRUPT]) {
411
+ checkpoint.versions_seen[constants_js_1.INTERRUPT] = {};
412
+ }
410
413
  checkpoint.versions_seen[constants_js_1.INTERRUPT][k] = version;
411
414
  }
412
415
  }
@@ -551,8 +554,8 @@ async function executeTasks(tasks, stepTimeout) {
551
554
  }
552
555
  }
553
556
  function _shouldInterrupt(checkpoint, interruptNodes, snapshotChannels, tasks) {
554
- const seen = checkpoint.versions_seen[constants_js_1.INTERRUPT];
555
- const anySnapshotChannelUpdated = snapshotChannels.some((chan) => checkpoint.channel_versions[chan] > seen?.[chan]);
557
+ const anySnapshotChannelUpdated = snapshotChannels.some((chan) => (0, base_js_2.getChannelVersion)(checkpoint, chan) >
558
+ (0, base_js_2.getVersionSeen)(checkpoint, constants_js_1.INTERRUPT, chan));
556
559
  const anyTaskNodeInInterruptNodes = tasks.some((task) => interruptNodes === "*"
557
560
  ? !task.config?.tags?.includes(constants_js_1.TAG_HIDDEN)
558
561
  : interruptNodes.includes(task.name));
@@ -627,11 +630,6 @@ function _prepareNextTasks(checkpoint, processes, channels, forExecution) {
627
630
  // Check if any processes should be run in next step
628
631
  // If so, prepare the values to be passed to them
629
632
  for (const [name, proc] of Object.entries(processes)) {
630
- let seen = newCheckpoint.versions_seen[name];
631
- if (!seen) {
632
- newCheckpoint.versions_seen[name] = {};
633
- seen = newCheckpoint.versions_seen[name];
634
- }
635
633
  // If any of the channels read by this process were updated
636
634
  if (proc.triggers
637
635
  .filter((chan) => {
@@ -643,7 +641,8 @@ function _prepareNextTasks(checkpoint, processes, channels, forExecution) {
643
641
  return false;
644
642
  }
645
643
  })
646
- .some((chan) => newCheckpoint.channel_versions[chan] > (seen[chan] ?? 0))) {
644
+ .some((chan) => (0, base_js_2.getChannelVersion)(newCheckpoint, chan) >
645
+ (0, base_js_2.getVersionSeen)(newCheckpoint, name, chan))) {
647
646
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
648
647
  let val;
649
648
  // If all trigger channels subscribed by this process are not empty
@@ -696,11 +695,14 @@ function _prepareNextTasks(checkpoint, processes, channels, forExecution) {
696
695
  }
697
696
  if (forExecution) {
698
697
  // Update seen versions
698
+ if (!newCheckpoint.versions_seen[name]) {
699
+ newCheckpoint.versions_seen[name] = {};
700
+ }
699
701
  proc.triggers.forEach((chan) => {
700
702
  const version = newCheckpoint.channel_versions[chan];
701
703
  if (version !== undefined) {
702
704
  // side effect: updates newCheckpoint
703
- seen[chan] = version;
705
+ newCheckpoint.versions_seen[name][chan] = version;
704
706
  }
705
707
  });
706
708
  const node = proc.getNode();
@@ -2,7 +2,7 @@
2
2
  import { Runnable, RunnableSequence, _coerceToRunnable, ensureConfig, patchConfig, } from "@langchain/core/runnables";
3
3
  import { IterableReadableStream } from "@langchain/core/utils/stream";
4
4
  import { createCheckpoint, emptyChannels, } from "../channels/base.js";
5
- import { copyCheckpoint, emptyCheckpoint, } from "../checkpoint/base.js";
5
+ import { copyCheckpoint, emptyCheckpoint, getChannelVersion, getVersionSeen, } from "../checkpoint/base.js";
6
6
  import { PregelNode } from "./read.js";
7
7
  import { validateGraph, validateKeys } from "./validate.js";
8
8
  import { mapInput, mapOutputUpdates, mapOutputValues, readChannel, readChannels, single, } from "./io.js";
@@ -402,7 +402,10 @@ export class Pregel extends Runnable {
402
402
  else {
403
403
  checkpoint = copyCheckpoint(checkpoint);
404
404
  for (const k of this.streamChannelsList) {
405
- const version = checkpoint.channel_versions[k];
405
+ const version = checkpoint.channel_versions[k] ?? 0;
406
+ if (!checkpoint.versions_seen[INTERRUPT]) {
407
+ checkpoint.versions_seen[INTERRUPT] = {};
408
+ }
406
409
  checkpoint.versions_seen[INTERRUPT][k] = version;
407
410
  }
408
411
  }
@@ -546,8 +549,8 @@ async function executeTasks(tasks, stepTimeout) {
546
549
  }
547
550
  }
548
551
  export function _shouldInterrupt(checkpoint, interruptNodes, snapshotChannels, tasks) {
549
- const seen = checkpoint.versions_seen[INTERRUPT];
550
- const anySnapshotChannelUpdated = snapshotChannels.some((chan) => checkpoint.channel_versions[chan] > seen?.[chan]);
552
+ const anySnapshotChannelUpdated = snapshotChannels.some((chan) => getChannelVersion(checkpoint, chan) >
553
+ getVersionSeen(checkpoint, INTERRUPT, chan));
551
554
  const anyTaskNodeInInterruptNodes = tasks.some((task) => interruptNodes === "*"
552
555
  ? !task.config?.tags?.includes(TAG_HIDDEN)
553
556
  : interruptNodes.includes(task.name));
@@ -619,11 +622,6 @@ export function _prepareNextTasks(checkpoint, processes, channels, forExecution)
619
622
  // Check if any processes should be run in next step
620
623
  // If so, prepare the values to be passed to them
621
624
  for (const [name, proc] of Object.entries(processes)) {
622
- let seen = newCheckpoint.versions_seen[name];
623
- if (!seen) {
624
- newCheckpoint.versions_seen[name] = {};
625
- seen = newCheckpoint.versions_seen[name];
626
- }
627
625
  // If any of the channels read by this process were updated
628
626
  if (proc.triggers
629
627
  .filter((chan) => {
@@ -635,7 +633,8 @@ export function _prepareNextTasks(checkpoint, processes, channels, forExecution)
635
633
  return false;
636
634
  }
637
635
  })
638
- .some((chan) => newCheckpoint.channel_versions[chan] > (seen[chan] ?? 0))) {
636
+ .some((chan) => getChannelVersion(newCheckpoint, chan) >
637
+ getVersionSeen(newCheckpoint, name, chan))) {
639
638
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
640
639
  let val;
641
640
  // If all trigger channels subscribed by this process are not empty
@@ -688,11 +687,14 @@ export function _prepareNextTasks(checkpoint, processes, channels, forExecution)
688
687
  }
689
688
  if (forExecution) {
690
689
  // Update seen versions
690
+ if (!newCheckpoint.versions_seen[name]) {
691
+ newCheckpoint.versions_seen[name] = {};
692
+ }
691
693
  proc.triggers.forEach((chan) => {
692
694
  const version = newCheckpoint.channel_versions[chan];
693
695
  if (version !== undefined) {
694
696
  // side effect: updates newCheckpoint
695
- seen[chan] = version;
697
+ newCheckpoint.versions_seen[name][chan] = version;
696
698
  }
697
699
  });
698
700
  const node = proc.getNode();
@@ -53,11 +53,10 @@ function* mapInput(inputChannels,
53
53
  chunk
54
54
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
55
  ) {
56
- if (chunk) {
56
+ if (chunk !== undefined && chunk !== null) {
57
57
  if (Array.isArray(inputChannels) &&
58
58
  typeof chunk === "object" &&
59
- !Array.isArray(chunk) &&
60
- !!chunk) {
59
+ !Array.isArray(chunk)) {
61
60
  for (const k in chunk) {
62
61
  if (inputChannels.includes(k)) {
63
62
  yield [k, chunk[k]];
package/dist/pregel/io.js CHANGED
@@ -48,11 +48,10 @@ export function* mapInput(inputChannels,
48
48
  chunk
49
49
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
50
  ) {
51
- if (chunk) {
51
+ if (chunk !== undefined && chunk !== null) {
52
52
  if (Array.isArray(inputChannels) &&
53
53
  typeof chunk === "object" &&
54
- !Array.isArray(chunk) &&
55
- !!chunk) {
54
+ !Array.isArray(chunk)) {
56
55
  for (const k in chunk) {
57
56
  if (inputChannels.includes(k)) {
58
57
  yield [k, chunk[k]];
@@ -209,6 +209,33 @@ describe("_shouldInterrupt", () => {
209
209
  },
210
210
  ])).toBe(true);
211
211
  });
212
+ it("should return true if any snapshot channel has been updated since last interrupt and any channel written to is in interrupt nodes list", () => {
213
+ // set up test
214
+ const checkpoint = {
215
+ v: 1,
216
+ id: uuid6(-1),
217
+ ts: "2024-04-19T17:19:07.952Z",
218
+ channel_values: {
219
+ channel1: "channel1value",
220
+ },
221
+ channel_versions: {
222
+ channel1: 2, // current channel version is greater than last version seen
223
+ },
224
+ versions_seen: {},
225
+ };
226
+ const interruptNodes = ["node1"];
227
+ const snapshotChannels = ["channel1"];
228
+ // call method / assertions
229
+ expect(_shouldInterrupt(checkpoint, interruptNodes, snapshotChannels, [
230
+ {
231
+ name: "node1",
232
+ input: undefined,
233
+ proc: new RunnablePassthrough(),
234
+ writes: [],
235
+ config: undefined,
236
+ },
237
+ ])).toBe(true);
238
+ });
212
239
  it("should return false if all snapshot channels have not been updated", () => {
213
240
  // set up test
214
241
  const checkpoint = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "LangGraph",
5
5
  "type": "module",
6
6
  "engines": {