@langchain/langgraph-checkpoint 0.0.13 → 0.0.15

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.
package/dist/base.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WRITES_IDX_MAP = exports.maxChannelVersion = exports.compareChannelVersions = exports.BaseCheckpointSaver = exports.copyCheckpoint = exports.emptyCheckpoint = exports.deepCopy = void 0;
3
+ exports.getCheckpointId = exports.WRITES_IDX_MAP = exports.maxChannelVersion = exports.compareChannelVersions = exports.BaseCheckpointSaver = exports.copyCheckpoint = exports.emptyCheckpoint = exports.deepCopy = void 0;
4
4
  const id_js_1 = require("./id.cjs");
5
5
  const types_js_1 = require("./serde/types.cjs");
6
6
  const jsonplus_js_1 = require("./serde/jsonplus.cjs");
@@ -99,3 +99,7 @@ exports.WRITES_IDX_MAP = {
99
99
  [types_js_1.INTERRUPT]: -3,
100
100
  [types_js_1.RESUME]: -4,
101
101
  };
102
+ function getCheckpointId(config) {
103
+ return (config.configurable?.checkpoint_id || config.configurable?.thread_ts || "");
104
+ }
105
+ exports.getCheckpointId = getCheckpointId;
package/dist/base.d.ts CHANGED
@@ -86,4 +86,5 @@ export declare function maxChannelVersion(...versions: ChannelVersion[]): Channe
86
86
  * Each Checkpointer implementation should use this mapping in put_writes.
87
87
  */
88
88
  export declare const WRITES_IDX_MAP: Record<string, number>;
89
+ export declare function getCheckpointId(config: RunnableConfig): string;
89
90
  export {};
package/dist/base.js CHANGED
@@ -90,3 +90,6 @@ export const WRITES_IDX_MAP = {
90
90
  [INTERRUPT]: -3,
91
91
  [RESUME]: -4,
92
92
  };
93
+ export function getCheckpointId(config) {
94
+ return (config.configurable?.checkpoint_id || config.configurable?.thread_ts || "");
95
+ }
package/dist/memory.cjs CHANGED
@@ -26,7 +26,8 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
26
26
  async _getPendingSends(threadId, checkpointNs, parentCheckpointId) {
27
27
  let pendingSends = [];
28
28
  if (parentCheckpointId !== undefined) {
29
- pendingSends = await Promise.all(this.writes[_generateKey(threadId, checkpointNs, parentCheckpointId)]
29
+ const key = _generateKey(threadId, checkpointNs, parentCheckpointId);
30
+ pendingSends = await Promise.all(Object.values(this.writes[key] || {})
30
31
  ?.filter(([_taskId, channel]) => {
31
32
  return channel === types_js_1.TASKS;
32
33
  })
@@ -39,19 +40,18 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
39
40
  async getTuple(config) {
40
41
  const thread_id = config.configurable?.thread_id;
41
42
  const checkpoint_ns = config.configurable?.checkpoint_ns ?? "";
42
- let checkpoint_id = config.configurable?.checkpoint_id;
43
+ let checkpoint_id = (0, base_js_1.getCheckpointId)(config);
43
44
  if (checkpoint_id) {
44
45
  const saved = this.storage[thread_id]?.[checkpoint_ns]?.[checkpoint_id];
45
46
  if (saved !== undefined) {
46
47
  const [checkpoint, metadata, parentCheckpointId] = saved;
47
- const writes = this.writes[_generateKey(thread_id, checkpoint_ns, checkpoint_id)] ??
48
- [];
48
+ const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);
49
49
  const pending_sends = await this._getPendingSends(thread_id, checkpoint_ns, parentCheckpointId);
50
50
  const deserializedCheckpoint = {
51
51
  ...(await this.serde.loadsTyped("json", checkpoint)),
52
52
  pending_sends,
53
53
  };
54
- const pendingWrites = await Promise.all(writes.map(async ([taskId, channel, value]) => {
54
+ const pendingWrites = await Promise.all(Object.values(this.writes[key] || {}).map(async ([taskId, channel, value]) => {
55
55
  return [
56
56
  taskId,
57
57
  channel,
@@ -83,14 +83,13 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
83
83
  checkpoint_id = Object.keys(checkpoints).sort((a, b) => b.localeCompare(a))[0];
84
84
  const saved = checkpoints[checkpoint_id];
85
85
  const [checkpoint, metadata, parentCheckpointId] = saved;
86
- const writes = this.writes[_generateKey(thread_id, checkpoint_ns, checkpoint_id)] ??
87
- [];
86
+ const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);
88
87
  const pending_sends = await this._getPendingSends(thread_id, checkpoint_ns, parentCheckpointId);
89
88
  const deserializedCheckpoint = {
90
89
  ...(await this.serde.loadsTyped("json", checkpoint)),
91
90
  pending_sends,
92
91
  };
93
- const pendingWrites = await Promise.all(writes.map(async ([taskId, channel, value]) => {
92
+ const pendingWrites = await Promise.all(Object.values(this.writes[key] || {}).map(async ([taskId, channel, value]) => {
94
93
  return [
95
94
  taskId,
96
95
  channel,
@@ -130,6 +129,7 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
130
129
  ? [config.configurable?.thread_id]
131
130
  : Object.keys(this.storage);
132
131
  const configCheckpointNamespace = config.configurable?.checkpoint_ns;
132
+ const configCheckpointId = config.configurable?.checkpoint_id;
133
133
  for (const threadId of threadIds) {
134
134
  for (const checkpointNamespace of Object.keys(this.storage[threadId] ?? {})) {
135
135
  if (configCheckpointNamespace !== undefined &&
@@ -139,7 +139,11 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
139
139
  const checkpoints = this.storage[threadId]?.[checkpointNamespace] ?? {};
140
140
  const sortedCheckpoints = Object.entries(checkpoints).sort((a, b) => b[0].localeCompare(a[0]));
141
141
  for (const [checkpointId, [checkpoint, metadataStr, parentCheckpointId],] of sortedCheckpoints) {
142
- // Filter by checkpoint ID
142
+ // Filter by checkpoint ID from config
143
+ if (configCheckpointId && checkpointId !== configCheckpointId) {
144
+ continue;
145
+ }
146
+ // Filter by checkpoint ID from before config
143
147
  if (before &&
144
148
  before.configurable?.checkpoint_id &&
145
149
  checkpointId >= before.configurable.checkpoint_id) {
@@ -155,10 +159,10 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
155
159
  if (limit !== undefined) {
156
160
  if (limit <= 0)
157
161
  break;
158
- // eslint-disable-next-line no-param-reassign
159
162
  limit -= 1;
160
163
  }
161
- const writes = this.writes[_generateKey(threadId, checkpointNamespace, checkpointId)] ?? [];
164
+ const key = _generateKey(threadId, checkpointNamespace, checkpointId);
165
+ const writes = Object.values(this.writes[key] || {});
162
166
  const pending_sends = await this._getPendingSends(threadId, checkpointNamespace, parentCheckpointId);
163
167
  const pendingWrites = await Promise.all(writes.map(async ([taskId, channel, value]) => {
164
168
  return [
@@ -236,15 +240,23 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
236
240
  if (checkpointId === undefined) {
237
241
  throw new Error(`Failed to put writes. The passed RunnableConfig is missing a required "checkpoint_id" field in its "configurable" property.`);
238
242
  }
239
- const key = _generateKey(threadId, checkpointNamespace, checkpointId);
240
- if (this.writes[key] === undefined) {
241
- this.writes[key] = [];
243
+ const outerKey = _generateKey(threadId, checkpointNamespace, checkpointId);
244
+ const outerWrites_ = this.writes[outerKey];
245
+ if (this.writes[outerKey] === undefined) {
246
+ this.writes[outerKey] = {};
242
247
  }
243
- const pendingWrites = writes.map(([channel, value]) => {
248
+ writes.forEach(([channel, value], idx) => {
244
249
  const [, serializedValue] = this.serde.dumpsTyped(value);
245
- return [taskId, channel, serializedValue];
250
+ const innerKey = [
251
+ taskId,
252
+ base_js_1.WRITES_IDX_MAP[channel] || idx,
253
+ ];
254
+ const innerKeyStr = `${innerKey[0]},${innerKey[1]}`;
255
+ if (innerKey[1] >= 0 && outerWrites_ && innerKeyStr in outerWrites_) {
256
+ return;
257
+ }
258
+ this.writes[outerKey][innerKeyStr] = [taskId, channel, serializedValue];
246
259
  });
247
- this.writes[key].push(...pendingWrites);
248
260
  }
249
261
  }
250
262
  exports.MemorySaver = MemorySaver;
package/dist/memory.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import type { RunnableConfig } from "@langchain/core/runnables";
2
2
  import { BaseCheckpointSaver, Checkpoint, CheckpointListOptions, CheckpointTuple } from "./base.js";
3
3
  import { SerializerProtocol } from "./serde/base.js";
4
- import { CheckpointMetadata, CheckpointPendingWrite, PendingWrite } from "./types.js";
4
+ import { CheckpointMetadata, PendingWrite } from "./types.js";
5
5
  import { SendProtocol } from "./serde/types.js";
6
6
  export declare class MemorySaver extends BaseCheckpointSaver {
7
7
  storage: Record<string, Record<string, Record<string, [Uint8Array, Uint8Array, string | undefined]>>>;
8
- writes: Record<string, CheckpointPendingWrite[]>;
8
+ writes: Record<string, Record<string, [string, string, Uint8Array]>>;
9
9
  constructor(serde?: SerializerProtocol);
10
10
  _getPendingSends(threadId: string, checkpointNs: string, parentCheckpointId?: string): Promise<SendProtocol[]>;
11
11
  getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;
package/dist/memory.js CHANGED
@@ -1,4 +1,4 @@
1
- import { BaseCheckpointSaver, copyCheckpoint, } from "./base.js";
1
+ import { BaseCheckpointSaver, copyCheckpoint, getCheckpointId, WRITES_IDX_MAP, } from "./base.js";
2
2
  import { TASKS } from "./serde/types.js";
3
3
  function _generateKey(threadId, checkpointNamespace, checkpointId) {
4
4
  return JSON.stringify([threadId, checkpointNamespace, checkpointId]);
@@ -23,7 +23,8 @@ export class MemorySaver extends BaseCheckpointSaver {
23
23
  async _getPendingSends(threadId, checkpointNs, parentCheckpointId) {
24
24
  let pendingSends = [];
25
25
  if (parentCheckpointId !== undefined) {
26
- pendingSends = await Promise.all(this.writes[_generateKey(threadId, checkpointNs, parentCheckpointId)]
26
+ const key = _generateKey(threadId, checkpointNs, parentCheckpointId);
27
+ pendingSends = await Promise.all(Object.values(this.writes[key] || {})
27
28
  ?.filter(([_taskId, channel]) => {
28
29
  return channel === TASKS;
29
30
  })
@@ -36,19 +37,18 @@ export class MemorySaver extends BaseCheckpointSaver {
36
37
  async getTuple(config) {
37
38
  const thread_id = config.configurable?.thread_id;
38
39
  const checkpoint_ns = config.configurable?.checkpoint_ns ?? "";
39
- let checkpoint_id = config.configurable?.checkpoint_id;
40
+ let checkpoint_id = getCheckpointId(config);
40
41
  if (checkpoint_id) {
41
42
  const saved = this.storage[thread_id]?.[checkpoint_ns]?.[checkpoint_id];
42
43
  if (saved !== undefined) {
43
44
  const [checkpoint, metadata, parentCheckpointId] = saved;
44
- const writes = this.writes[_generateKey(thread_id, checkpoint_ns, checkpoint_id)] ??
45
- [];
45
+ const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);
46
46
  const pending_sends = await this._getPendingSends(thread_id, checkpoint_ns, parentCheckpointId);
47
47
  const deserializedCheckpoint = {
48
48
  ...(await this.serde.loadsTyped("json", checkpoint)),
49
49
  pending_sends,
50
50
  };
51
- const pendingWrites = await Promise.all(writes.map(async ([taskId, channel, value]) => {
51
+ const pendingWrites = await Promise.all(Object.values(this.writes[key] || {}).map(async ([taskId, channel, value]) => {
52
52
  return [
53
53
  taskId,
54
54
  channel,
@@ -80,14 +80,13 @@ export class MemorySaver extends BaseCheckpointSaver {
80
80
  checkpoint_id = Object.keys(checkpoints).sort((a, b) => b.localeCompare(a))[0];
81
81
  const saved = checkpoints[checkpoint_id];
82
82
  const [checkpoint, metadata, parentCheckpointId] = saved;
83
- const writes = this.writes[_generateKey(thread_id, checkpoint_ns, checkpoint_id)] ??
84
- [];
83
+ const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);
85
84
  const pending_sends = await this._getPendingSends(thread_id, checkpoint_ns, parentCheckpointId);
86
85
  const deserializedCheckpoint = {
87
86
  ...(await this.serde.loadsTyped("json", checkpoint)),
88
87
  pending_sends,
89
88
  };
90
- const pendingWrites = await Promise.all(writes.map(async ([taskId, channel, value]) => {
89
+ const pendingWrites = await Promise.all(Object.values(this.writes[key] || {}).map(async ([taskId, channel, value]) => {
91
90
  return [
92
91
  taskId,
93
92
  channel,
@@ -127,6 +126,7 @@ export class MemorySaver extends BaseCheckpointSaver {
127
126
  ? [config.configurable?.thread_id]
128
127
  : Object.keys(this.storage);
129
128
  const configCheckpointNamespace = config.configurable?.checkpoint_ns;
129
+ const configCheckpointId = config.configurable?.checkpoint_id;
130
130
  for (const threadId of threadIds) {
131
131
  for (const checkpointNamespace of Object.keys(this.storage[threadId] ?? {})) {
132
132
  if (configCheckpointNamespace !== undefined &&
@@ -136,7 +136,11 @@ export class MemorySaver extends BaseCheckpointSaver {
136
136
  const checkpoints = this.storage[threadId]?.[checkpointNamespace] ?? {};
137
137
  const sortedCheckpoints = Object.entries(checkpoints).sort((a, b) => b[0].localeCompare(a[0]));
138
138
  for (const [checkpointId, [checkpoint, metadataStr, parentCheckpointId],] of sortedCheckpoints) {
139
- // Filter by checkpoint ID
139
+ // Filter by checkpoint ID from config
140
+ if (configCheckpointId && checkpointId !== configCheckpointId) {
141
+ continue;
142
+ }
143
+ // Filter by checkpoint ID from before config
140
144
  if (before &&
141
145
  before.configurable?.checkpoint_id &&
142
146
  checkpointId >= before.configurable.checkpoint_id) {
@@ -152,10 +156,10 @@ export class MemorySaver extends BaseCheckpointSaver {
152
156
  if (limit !== undefined) {
153
157
  if (limit <= 0)
154
158
  break;
155
- // eslint-disable-next-line no-param-reassign
156
159
  limit -= 1;
157
160
  }
158
- const writes = this.writes[_generateKey(threadId, checkpointNamespace, checkpointId)] ?? [];
161
+ const key = _generateKey(threadId, checkpointNamespace, checkpointId);
162
+ const writes = Object.values(this.writes[key] || {});
159
163
  const pending_sends = await this._getPendingSends(threadId, checkpointNamespace, parentCheckpointId);
160
164
  const pendingWrites = await Promise.all(writes.map(async ([taskId, channel, value]) => {
161
165
  return [
@@ -233,14 +237,22 @@ export class MemorySaver extends BaseCheckpointSaver {
233
237
  if (checkpointId === undefined) {
234
238
  throw new Error(`Failed to put writes. The passed RunnableConfig is missing a required "checkpoint_id" field in its "configurable" property.`);
235
239
  }
236
- const key = _generateKey(threadId, checkpointNamespace, checkpointId);
237
- if (this.writes[key] === undefined) {
238
- this.writes[key] = [];
240
+ const outerKey = _generateKey(threadId, checkpointNamespace, checkpointId);
241
+ const outerWrites_ = this.writes[outerKey];
242
+ if (this.writes[outerKey] === undefined) {
243
+ this.writes[outerKey] = {};
239
244
  }
240
- const pendingWrites = writes.map(([channel, value]) => {
245
+ writes.forEach(([channel, value], idx) => {
241
246
  const [, serializedValue] = this.serde.dumpsTyped(value);
242
- return [taskId, channel, serializedValue];
247
+ const innerKey = [
248
+ taskId,
249
+ WRITES_IDX_MAP[channel] || idx,
250
+ ];
251
+ const innerKeyStr = `${innerKey[0]},${innerKey[1]}`;
252
+ if (innerKey[1] >= 0 && outerWrites_ && innerKeyStr in outerWrites_) {
253
+ return;
254
+ }
255
+ this.writes[outerKey][innerKeyStr] = [taskId, channel, serializedValue];
243
256
  });
244
- this.writes[key].push(...pendingWrites);
245
257
  }
246
258
  }
@@ -117,5 +117,16 @@ class AsyncBatchedStore extends base_js_1.BaseStore {
117
117
  }
118
118
  }
119
119
  }
120
+ // AsyncBatchedStore is internal and gets passed as args into traced tasks
121
+ // some BaseStores contain circular references so just serialize without it
122
+ // as this causes warnings when tracing with LangSmith.
123
+ toJSON() {
124
+ return {
125
+ queue: this.queue,
126
+ nextKey: this.nextKey,
127
+ running: this.running,
128
+ store: "[LangGraphStore]",
129
+ };
130
+ }
120
131
  }
121
132
  exports.AsyncBatchedStore = AsyncBatchedStore;
@@ -26,4 +26,14 @@ export declare class AsyncBatchedStore extends BaseStore {
26
26
  stop(): Promise<void>;
27
27
  private enqueueOperation;
28
28
  private processBatchQueue;
29
+ toJSON(): {
30
+ queue: Map<number, {
31
+ operation: Operation;
32
+ resolve: (value: any) => void;
33
+ reject: (reason?: any) => void;
34
+ }>;
35
+ nextKey: number;
36
+ running: boolean;
37
+ store: string;
38
+ };
29
39
  }
@@ -114,4 +114,15 @@ export class AsyncBatchedStore extends BaseStore {
114
114
  }
115
115
  }
116
116
  }
117
+ // AsyncBatchedStore is internal and gets passed as args into traced tasks
118
+ // some BaseStores contain circular references so just serialize without it
119
+ // as this causes warnings when tracing with LangSmith.
120
+ toJSON() {
121
+ return {
122
+ queue: this.queue,
123
+ nextKey: this.nextKey,
124
+ running: this.running,
125
+ store: "[LangGraphStore]",
126
+ };
127
+ }
117
128
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph-checkpoint",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "Library with base interfaces for LangGraph checkpoint savers.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -58,7 +58,7 @@
58
58
  "jest-environment-node": "^29.6.4",
59
59
  "prettier": "^2.8.3",
60
60
  "release-it": "^17.6.0",
61
- "rollup": "^4.22.4",
61
+ "rollup": "^4.32.1",
62
62
  "ts-jest": "^29.1.0",
63
63
  "tsx": "^4.7.0",
64
64
  "typescript": "^4.9.5 || ^5.4.5"