@langchain/langgraph 0.0.14 → 0.0.16

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,7 +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
+ const base_js_1 = require("../serde/base.cjs");
4
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;
5
14
  function deepCopy(obj) {
6
15
  if (typeof obj !== "object" || obj === null) {
7
16
  return obj;
@@ -43,7 +52,7 @@ class BaseCheckpointSaver {
43
52
  enumerable: true,
44
53
  configurable: true,
45
54
  writable: true,
46
- value: JSON
55
+ value: base_js_1.DefaultSerializer
47
56
  });
48
57
  this.serde = serde || this.serde;
49
58
  }
@@ -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,4 +1,11 @@
1
+ import { DefaultSerializer } from "../serde/base.js";
1
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
+ }
2
9
  export function deepCopy(obj) {
3
10
  if (typeof obj !== "object" || obj === null) {
4
11
  return obj;
@@ -37,7 +44,7 @@ export class BaseCheckpointSaver {
37
44
  enumerable: true,
38
45
  configurable: true,
39
46
  writable: true,
40
- value: JSON
47
+ value: DefaultSerializer
41
48
  });
42
49
  this.serde = serde || this.serde;
43
50
  }
@@ -22,8 +22,8 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
22
22
  if (checkpoint) {
23
23
  return {
24
24
  config,
25
- checkpoint: this.serde.parse(checkpoint[0]),
26
- metadata: this.serde.parse(checkpoint[1]),
25
+ checkpoint: (await this.serde.parse(checkpoint[0])),
26
+ metadata: (await this.serde.parse(checkpoint[1])),
27
27
  };
28
28
  }
29
29
  }
@@ -33,8 +33,8 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
33
33
  const checkpoint = checkpoints[maxThreadTs];
34
34
  return {
35
35
  config: { configurable: { thread_id, checkpoint_id: maxThreadTs } },
36
- checkpoint: this.serde.parse(checkpoint[0]),
37
- metadata: this.serde.parse(checkpoint[1]),
36
+ checkpoint: (await this.serde.parse(checkpoint[0])),
37
+ metadata: (await this.serde.parse(checkpoint[1])),
38
38
  };
39
39
  }
40
40
  }
@@ -50,8 +50,8 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
50
50
  .slice(0, limit)) {
51
51
  yield {
52
52
  config: { configurable: { thread_id, checkpoint_id } },
53
- checkpoint: this.serde.parse(checkpoint[0]),
54
- metadata: this.serde.parse(checkpoint[1]),
53
+ checkpoint: (await this.serde.parse(checkpoint[0])),
54
+ metadata: (await this.serde.parse(checkpoint[1])),
55
55
  };
56
56
  }
57
57
  }
@@ -19,8 +19,8 @@ export class MemorySaver extends BaseCheckpointSaver {
19
19
  if (checkpoint) {
20
20
  return {
21
21
  config,
22
- checkpoint: this.serde.parse(checkpoint[0]),
23
- metadata: this.serde.parse(checkpoint[1]),
22
+ checkpoint: (await this.serde.parse(checkpoint[0])),
23
+ metadata: (await this.serde.parse(checkpoint[1])),
24
24
  };
25
25
  }
26
26
  }
@@ -30,8 +30,8 @@ export class MemorySaver extends BaseCheckpointSaver {
30
30
  const checkpoint = checkpoints[maxThreadTs];
31
31
  return {
32
32
  config: { configurable: { thread_id, checkpoint_id: maxThreadTs } },
33
- checkpoint: this.serde.parse(checkpoint[0]),
34
- metadata: this.serde.parse(checkpoint[1]),
33
+ checkpoint: (await this.serde.parse(checkpoint[0])),
34
+ metadata: (await this.serde.parse(checkpoint[1])),
35
35
  };
36
36
  }
37
37
  }
@@ -47,8 +47,8 @@ export class MemorySaver extends BaseCheckpointSaver {
47
47
  .slice(0, limit)) {
48
48
  yield {
49
49
  config: { configurable: { thread_id, checkpoint_id } },
50
- checkpoint: this.serde.parse(checkpoint[0]),
51
- metadata: this.serde.parse(checkpoint[1]),
50
+ checkpoint: (await this.serde.parse(checkpoint[0])),
51
+ metadata: (await this.serde.parse(checkpoint[1])),
52
52
  };
53
53
  }
54
54
  }
@@ -61,8 +61,8 @@ CREATE TABLE IF NOT EXISTS checkpoints (
61
61
  if (row) {
62
62
  return {
63
63
  config,
64
- checkpoint: this.serde.parse(row.checkpoint),
65
- metadata: this.serde.parse(row.metadata),
64
+ checkpoint: (await this.serde.parse(row.checkpoint)),
65
+ metadata: (await this.serde.parse(row.metadata)),
66
66
  parentConfig: row.parent_id
67
67
  ? {
68
68
  configurable: {
@@ -91,8 +91,8 @@ CREATE TABLE IF NOT EXISTS checkpoints (
91
91
  checkpoint_id: row.checkpoint_id,
92
92
  },
93
93
  },
94
- checkpoint: this.serde.parse(row.checkpoint),
95
- metadata: this.serde.parse(row.metadata),
94
+ checkpoint: (await this.serde.parse(row.checkpoint)),
95
+ metadata: (await this.serde.parse(row.metadata)),
96
96
  parentConfig: row.parent_id
97
97
  ? {
98
98
  configurable: {
@@ -125,8 +125,8 @@ CREATE TABLE IF NOT EXISTS checkpoints (
125
125
  checkpoint_id: row.checkpoint_id,
126
126
  },
127
127
  },
128
- checkpoint: this.serde.parse(row.checkpoint),
129
- metadata: this.serde.parse(row.metadata),
128
+ checkpoint: (await this.serde.parse(row.checkpoint)),
129
+ metadata: (await this.serde.parse(row.metadata)),
130
130
  parentConfig: row.parent_id
131
131
  ? {
132
132
  configurable: {
@@ -55,8 +55,8 @@ CREATE TABLE IF NOT EXISTS checkpoints (
55
55
  if (row) {
56
56
  return {
57
57
  config,
58
- checkpoint: this.serde.parse(row.checkpoint),
59
- metadata: this.serde.parse(row.metadata),
58
+ checkpoint: (await this.serde.parse(row.checkpoint)),
59
+ metadata: (await this.serde.parse(row.metadata)),
60
60
  parentConfig: row.parent_id
61
61
  ? {
62
62
  configurable: {
@@ -85,8 +85,8 @@ CREATE TABLE IF NOT EXISTS checkpoints (
85
85
  checkpoint_id: row.checkpoint_id,
86
86
  },
87
87
  },
88
- checkpoint: this.serde.parse(row.checkpoint),
89
- metadata: this.serde.parse(row.metadata),
88
+ checkpoint: (await this.serde.parse(row.checkpoint)),
89
+ metadata: (await this.serde.parse(row.metadata)),
90
90
  parentConfig: row.parent_id
91
91
  ? {
92
92
  configurable: {
@@ -119,8 +119,8 @@ CREATE TABLE IF NOT EXISTS checkpoints (
119
119
  checkpoint_id: row.checkpoint_id,
120
120
  },
121
121
  },
122
- checkpoint: this.serde.parse(row.checkpoint),
123
- metadata: this.serde.parse(row.metadata),
122
+ checkpoint: (await this.serde.parse(row.checkpoint)),
123
+ metadata: (await this.serde.parse(row.metadata)),
124
124
  parentConfig: row.parent_id
125
125
  ? {
126
126
  configurable: {
@@ -551,8 +551,8 @@ async function executeTasks(tasks, stepTimeout) {
551
551
  }
552
552
  }
553
553
  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]);
554
+ const anySnapshotChannelUpdated = snapshotChannels.some((chan) => (0, base_js_2.getChannelVersion)(checkpoint, chan) >
555
+ (0, base_js_2.getVersionSeen)(checkpoint, constants_js_1.INTERRUPT, chan));
556
556
  const anyTaskNodeInInterruptNodes = tasks.some((task) => interruptNodes === "*"
557
557
  ? !task.config?.tags?.includes(constants_js_1.TAG_HIDDEN)
558
558
  : interruptNodes.includes(task.name));
@@ -627,11 +627,6 @@ function _prepareNextTasks(checkpoint, processes, channels, forExecution) {
627
627
  // Check if any processes should be run in next step
628
628
  // If so, prepare the values to be passed to them
629
629
  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
630
  // If any of the channels read by this process were updated
636
631
  if (proc.triggers
637
632
  .filter((chan) => {
@@ -643,7 +638,8 @@ function _prepareNextTasks(checkpoint, processes, channels, forExecution) {
643
638
  return false;
644
639
  }
645
640
  })
646
- .some((chan) => newCheckpoint.channel_versions[chan] > (seen[chan] ?? 0))) {
641
+ .some((chan) => (0, base_js_2.getChannelVersion)(newCheckpoint, chan) >
642
+ (0, base_js_2.getVersionSeen)(newCheckpoint, name, chan))) {
647
643
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
648
644
  let val;
649
645
  // If all trigger channels subscribed by this process are not empty
@@ -696,11 +692,14 @@ function _prepareNextTasks(checkpoint, processes, channels, forExecution) {
696
692
  }
697
693
  if (forExecution) {
698
694
  // Update seen versions
695
+ if (!newCheckpoint.versions_seen[name]) {
696
+ newCheckpoint.versions_seen[name] = {};
697
+ }
699
698
  proc.triggers.forEach((chan) => {
700
699
  const version = newCheckpoint.channel_versions[chan];
701
700
  if (version !== undefined) {
702
701
  // side effect: updates newCheckpoint
703
- seen[chan] = version;
702
+ newCheckpoint.versions_seen[name][chan] = version;
704
703
  }
705
704
  });
706
705
  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";
@@ -546,8 +546,8 @@ async function executeTasks(tasks, stepTimeout) {
546
546
  }
547
547
  }
548
548
  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]);
549
+ const anySnapshotChannelUpdated = snapshotChannels.some((chan) => getChannelVersion(checkpoint, chan) >
550
+ getVersionSeen(checkpoint, INTERRUPT, chan));
551
551
  const anyTaskNodeInInterruptNodes = tasks.some((task) => interruptNodes === "*"
552
552
  ? !task.config?.tags?.includes(TAG_HIDDEN)
553
553
  : interruptNodes.includes(task.name));
@@ -619,11 +619,6 @@ export function _prepareNextTasks(checkpoint, processes, channels, forExecution)
619
619
  // Check if any processes should be run in next step
620
620
  // If so, prepare the values to be passed to them
621
621
  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
622
  // If any of the channels read by this process were updated
628
623
  if (proc.triggers
629
624
  .filter((chan) => {
@@ -635,7 +630,8 @@ export function _prepareNextTasks(checkpoint, processes, channels, forExecution)
635
630
  return false;
636
631
  }
637
632
  })
638
- .some((chan) => newCheckpoint.channel_versions[chan] > (seen[chan] ?? 0))) {
633
+ .some((chan) => getChannelVersion(newCheckpoint, chan) >
634
+ getVersionSeen(newCheckpoint, name, chan))) {
639
635
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
640
636
  let val;
641
637
  // If all trigger channels subscribed by this process are not empty
@@ -688,11 +684,14 @@ export function _prepareNextTasks(checkpoint, processes, channels, forExecution)
688
684
  }
689
685
  if (forExecution) {
690
686
  // Update seen versions
687
+ if (!newCheckpoint.versions_seen[name]) {
688
+ newCheckpoint.versions_seen[name] = {};
689
+ }
691
690
  proc.triggers.forEach((chan) => {
692
691
  const version = newCheckpoint.channel_versions[chan];
693
692
  if (version !== undefined) {
694
693
  // side effect: updates newCheckpoint
695
- seen[chan] = version;
694
+ newCheckpoint.versions_seen[name][chan] = version;
696
695
  }
697
696
  });
698
697
  const node = proc.getNode();
@@ -1,2 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultSerializer = void 0;
4
+ const load_1 = require("@langchain/core/load");
5
+ exports.DefaultSerializer = {
6
+ stringify: JSON.stringify,
7
+ parse: load_1.load,
8
+ };
@@ -1,4 +1,12 @@
1
+ import { load } from "@langchain/core/load";
1
2
  export interface SerializerProtocol<D> {
2
3
  stringify(obj: D): string;
3
- parse(data: string): D;
4
+ parse(data: string): Promise<D>;
4
5
  }
6
+ export declare const DefaultSerializer: {
7
+ stringify: {
8
+ (value: any, replacer?: ((this: any, key: string, value: any) => any) | undefined, space?: string | number | undefined): string;
9
+ (value: any, replacer?: (string | number)[] | null | undefined, space?: string | number | undefined): string;
10
+ };
11
+ parse: typeof load;
12
+ };
@@ -1 +1,5 @@
1
- export {};
1
+ import { load } from "@langchain/core/load";
2
+ export const DefaultSerializer = {
3
+ stringify: JSON.stringify,
4
+ parse: load,
5
+ };
@@ -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.14",
3
+ "version": "0.0.16",
4
4
  "description": "LangGraph",
5
5
  "type": "module",
6
6
  "engines": {