@langchain/langgraph-checkpoint-redis 1.0.0 → 1.0.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2024 LangChain
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/dist/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
- const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
- const __langchain_langgraph_checkpoint = require_rolldown_runtime.__toESM(require("@langchain/langgraph-checkpoint"));
3
- const redis = require_rolldown_runtime.__toESM(require("redis"));
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_utils = require('./utils.cjs');
3
+ let _langchain_langgraph_checkpoint = require("@langchain/langgraph-checkpoint");
4
+ let redis = require("redis");
4
5
 
5
6
  //#region src/index.ts
6
7
  const SCHEMAS = [
@@ -107,7 +108,7 @@ const SCHEMAS = [
107
108
  }
108
109
  }
109
110
  ];
110
- var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseCheckpointSaver {
111
+ var RedisSaver = class RedisSaver extends _langchain_langgraph_checkpoint.BaseCheckpointSaver {
111
112
  client;
112
113
  ttlConfig;
113
114
  constructor(client, ttlConfig) {
@@ -130,14 +131,13 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
130
131
  return saver;
131
132
  }
132
133
  async get(config) {
133
- const tuple = await this.getTuple(config);
134
- return tuple?.checkpoint;
134
+ return (await this.getTuple(config))?.checkpoint;
135
135
  }
136
136
  async getTuple(config) {
137
137
  const threadId = config.configurable?.thread_id;
138
138
  const checkpointNs = config.configurable?.checkpoint_ns ?? "";
139
139
  const checkpointId = config.configurable?.checkpoint_id;
140
- if (!threadId) return void 0;
140
+ if (!threadId) return;
141
141
  let key;
142
142
  let jsonDoc;
143
143
  if (checkpointId) {
@@ -146,15 +146,15 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
146
146
  } else {
147
147
  const pattern = `checkpoint:${threadId}:${checkpointNs}:*`;
148
148
  const keys = await this.client.keys(pattern);
149
- if (keys.length === 0) return void 0;
149
+ if (keys.length === 0) return;
150
150
  keys.sort();
151
151
  key = keys[keys.length - 1];
152
152
  jsonDoc = await this.client.json.get(key);
153
153
  }
154
- if (!jsonDoc) return void 0;
154
+ if (!jsonDoc) return;
155
155
  if (this.ttlConfig?.refreshOnRead && this.ttlConfig?.defaultTTL) await this.applyTTL(key);
156
156
  const { checkpoint, pendingWrites } = await this.loadCheckpointWithWrites(jsonDoc);
157
- return this.createCheckpointTuple(jsonDoc, checkpoint, pendingWrites);
157
+ return await this.createCheckpointTuple(jsonDoc, checkpoint, pendingWrites);
158
158
  }
159
159
  async put(config, checkpoint, metadata, newVersions) {
160
160
  await this.ensureIndexes();
@@ -162,9 +162,9 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
162
162
  const checkpointNs = config.configurable?.checkpoint_ns ?? "";
163
163
  const parentCheckpointId = config.configurable?.checkpoint_id;
164
164
  if (!threadId) throw new Error("thread_id is required");
165
- const checkpointId = checkpoint.id || (0, __langchain_langgraph_checkpoint.uuid6)(0);
165
+ const checkpointId = checkpoint.id || (0, _langchain_langgraph_checkpoint.uuid6)(0);
166
166
  const key = `checkpoint:${threadId}:${checkpointNs}:${checkpointId}`;
167
- const storedCheckpoint = (0, __langchain_langgraph_checkpoint.copyCheckpoint)(checkpoint);
167
+ const storedCheckpoint = (0, _langchain_langgraph_checkpoint.copyCheckpoint)(checkpoint);
168
168
  if (storedCheckpoint.channel_values && newVersions !== void 0) if (Object.keys(newVersions).length === 0) storedCheckpoint.channel_values = {};
169
169
  else {
170
170
  const filteredChannelValues = {};
@@ -208,16 +208,21 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
208
208
  }
209
209
  }
210
210
  if (!options?.before && options?.filter) {
211
- for (const [key, value] of Object.entries(options.filter)) if (value === void 0) {} else if (value === null) {} else if (typeof value === "string") queryParts.push(`(@${key}:{${value}})`);
212
- else if (typeof value === "number") queryParts.push(`(@${key}:[${value} ${value}])`);
213
- else if (typeof value === "object" && Object.keys(value).length === 0) {}
211
+ for (const [key, value] of Object.entries(options.filter)) if (value === void 0) {} else if (value === null) {} else if (typeof value === "string") {
212
+ const escapedKey = require_utils.escapeRediSearchTagValue(key);
213
+ const escapedValue = require_utils.escapeRediSearchTagValue(value);
214
+ queryParts.push(`(@${escapedKey}:{${escapedValue}})`);
215
+ } else if (typeof value === "number") {
216
+ const escapedKey = require_utils.escapeRediSearchTagValue(key);
217
+ queryParts.push(`(@${escapedKey}:[${value} ${value}])`);
218
+ } else if (typeof value === "object" && Object.keys(value).length === 0) {}
214
219
  }
215
220
  if (queryParts.length === 0) queryParts.push("*");
216
221
  const query = queryParts.join(" ");
217
222
  const limit = options?.limit ?? 10;
218
223
  try {
219
224
  const fetchLimit = options?.before && !config?.configurable?.thread_id ? 1e3 : options?.before ? limit * 10 : limit;
220
- const results = await this.client.ft.search("checkpoints", query, {
225
+ let documents = (await this.client.ft.search("checkpoints", query, {
221
226
  LIMIT: {
222
227
  from: 0,
223
228
  size: fetchLimit
@@ -226,22 +231,18 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
226
231
  BY: "checkpoint_ts",
227
232
  DIRECTION: "DESC"
228
233
  }
229
- });
230
- let documents = results.documents;
234
+ })).documents;
231
235
  let yieldedCount = 0;
232
236
  for (const doc of documents) {
233
237
  if (yieldedCount >= limit) break;
234
238
  if (options?.before?.configurable?.checkpoint_id) {
235
- const currentCheckpointId = doc.value.checkpoint_id;
236
- const beforeCheckpointId = options.before.configurable.checkpoint_id;
237
- if (currentCheckpointId >= beforeCheckpointId) continue;
239
+ if (doc.value.checkpoint_id >= options.before.configurable.checkpoint_id) continue;
238
240
  }
239
241
  const jsonDoc = doc.value;
240
242
  let matches = true;
241
243
  if ((hasNullFilter || options?.before) && options?.filter) {
242
244
  for (const [filterKey, filterValue] of Object.entries(options.filter)) if (filterValue === null) {
243
- const metadataValue = jsonDoc.metadata?.[filterKey];
244
- if (metadataValue !== null) {
245
+ if (jsonDoc.metadata?.[filterKey] !== null) {
245
246
  matches = false;
246
247
  break;
247
248
  }
@@ -260,7 +261,7 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
260
261
  if (!matches) continue;
261
262
  }
262
263
  const { checkpoint, pendingWrites } = await this.loadCheckpointWithWrites(jsonDoc);
263
- yield this.createCheckpointTuple(jsonDoc, checkpoint, pendingWrites);
264
+ yield await this.createCheckpointTuple(jsonDoc, checkpoint, pendingWrites);
264
265
  yieldedCount++;
265
266
  }
266
267
  return;
@@ -275,15 +276,13 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
275
276
  keys.sort().reverse();
276
277
  let filteredKeys = keys;
277
278
  if (options?.before?.configurable?.checkpoint_id) {
278
- const beforeThreadId = options.before.configurable.thread_id || threadId;
279
- const beforeCheckpointNs = options.before.configurable.checkpoint_ns ?? checkpointNs;
280
- const beforeKey = `checkpoint:${beforeThreadId}:${beforeCheckpointNs}:${options.before.configurable.checkpoint_id}`;
279
+ const beforeKey = `checkpoint:${options.before.configurable.thread_id || threadId}:${options.before.configurable.checkpoint_ns ?? checkpointNs}:${options.before.configurable.checkpoint_id}`;
281
280
  const beforeIndex = keys.indexOf(beforeKey);
282
281
  if (beforeIndex > 0) filteredKeys = keys.slice(beforeIndex + 1);
283
282
  else if (beforeIndex === 0) filteredKeys = [];
284
283
  }
285
- const limit$1 = options?.limit ?? 10;
286
- const limitedKeys = filteredKeys.slice(0, limit$1);
284
+ const limit = options?.limit ?? 10;
285
+ const limitedKeys = filteredKeys.slice(0, limit);
287
286
  for (const key of limitedKeys) {
288
287
  const jsonDoc = await this.client.json.get(key);
289
288
  if (jsonDoc) {
@@ -302,7 +301,7 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
302
301
  }
303
302
  if (!matches) continue;
304
303
  const { checkpoint, pendingWrites } = await this.loadCheckpointWithWrites(jsonDoc);
305
- yield this.createCheckpointTuple(jsonDoc, checkpoint, pendingWrites);
304
+ yield await this.createCheckpointTuple(jsonDoc, checkpoint, pendingWrites);
306
305
  }
307
306
  }
308
307
  } else {
@@ -318,19 +317,16 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
318
317
  }
319
318
  allDocuments.sort((a, b) => b.doc.checkpoint_ts - a.doc.checkpoint_ts);
320
319
  let yieldedCount = 0;
321
- const limit$1 = options?.limit ?? 10;
320
+ const limit = options?.limit ?? 10;
322
321
  for (const { doc: jsonDoc } of allDocuments) {
323
- if (yieldedCount >= limit$1) break;
322
+ if (yieldedCount >= limit) break;
324
323
  if (options?.before?.configurable?.checkpoint_id) {
325
- const currentCheckpointId = jsonDoc.checkpoint_id;
326
- const beforeCheckpointId = options.before.configurable.checkpoint_id;
327
- if (currentCheckpointId >= beforeCheckpointId) continue;
324
+ if (jsonDoc.checkpoint_id >= options.before.configurable.checkpoint_id) continue;
328
325
  }
329
326
  let matches = true;
330
327
  if (options?.filter) {
331
328
  for (const [filterKey, filterValue] of Object.entries(options.filter)) if (filterValue === null) {
332
- const metadataValue = jsonDoc.metadata?.[filterKey];
333
- if (metadataValue !== null) {
329
+ if (jsonDoc.metadata?.[filterKey] !== null) {
334
330
  matches = false;
335
331
  break;
336
332
  }
@@ -349,7 +345,7 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
349
345
  if (!matches) continue;
350
346
  }
351
347
  const { checkpoint, pendingWrites } = await this.loadCheckpointWithWrites(jsonDoc);
352
- yield this.createCheckpointTuple(jsonDoc, checkpoint, pendingWrites);
348
+ yield await this.createCheckpointTuple(jsonDoc, checkpoint, pendingWrites);
353
349
  yieldedCount++;
354
350
  }
355
351
  }
@@ -400,8 +396,7 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
400
396
  if (this.ttlConfig?.defaultTTL) await this.applyTTL(...writeKeys, zsetKey);
401
397
  }
402
398
  const checkpointKey = `checkpoint:${threadId}:${checkpointNs}:${checkpointId}`;
403
- const checkpointExists = await this.client.exists(checkpointKey);
404
- if (checkpointExists) {
399
+ if (await this.client.exists(checkpointKey)) {
405
400
  const currentDoc = await this.client.json.get(checkpointKey);
406
401
  if (currentDoc) {
407
402
  currentDoc.has_writes = "true";
@@ -423,7 +418,7 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
423
418
  async loadPendingWrites(threadId, checkpointNs, checkpointId) {
424
419
  const pattern = `checkpoint_write:${threadId}:${checkpointNs}:${checkpointId}:*`;
425
420
  const writeKeys = await this.client.keys(pattern);
426
- if (writeKeys.length === 0) return void 0;
421
+ if (writeKeys.length === 0) return;
427
422
  const writeDocuments = [];
428
423
  for (const writeKey of writeKeys) {
429
424
  const writeDoc = await this.client.json.get(writeKey);
@@ -431,15 +426,18 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
431
426
  }
432
427
  writeDocuments.sort((a, b) => (a.global_idx || 0) - (b.global_idx || 0));
433
428
  const pendingWrites = [];
434
- for (const writeDoc of writeDocuments) pendingWrites.push([
435
- writeDoc.task_id,
436
- writeDoc.channel,
437
- writeDoc.value
438
- ]);
429
+ for (const writeDoc of writeDocuments) {
430
+ const deserializedValue = await this.serde.loadsTyped("json", JSON.stringify(writeDoc.value));
431
+ pendingWrites.push([
432
+ writeDoc.task_id,
433
+ writeDoc.channel,
434
+ deserializedValue
435
+ ]);
436
+ }
439
437
  return pendingWrites;
440
438
  }
441
439
  async loadCheckpointWithWrites(jsonDoc) {
442
- const checkpoint = { ...jsonDoc.checkpoint };
440
+ const checkpoint = await this.serde.loadsTyped("json", JSON.stringify(jsonDoc.checkpoint));
443
441
  if (checkpoint.v < 4 && jsonDoc.parent_checkpoint_id != null) {
444
442
  const actualNs = jsonDoc.checkpoint_ns === "__empty__" ? "" : jsonDoc.checkpoint_ns;
445
443
  await this.migratePendingSends(checkpoint, jsonDoc.thread_id, actualNs, jsonDoc.parent_checkpoint_id);
@@ -457,16 +455,17 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
457
455
  async migratePendingSends(checkpoint, threadId, checkpointNs, parentCheckpointId) {
458
456
  const parentWrites = await this.loadPendingWrites(threadId, checkpointNs, parentCheckpointId);
459
457
  if (!parentWrites || parentWrites.length === 0) return;
460
- const taskWrites = parentWrites.filter(([, channel]) => channel === __langchain_langgraph_checkpoint.TASKS);
458
+ const taskWrites = parentWrites.filter(([, channel]) => channel === _langchain_langgraph_checkpoint.TASKS);
461
459
  if (taskWrites.length === 0) return;
462
460
  const allTasks = [];
463
461
  for (const [, , value] of taskWrites) allTasks.push(value);
464
462
  checkpoint.channel_values ??= {};
465
- checkpoint.channel_values[__langchain_langgraph_checkpoint.TASKS] = allTasks;
466
- checkpoint.channel_versions[__langchain_langgraph_checkpoint.TASKS] = Object.keys(checkpoint.channel_versions).length > 0 ? (0, __langchain_langgraph_checkpoint.maxChannelVersion)(...Object.values(checkpoint.channel_versions)) : 1;
463
+ checkpoint.channel_values[_langchain_langgraph_checkpoint.TASKS] = allTasks;
464
+ checkpoint.channel_versions[_langchain_langgraph_checkpoint.TASKS] = Object.keys(checkpoint.channel_versions).length > 0 ? (0, _langchain_langgraph_checkpoint.maxChannelVersion)(...Object.values(checkpoint.channel_versions)) : 1;
467
465
  }
468
- createCheckpointTuple(jsonDoc, checkpoint, pendingWrites) {
466
+ async createCheckpointTuple(jsonDoc, checkpoint, pendingWrites) {
469
467
  const checkpointNs = jsonDoc.checkpoint_ns === "__empty__" ? "" : jsonDoc.checkpoint_ns;
468
+ const metadata = await this.serde.loadsTyped("json", JSON.stringify(jsonDoc.metadata));
470
469
  return {
471
470
  config: { configurable: {
472
471
  thread_id: jsonDoc.thread_id,
@@ -474,7 +473,7 @@ var RedisSaver = class RedisSaver extends __langchain_langgraph_checkpoint.BaseC
474
473
  checkpoint_id: jsonDoc.checkpoint_id
475
474
  } },
476
475
  checkpoint,
477
- metadata: jsonDoc.metadata,
476
+ metadata,
478
477
  parentConfig: jsonDoc.parent_checkpoint_id ? { configurable: {
479
478
  thread_id: jsonDoc.thread_id,
480
479
  checkpoint_ns: checkpointNs,
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["BaseCheckpointSaver","key: string","jsonDoc: CheckpointDocument | null","filteredChannelValues: Record<string, any>","jsonDoc: CheckpointDocument","queryParts: string[]","error: any","limit","allDocuments: { key: string; doc: CheckpointDocument }[]","searchOptions: CheckpointListOptions & {\n filter?: CheckpointMetadata;\n }","writeKeys: string[]","zaddArgs: Record<string, number>","writeDocuments: any[]","pendingWrites: Array<[string, string, any]>","pendingWrites: Array<[string, string, any]> | undefined","TASKS","allTasks: any[]","sortedObj: Record<string, any>","sorted: Record<string, any>"],"sources":["../src/index.ts"],"sourcesContent":["import {\n BaseCheckpointSaver,\n ChannelVersions,\n Checkpoint,\n CheckpointListOptions,\n CheckpointMetadata,\n CheckpointTuple,\n PendingWrite,\n uuid6,\n TASKS,\n maxChannelVersion,\n copyCheckpoint,\n} from \"@langchain/langgraph-checkpoint\";\nimport { RunnableConfig } from \"@langchain/core/runnables\";\nimport { createClient, createCluster } from \"redis\";\n\n// Type for Redis client - supports both standalone and cluster\nexport type RedisClientType =\n | ReturnType<typeof createClient>\n | ReturnType<typeof createCluster>;\n\nconst SCHEMAS = [\n {\n index: \"checkpoints\",\n prefix: \"checkpoint:\",\n schema: {\n \"$.thread_id\": { type: \"TAG\", AS: \"thread_id\" },\n \"$.checkpoint_ns\": { type: \"TAG\", AS: \"checkpoint_ns\" },\n \"$.checkpoint_id\": { type: \"TAG\", AS: \"checkpoint_id\" },\n \"$.parent_checkpoint_id\": { type: \"TAG\", AS: \"parent_checkpoint_id\" },\n \"$.checkpoint_ts\": { type: \"NUMERIC\", AS: \"checkpoint_ts\" },\n \"$.has_writes\": { type: \"TAG\", AS: \"has_writes\" },\n \"$.source\": { type: \"TAG\", AS: \"source\" },\n \"$.step\": { type: \"NUMERIC\", AS: \"step\" },\n },\n },\n {\n index: \"checkpoint_blobs\",\n prefix: \"checkpoint_blob:\",\n schema: {\n \"$.thread_id\": { type: \"TAG\", AS: \"thread_id\" },\n \"$.checkpoint_ns\": { type: \"TAG\", AS: \"checkpoint_ns\" },\n \"$.checkpoint_id\": { type: \"TAG\", AS: \"checkpoint_id\" },\n \"$.channel\": { type: \"TAG\", AS: \"channel\" },\n \"$.version\": { type: \"TAG\", AS: \"version\" },\n \"$.type\": { type: \"TAG\", AS: \"type\" },\n },\n },\n {\n index: \"checkpoint_writes\",\n prefix: \"checkpoint_write:\",\n schema: {\n \"$.thread_id\": { type: \"TAG\", AS: \"thread_id\" },\n \"$.checkpoint_ns\": { type: \"TAG\", AS: \"checkpoint_ns\" },\n \"$.checkpoint_id\": { type: \"TAG\", AS: \"checkpoint_id\" },\n \"$.task_id\": { type: \"TAG\", AS: \"task_id\" },\n \"$.idx\": { type: \"NUMERIC\", AS: \"idx\" },\n \"$.channel\": { type: \"TAG\", AS: \"channel\" },\n \"$.type\": { type: \"TAG\", AS: \"type\" },\n },\n },\n];\n\nexport interface TTLConfig {\n defaultTTL?: number; // TTL in minutes\n refreshOnRead?: boolean; // Whether to refresh TTL when reading\n}\n\ninterface CheckpointDocument {\n thread_id: string;\n checkpoint_ns: string;\n checkpoint_id: string;\n parent_checkpoint_id: string | null;\n checkpoint: Checkpoint & {\n channel_values?: Record<string, any>;\n channel_blobs?: Record<string, { __blob__: boolean; key: string }>;\n };\n metadata: CheckpointMetadata;\n checkpoint_ts: number;\n has_writes: string;\n source?: string;\n step?: number;\n [key: string]: any; // Allow additional fields for metadata\n}\n\nexport class RedisSaver extends BaseCheckpointSaver {\n private client: RedisClientType;\n private ttlConfig?: TTLConfig;\n\n constructor(client: RedisClientType, ttlConfig?: TTLConfig) {\n super();\n this.client = client;\n this.ttlConfig = ttlConfig;\n }\n\n static async fromUrl(\n url: string,\n ttlConfig?: TTLConfig\n ): Promise<RedisSaver> {\n const client = createClient({ url });\n await client.connect();\n const saver = new RedisSaver(client, ttlConfig);\n await saver.ensureIndexes();\n return saver;\n }\n\n static async fromCluster(\n rootNodes: Array<{ url: string }>,\n ttlConfig?: TTLConfig\n ): Promise<RedisSaver> {\n const client = createCluster({ rootNodes });\n await client.connect();\n const saver = new RedisSaver(client, ttlConfig);\n await saver.ensureIndexes();\n return saver;\n }\n\n async get(config: RunnableConfig): Promise<Checkpoint | undefined> {\n const tuple = await this.getTuple(config);\n return tuple?.checkpoint;\n }\n\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? \"\";\n const checkpointId = config.configurable?.checkpoint_id;\n\n if (!threadId) {\n return undefined;\n }\n\n let key: string;\n let jsonDoc: CheckpointDocument | null;\n\n if (checkpointId) {\n // Get specific checkpoint\n key = `checkpoint:${threadId}:${checkpointNs}:${checkpointId}`;\n jsonDoc = (await this.client.json.get(key)) as CheckpointDocument | null;\n } else {\n // Get latest checkpoint - need to search\n const pattern = `checkpoint:${threadId}:${checkpointNs}:*`;\n // Use keys for simplicity - scan would be better for large datasets\n const keys = await (this.client as any).keys(pattern);\n\n if (keys.length === 0) {\n return undefined;\n }\n\n // Sort by key to get latest\n keys.sort();\n key = keys[keys.length - 1];\n jsonDoc = (await this.client.json.get(key)) as CheckpointDocument | null;\n }\n\n if (!jsonDoc) {\n return undefined;\n }\n\n // Refresh TTL if configured\n if (this.ttlConfig?.refreshOnRead && this.ttlConfig?.defaultTTL) {\n await this.applyTTL(key);\n }\n\n // Load checkpoint with pending writes\n const { checkpoint, pendingWrites } = await this.loadCheckpointWithWrites(\n jsonDoc\n );\n\n return this.createCheckpointTuple(jsonDoc, checkpoint, pendingWrites);\n }\n\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata,\n newVersions: ChannelVersions\n ): Promise<RunnableConfig> {\n await this.ensureIndexes();\n\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? \"\";\n const parentCheckpointId = config.configurable?.checkpoint_id;\n\n if (!threadId) {\n throw new Error(\"thread_id is required\");\n }\n\n const checkpointId = checkpoint.id || uuid6(0);\n const key = `checkpoint:${threadId}:${checkpointNs}:${checkpointId}`;\n\n // Copy checkpoint and filter channel_values to only include changed channels\n const storedCheckpoint = copyCheckpoint(checkpoint);\n\n // If newVersions is provided and has keys, only store those channels that changed\n // If newVersions is empty {}, store no channel values\n // If newVersions is not provided (undefined), keep all channel_values as-is\n if (storedCheckpoint.channel_values && newVersions !== undefined) {\n if (Object.keys(newVersions).length === 0) {\n // Empty newVersions means no channels changed - store empty channel_values\n storedCheckpoint.channel_values = {};\n } else {\n // Only store the channels that are in newVersions\n const filteredChannelValues: Record<string, any> = {};\n for (const channel of Object.keys(newVersions)) {\n if (channel in storedCheckpoint.channel_values) {\n filteredChannelValues[channel] =\n storedCheckpoint.channel_values[channel];\n }\n }\n storedCheckpoint.channel_values = filteredChannelValues;\n }\n }\n // If newVersions is undefined, keep all channel_values as-is (for backward compatibility)\n\n // Structure matching Python implementation\n const jsonDoc: CheckpointDocument = {\n thread_id: threadId,\n // Store empty namespace as \"__empty__\" for RediSearch compatibility\n checkpoint_ns: checkpointNs === \"\" ? \"__empty__\" : checkpointNs,\n checkpoint_id: checkpointId,\n parent_checkpoint_id: parentCheckpointId || null,\n checkpoint: storedCheckpoint,\n metadata: metadata,\n checkpoint_ts: Date.now(),\n has_writes: \"false\",\n };\n\n // Store metadata fields at top-level for searching\n this.addSearchableMetadataFields(jsonDoc, metadata);\n\n // Use Redis JSON commands\n await this.client.json.set(key, \"$\", jsonDoc as any);\n\n // Apply TTL if configured\n if (this.ttlConfig?.defaultTTL) {\n await this.applyTTL(key);\n }\n\n return {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNs,\n checkpoint_id: checkpointId,\n },\n };\n }\n\n async *list(\n config: RunnableConfig | null,\n options?: CheckpointListOptions & { filter?: CheckpointMetadata }\n ): AsyncGenerator<CheckpointTuple> {\n await this.ensureIndexes();\n\n // If filter is provided (even if empty), use search functionality\n if (options?.filter !== undefined) {\n // Check if we have null values in the filter which RediSearch can't handle\n const hasNullFilter = Object.values(options.filter).some(\n (v) => v === null\n );\n\n // Build search query\n const queryParts: string[] = [];\n\n // Add thread_id constraint if provided\n if (config?.configurable?.thread_id) {\n const threadId = config.configurable.thread_id.replace(\n /[-.@]/g,\n \"\\\\$&\"\n );\n queryParts.push(`(@thread_id:{${threadId}})`);\n }\n\n // Add checkpoint_ns constraint if provided\n if (config?.configurable?.checkpoint_ns !== undefined) {\n const checkpointNs = config.configurable.checkpoint_ns;\n if (checkpointNs === \"\") {\n // Empty string needs special handling in RediSearch\n // We'll store it as \"__empty__\" in the index\n queryParts.push(`(@checkpoint_ns:{__empty__})`);\n } else {\n const escapedNs = checkpointNs.replace(/[-.@]/g, \"\\\\$&\");\n queryParts.push(`(@checkpoint_ns:{${escapedNs}})`);\n }\n }\n\n // Skip metadata filters in search query when 'before' parameter is used\n // We'll apply them after the before filtering to get correct results\n if (!options?.before && options?.filter) {\n // Add metadata filters (but skip null values)\n for (const [key, value] of Object.entries(options.filter)) {\n if (value === undefined) {\n // Skip undefined filters\n } else if (value === null) {\n // Skip null values for RediSearch query, will handle in post-processing\n } else if (typeof value === \"string\") {\n // Don't escape, just wrap in braces for exact match\n queryParts.push(`(@${key}:{${value}})`);\n } else if (typeof value === \"number\") {\n queryParts.push(`(@${key}:[${value} ${value}])`);\n } else if (\n typeof value === \"object\" &&\n Object.keys(value).length === 0\n ) {\n // Skip empty objects\n }\n }\n }\n\n if (queryParts.length === 0) {\n queryParts.push(\"*\");\n }\n\n const query = queryParts.join(\" \");\n const limit = options?.limit ?? 10;\n\n try {\n // Fetch more results than the limit to handle post-filtering for 'before'\n // When no thread_id is specified but 'before' is used, we need to fetch all results\n const fetchLimit =\n options?.before && !config?.configurable?.thread_id\n ? 1000 // Fetch many results for global search with 'before' filtering\n : options?.before\n ? limit * 10\n : limit;\n\n const results = await this.client.ft.search(\"checkpoints\", query, {\n LIMIT: { from: 0, size: fetchLimit },\n SORTBY: { BY: \"checkpoint_ts\", DIRECTION: \"DESC\" },\n });\n\n let documents = results.documents;\n\n let yieldedCount = 0;\n\n for (const doc of documents) {\n if (yieldedCount >= limit) break;\n\n // Handle 'before' parameter - filter based on checkpoint_id comparison\n // UUID6 IDs are time-sortable, so string comparison works for ordering\n if (options?.before?.configurable?.checkpoint_id) {\n const currentCheckpointId = doc.value.checkpoint_id as string;\n const beforeCheckpointId =\n options.before.configurable.checkpoint_id;\n\n // Skip checkpoints that are not before the specified checkpoint\n if (currentCheckpointId >= beforeCheckpointId) {\n continue;\n }\n }\n\n const jsonDoc = doc.value;\n\n // Apply metadata filters manually (either for null filters or when before parameter was used)\n let matches = true;\n if ((hasNullFilter || options?.before) && options?.filter) {\n for (const [filterKey, filterValue] of Object.entries(\n options.filter\n )) {\n if (filterValue === null) {\n // Check if the field exists and is null in metadata\n // This should only match explicit null, not missing fields\n const metadataValue = (jsonDoc.metadata as any)?.[filterKey];\n if (metadataValue !== null) {\n matches = false;\n break;\n }\n } else if (filterValue !== undefined) {\n // Check other metadata values\n const metadataValue = (jsonDoc.metadata as any)?.[filterKey];\n // For objects, do deep equality check with deterministic key ordering\n if (typeof filterValue === \"object\" && filterValue !== null) {\n if (\n deterministicStringify(metadataValue) !==\n deterministicStringify(filterValue)\n ) {\n matches = false;\n break;\n }\n } else if (metadataValue !== filterValue) {\n matches = false;\n break;\n }\n }\n }\n if (!matches) continue;\n }\n\n // Load checkpoint with pending writes and migrate sends\n const { checkpoint, pendingWrites } =\n await this.loadCheckpointWithWrites(jsonDoc);\n yield this.createCheckpointTuple(jsonDoc, checkpoint, pendingWrites);\n yieldedCount++;\n }\n\n // Search succeeded, return without falling through\n return;\n } catch (error: any) {\n if (error.message?.includes(\"no such index\")) {\n // Index doesn't exist yet, fall through to regular listing\n } else {\n throw error;\n }\n }\n\n // If search failed due to missing index, fall through to regular listing\n if (config?.configurable?.thread_id) {\n // Fall back to regular listing with manual filtering when thread_id is specified\n const threadId = config.configurable.thread_id;\n const checkpointNs = config.configurable.checkpoint_ns ?? \"\";\n const pattern = `checkpoint:${threadId}:${checkpointNs}:*`;\n // Use scan for better performance and cluster compatibility\n // Use keys for simplicity - scan would be better for large datasets\n const keys = await (this.client as any).keys(pattern);\n\n keys.sort().reverse();\n\n let filteredKeys = keys;\n\n // Handle 'before' parameter\n if (options?.before?.configurable?.checkpoint_id) {\n const beforeThreadId =\n options.before.configurable.thread_id || threadId;\n const beforeCheckpointNs =\n options.before.configurable.checkpoint_ns ?? checkpointNs;\n const beforeKey = `checkpoint:${beforeThreadId}:${beforeCheckpointNs}:${options.before.configurable.checkpoint_id}`;\n\n const beforeIndex = keys.indexOf(beforeKey);\n if (beforeIndex > 0) {\n // Return all items that come after the found index (i.e., before in time)\n filteredKeys = keys.slice(beforeIndex + 1);\n } else if (beforeIndex === 0) {\n // Nothing before the first item (most recent)\n filteredKeys = [];\n }\n // If not found, return all\n }\n\n const limit = options?.limit ?? 10;\n const limitedKeys = filteredKeys.slice(0, limit);\n\n for (const key of limitedKeys) {\n const jsonDoc = (await this.client.json.get(\n key\n )) as CheckpointDocument | null;\n if (jsonDoc) {\n // Check if metadata matches filter\n let matches = true;\n for (const [filterKey, filterValue] of Object.entries(\n options.filter\n )) {\n const metadataValue = (jsonDoc.metadata as any)?.[filterKey];\n if (filterValue === null) {\n if (metadataValue !== null) {\n matches = false;\n break;\n }\n } else if (metadataValue !== filterValue) {\n matches = false;\n break;\n }\n }\n\n if (!matches) continue;\n\n // Load checkpoint with pending writes and migrate sends\n const { checkpoint, pendingWrites } =\n await this.loadCheckpointWithWrites(jsonDoc);\n yield this.createCheckpointTuple(\n jsonDoc,\n checkpoint,\n pendingWrites\n );\n }\n }\n } else {\n // Fall back to global search when thread_id is undefined\n // This is needed for validation tests that search globally with 'before' parameter\n const globalPattern =\n config?.configurable?.checkpoint_ns !== undefined\n ? `checkpoint:*:${\n config.configurable.checkpoint_ns === \"\"\n ? \"__empty__\"\n : config.configurable.checkpoint_ns\n }:*`\n : \"checkpoint:*\";\n\n const allKeys = await (this.client as any).keys(globalPattern);\n const allDocuments: { key: string; doc: CheckpointDocument }[] = [];\n\n // Load all matching documents\n for (const key of allKeys) {\n const jsonDoc = (await this.client.json.get(\n key\n )) as CheckpointDocument | null;\n if (jsonDoc) {\n allDocuments.push({ key, doc: jsonDoc });\n }\n }\n\n // Sort by timestamp (descending) to match the search behavior\n allDocuments.sort((a, b) => b.doc.checkpoint_ts - a.doc.checkpoint_ts);\n\n let yieldedCount = 0;\n const limit = options?.limit ?? 10;\n\n for (const { doc: jsonDoc } of allDocuments) {\n if (yieldedCount >= limit) break;\n\n // Handle 'before' parameter - filter based on checkpoint_id comparison\n if (options?.before?.configurable?.checkpoint_id) {\n const currentCheckpointId = jsonDoc.checkpoint_id;\n const beforeCheckpointId =\n options.before.configurable.checkpoint_id;\n\n // Skip checkpoints that are not before the specified checkpoint\n if (currentCheckpointId >= beforeCheckpointId) {\n continue;\n }\n }\n\n // Apply metadata filters manually\n let matches = true;\n if (options?.filter) {\n for (const [filterKey, filterValue] of Object.entries(\n options.filter\n )) {\n if (filterValue === null) {\n // Check if the field exists and is null in metadata\n const metadataValue = (jsonDoc.metadata as any)?.[filterKey];\n if (metadataValue !== null) {\n matches = false;\n break;\n }\n } else if (filterValue !== undefined) {\n // Check other metadata values\n const metadataValue = (jsonDoc.metadata as any)?.[filterKey];\n // For objects, do deep equality check with deterministic key ordering\n if (typeof filterValue === \"object\" && filterValue !== null) {\n if (\n deterministicStringify(metadataValue) !==\n deterministicStringify(filterValue)\n ) {\n matches = false;\n break;\n }\n } else if (metadataValue !== filterValue) {\n matches = false;\n break;\n }\n }\n }\n if (!matches) continue;\n }\n\n // Load checkpoint with pending writes and migrate sends\n const { checkpoint, pendingWrites } =\n await this.loadCheckpointWithWrites(jsonDoc);\n yield this.createCheckpointTuple(jsonDoc, checkpoint, pendingWrites);\n yieldedCount++;\n }\n }\n\n return;\n }\n\n // Regular listing without filter - use search with empty filter instead\n // This ensures consistent behavior between filter={} and filter=undefined\n const searchOptions: CheckpointListOptions & {\n filter?: CheckpointMetadata;\n } = {\n ...options,\n filter: {} as CheckpointMetadata,\n };\n\n // Delegate to the search path\n yield* this.list(config, searchOptions);\n return;\n }\n\n async putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void> {\n await this.ensureIndexes();\n\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? \"\";\n const checkpointId = config.configurable?.checkpoint_id;\n\n if (!threadId || !checkpointId) {\n throw new Error(\"thread_id and checkpoint_id are required\");\n }\n\n // Collect write keys for sorted set tracking\n const writeKeys: string[] = [];\n\n // Use high-resolution timestamp to ensure unique ordering across putWrites calls\n const baseTimestamp = performance.now() * 1000; // Microsecond precision\n\n // Store each write as a separate indexed JSON document\n for (let idx = 0; idx < writes.length; idx++) {\n const [channel, value] = writes[idx];\n const writeKey = `checkpoint_write:${threadId}:${checkpointNs}:${checkpointId}:${taskId}:${idx}`;\n writeKeys.push(writeKey);\n\n const writeDoc = {\n thread_id: threadId,\n checkpoint_ns: checkpointNs,\n checkpoint_id: checkpointId,\n task_id: taskId,\n idx: idx,\n channel: channel,\n type: typeof value === \"object\" ? \"json\" : \"string\",\n value: value,\n timestamp: baseTimestamp,\n global_idx: baseTimestamp + idx, // Add microseconds for sub-millisecond ordering\n };\n\n await this.client.json.set(writeKey, \"$\", writeDoc as any);\n }\n\n // Register write keys in sorted set for efficient retrieval\n if (writeKeys.length > 0) {\n const zsetKey = `write_keys_zset:${threadId}:${checkpointNs}:${checkpointId}`;\n\n // Use timestamp + idx for scoring to maintain correct order\n const zaddArgs: Record<string, number> = {};\n writeKeys.forEach((key, idx) => {\n zaddArgs[key] = baseTimestamp + idx;\n });\n await this.client.zAdd(\n zsetKey,\n Object.entries(zaddArgs).map(([key, score]) => ({ score, value: key }))\n );\n\n // Apply TTL to write keys and zset if configured\n if (this.ttlConfig?.defaultTTL) {\n // Apply TTL to write keys and zset\n await this.applyTTL(...writeKeys, zsetKey);\n }\n }\n\n // Update checkpoint to indicate it has writes\n const checkpointKey = `checkpoint:${threadId}:${checkpointNs}:${checkpointId}`;\n const checkpointExists = await this.client.exists(checkpointKey);\n if (checkpointExists) {\n // Get the current document and update it\n const currentDoc = (await this.client.json.get(\n checkpointKey\n )) as CheckpointDocument | null;\n if (currentDoc) {\n currentDoc.has_writes = \"true\";\n await this.client.json.set(checkpointKey, \"$\", currentDoc as any);\n }\n }\n }\n\n async deleteThread(threadId: string): Promise<void> {\n // Delete checkpoints\n const checkpointPattern = `checkpoint:${threadId}:*`;\n // Use scan for better performance and cluster compatibility\n // Use keys for simplicity - scan would be better for large datasets\n const checkpointKeys = await (this.client as any).keys(checkpointPattern);\n\n if (checkpointKeys.length > 0) {\n await this.client.del(checkpointKeys);\n }\n\n // Delete writes\n const writesPattern = `writes:${threadId}:*`;\n // Use scan for better performance and cluster compatibility\n // Use keys for simplicity - scan would be better for large datasets\n const writesKeys = await (this.client as any).keys(writesPattern);\n\n if (writesKeys.length > 0) {\n await this.client.del(writesKeys);\n }\n }\n\n async end(): Promise<void> {\n await this.client.quit();\n }\n\n // Helper method to load channel blobs (simplified - no blob support for now)\n // private async loadChannelBlobs(\n // checkpoint: Checkpoint & { channel_blobs?: any }\n // ): Promise<Checkpoint> {\n // // Since we're not using blobs anymore, just return the checkpoint as-is\n // return checkpoint;\n // }\n\n // Helper method to load pending writes\n private async loadPendingWrites(\n threadId: string,\n checkpointNs: string,\n checkpointId: string\n ): Promise<Array<[string, string, any]> | undefined> {\n // Search for all write documents for this checkpoint\n const pattern = `checkpoint_write:${threadId}:${checkpointNs}:${checkpointId}:*`;\n const writeKeys = await (this.client as any).keys(pattern);\n\n if (writeKeys.length === 0) {\n return undefined;\n }\n\n const writeDocuments: any[] = [];\n for (const writeKey of writeKeys) {\n const writeDoc = (await this.client.json.get(writeKey)) as any;\n if (writeDoc) {\n writeDocuments.push(writeDoc);\n }\n }\n\n // Sort by global_idx (which represents insertion order across all putWrites calls)\n // This matches how SQLite would naturally order by insertion time + idx\n writeDocuments.sort((a, b) => (a.global_idx || 0) - (b.global_idx || 0));\n\n const pendingWrites: Array<[string, string, any]> = [];\n for (const writeDoc of writeDocuments) {\n pendingWrites.push([writeDoc.task_id, writeDoc.channel, writeDoc.value]);\n }\n\n return pendingWrites;\n }\n\n // Helper method to load checkpoint with pending writes\n private async loadCheckpointWithWrites(jsonDoc: any): Promise<{\n checkpoint: Checkpoint;\n pendingWrites?: Array<[string, string, any]>;\n }> {\n // Load checkpoint directly from JSON\n const checkpoint = { ...jsonDoc.checkpoint };\n\n // Migrate pending sends ONLY for OLD checkpoint versions (v < 4) with parents\n // Modern checkpoints (v >= 4) should NEVER have pending sends migrated\n if (checkpoint.v < 4 && jsonDoc.parent_checkpoint_id != null) {\n // Convert back from \"__empty__\" to empty string for migration\n const actualNs =\n jsonDoc.checkpoint_ns === \"__empty__\" ? \"\" : jsonDoc.checkpoint_ns;\n await this.migratePendingSends(\n checkpoint,\n jsonDoc.thread_id,\n actualNs,\n jsonDoc.parent_checkpoint_id\n );\n }\n\n // Load this checkpoint's own pending writes (but don't migrate them)\n let pendingWrites: Array<[string, string, any]> | undefined;\n if (jsonDoc.has_writes === \"true\") {\n // Convert back from \"__empty__\" to empty string for key lookup\n const actualNs =\n jsonDoc.checkpoint_ns === \"__empty__\" ? \"\" : jsonDoc.checkpoint_ns;\n pendingWrites = await this.loadPendingWrites(\n jsonDoc.thread_id,\n actualNs,\n jsonDoc.checkpoint_id\n );\n }\n\n return { checkpoint, pendingWrites };\n }\n\n // Migrate pending sends from parent checkpoint (matches SQLite implementation)\n private async migratePendingSends(\n checkpoint: Checkpoint,\n threadId: string,\n checkpointNs: string,\n parentCheckpointId: string\n ): Promise<void> {\n // Load pending writes from parent checkpoint that have TASKS channel\n const parentWrites = await this.loadPendingWrites(\n threadId,\n checkpointNs,\n parentCheckpointId\n );\n\n if (!parentWrites || parentWrites.length === 0) {\n return;\n }\n\n // Filter for TASKS channel writes only\n const taskWrites = parentWrites.filter(([, channel]) => channel === TASKS);\n\n if (taskWrites.length === 0) {\n return;\n }\n\n // Collect all task values in order\n const allTasks: any[] = [];\n for (const [, , value] of taskWrites) {\n allTasks.push(value);\n }\n\n // Add pending sends to checkpoint\n checkpoint.channel_values ??= {};\n checkpoint.channel_values[TASKS] = allTasks;\n\n // Add to versions (matches SQLite logic)\n checkpoint.channel_versions[TASKS] =\n Object.keys(checkpoint.channel_versions).length > 0\n ? maxChannelVersion(...Object.values(checkpoint.channel_versions))\n : 1;\n }\n\n // Helper method to create checkpoint tuple from json document\n private createCheckpointTuple(\n jsonDoc: any,\n checkpoint: Checkpoint,\n pendingWrites?: Array<[string, string, any]>\n ): CheckpointTuple {\n // Convert back from \"__empty__\" to empty string\n const checkpointNs =\n jsonDoc.checkpoint_ns === \"__empty__\" ? \"\" : jsonDoc.checkpoint_ns;\n\n return {\n config: {\n configurable: {\n thread_id: jsonDoc.thread_id,\n checkpoint_ns: checkpointNs,\n checkpoint_id: jsonDoc.checkpoint_id,\n },\n },\n checkpoint,\n metadata: jsonDoc.metadata,\n parentConfig: jsonDoc.parent_checkpoint_id\n ? {\n configurable: {\n thread_id: jsonDoc.thread_id,\n checkpoint_ns: checkpointNs,\n checkpoint_id: jsonDoc.parent_checkpoint_id,\n },\n }\n : undefined,\n pendingWrites,\n };\n }\n\n // Helper method to add searchable metadata fields\n private addSearchableMetadataFields(\n jsonDoc: any,\n metadata?: CheckpointMetadata\n ): void {\n if (!metadata) return;\n\n // Add common searchable fields at top level\n if (\"source\" in metadata) {\n jsonDoc.source = metadata.source;\n }\n if (\"step\" in metadata) {\n jsonDoc.step = metadata.step;\n }\n if (\"writes\" in metadata) {\n // Writes field needs to be JSON stringified for TAG search\n jsonDoc.writes =\n typeof metadata.writes === \"object\"\n ? JSON.stringify(metadata.writes)\n : metadata.writes;\n }\n if (\"score\" in metadata) {\n jsonDoc.score = metadata.score;\n }\n }\n\n // Helper method to apply TTL to keys\n private async applyTTL(...keys: string[]): Promise<void> {\n if (!this.ttlConfig?.defaultTTL) return;\n\n const ttlSeconds = Math.floor(this.ttlConfig.defaultTTL * 60);\n const results = await Promise.allSettled(\n keys.map((key) => this.client.expire(key, ttlSeconds))\n );\n\n // Log any failures but don't throw - TTL is best effort\n for (let i = 0; i < results.length; i++) {\n if (results[i].status === \"rejected\") {\n console.warn(\n `Failed to set TTL for key ${keys[i]}:`,\n (results[i] as PromiseRejectedResult).reason\n );\n }\n }\n }\n\n private async ensureIndexes(): Promise<void> {\n for (const schema of SCHEMAS) {\n try {\n // Try to create the index\n await this.client.ft.create(schema.index, schema.schema as any, {\n ON: \"JSON\",\n PREFIX: schema.prefix,\n });\n } catch (error: any) {\n // Ignore if index already exists\n if (!error.message?.includes(\"Index already exists\")) {\n console.error(\n `Failed to create index ${schema.index}:`,\n error.message\n );\n }\n }\n }\n }\n}\n\n// Helper function for deterministic object comparison\nfunction deterministicStringify(obj: any): string {\n if (obj === null || typeof obj !== \"object\") {\n return JSON.stringify(obj);\n }\n if (Array.isArray(obj)) {\n return JSON.stringify(obj.map((item) => deterministicStringify(item)));\n }\n const sortedObj: Record<string, any> = {};\n const sortedKeys = Object.keys(obj).sort();\n for (const key of sortedKeys) {\n sortedObj[key] = obj[key];\n }\n return JSON.stringify(sortedObj, (_, value) => {\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n const sorted: Record<string, any> = {};\n const keys = Object.keys(value).sort();\n for (const k of keys) {\n sorted[k] = value[k];\n }\n return sorted;\n }\n return value;\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["BaseCheckpointSaver","escapeRediSearchTagValue","TASKS"],"sources":["../src/index.ts"],"sourcesContent":["import {\n BaseCheckpointSaver,\n ChannelVersions,\n Checkpoint,\n CheckpointListOptions,\n CheckpointMetadata,\n CheckpointTuple,\n PendingWrite,\n uuid6,\n TASKS,\n maxChannelVersion,\n copyCheckpoint,\n} from \"@langchain/langgraph-checkpoint\";\nimport { RunnableConfig } from \"@langchain/core/runnables\";\nimport { createClient, createCluster } from \"redis\";\nimport { escapeRediSearchTagValue } from \"./utils.js\";\n\n// Type for Redis client - supports both standalone and cluster\nexport type RedisClientType =\n | ReturnType<typeof createClient>\n | ReturnType<typeof createCluster>;\n\nconst SCHEMAS = [\n {\n index: \"checkpoints\",\n prefix: \"checkpoint:\",\n schema: {\n \"$.thread_id\": { type: \"TAG\", AS: \"thread_id\" },\n \"$.checkpoint_ns\": { type: \"TAG\", AS: \"checkpoint_ns\" },\n \"$.checkpoint_id\": { type: \"TAG\", AS: \"checkpoint_id\" },\n \"$.parent_checkpoint_id\": { type: \"TAG\", AS: \"parent_checkpoint_id\" },\n \"$.checkpoint_ts\": { type: \"NUMERIC\", AS: \"checkpoint_ts\" },\n \"$.has_writes\": { type: \"TAG\", AS: \"has_writes\" },\n \"$.source\": { type: \"TAG\", AS: \"source\" },\n \"$.step\": { type: \"NUMERIC\", AS: \"step\" },\n },\n },\n {\n index: \"checkpoint_blobs\",\n prefix: \"checkpoint_blob:\",\n schema: {\n \"$.thread_id\": { type: \"TAG\", AS: \"thread_id\" },\n \"$.checkpoint_ns\": { type: \"TAG\", AS: \"checkpoint_ns\" },\n \"$.checkpoint_id\": { type: \"TAG\", AS: \"checkpoint_id\" },\n \"$.channel\": { type: \"TAG\", AS: \"channel\" },\n \"$.version\": { type: \"TAG\", AS: \"version\" },\n \"$.type\": { type: \"TAG\", AS: \"type\" },\n },\n },\n {\n index: \"checkpoint_writes\",\n prefix: \"checkpoint_write:\",\n schema: {\n \"$.thread_id\": { type: \"TAG\", AS: \"thread_id\" },\n \"$.checkpoint_ns\": { type: \"TAG\", AS: \"checkpoint_ns\" },\n \"$.checkpoint_id\": { type: \"TAG\", AS: \"checkpoint_id\" },\n \"$.task_id\": { type: \"TAG\", AS: \"task_id\" },\n \"$.idx\": { type: \"NUMERIC\", AS: \"idx\" },\n \"$.channel\": { type: \"TAG\", AS: \"channel\" },\n \"$.type\": { type: \"TAG\", AS: \"type\" },\n },\n },\n];\n\nexport interface TTLConfig {\n defaultTTL?: number; // TTL in minutes\n refreshOnRead?: boolean; // Whether to refresh TTL when reading\n}\n\ninterface CheckpointDocument {\n thread_id: string;\n checkpoint_ns: string;\n checkpoint_id: string;\n parent_checkpoint_id: string | null;\n checkpoint: Checkpoint & {\n channel_values?: Record<string, any>;\n channel_blobs?: Record<string, { __blob__: boolean; key: string }>;\n };\n metadata: CheckpointMetadata;\n checkpoint_ts: number;\n has_writes: string;\n source?: string;\n step?: number;\n [key: string]: any; // Allow additional fields for metadata\n}\n\nexport class RedisSaver extends BaseCheckpointSaver {\n private client: RedisClientType;\n private ttlConfig?: TTLConfig;\n\n constructor(client: RedisClientType, ttlConfig?: TTLConfig) {\n super();\n this.client = client;\n this.ttlConfig = ttlConfig;\n }\n\n static async fromUrl(\n url: string,\n ttlConfig?: TTLConfig\n ): Promise<RedisSaver> {\n const client = createClient({ url });\n await client.connect();\n const saver = new RedisSaver(client, ttlConfig);\n await saver.ensureIndexes();\n return saver;\n }\n\n static async fromCluster(\n rootNodes: Array<{ url: string }>,\n ttlConfig?: TTLConfig\n ): Promise<RedisSaver> {\n const client = createCluster({ rootNodes });\n await client.connect();\n const saver = new RedisSaver(client, ttlConfig);\n await saver.ensureIndexes();\n return saver;\n }\n\n async get(config: RunnableConfig): Promise<Checkpoint | undefined> {\n const tuple = await this.getTuple(config);\n return tuple?.checkpoint;\n }\n\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? \"\";\n const checkpointId = config.configurable?.checkpoint_id;\n\n if (!threadId) {\n return undefined;\n }\n\n let key: string;\n let jsonDoc: CheckpointDocument | null;\n\n if (checkpointId) {\n // Get specific checkpoint\n key = `checkpoint:${threadId}:${checkpointNs}:${checkpointId}`;\n jsonDoc = (await this.client.json.get(key)) as CheckpointDocument | null;\n } else {\n // Get latest checkpoint - need to search\n const pattern = `checkpoint:${threadId}:${checkpointNs}:*`;\n // Use keys for simplicity - scan would be better for large datasets\n const keys = await (this.client as any).keys(pattern);\n\n if (keys.length === 0) {\n return undefined;\n }\n\n // Sort by key to get latest\n keys.sort();\n key = keys[keys.length - 1];\n jsonDoc = (await this.client.json.get(key)) as CheckpointDocument | null;\n }\n\n if (!jsonDoc) {\n return undefined;\n }\n\n // Refresh TTL if configured\n if (this.ttlConfig?.refreshOnRead && this.ttlConfig?.defaultTTL) {\n await this.applyTTL(key);\n }\n\n // Load checkpoint with pending writes\n const { checkpoint, pendingWrites } = await this.loadCheckpointWithWrites(\n jsonDoc\n );\n\n return await this.createCheckpointTuple(jsonDoc, checkpoint, pendingWrites);\n }\n\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata,\n newVersions: ChannelVersions\n ): Promise<RunnableConfig> {\n await this.ensureIndexes();\n\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? \"\";\n const parentCheckpointId = config.configurable?.checkpoint_id;\n\n if (!threadId) {\n throw new Error(\"thread_id is required\");\n }\n\n const checkpointId = checkpoint.id || uuid6(0);\n const key = `checkpoint:${threadId}:${checkpointNs}:${checkpointId}`;\n\n // Copy checkpoint and filter channel_values to only include changed channels\n const storedCheckpoint = copyCheckpoint(checkpoint);\n\n // If newVersions is provided and has keys, only store those channels that changed\n // If newVersions is empty {}, store no channel values\n // If newVersions is not provided (undefined), keep all channel_values as-is\n if (storedCheckpoint.channel_values && newVersions !== undefined) {\n if (Object.keys(newVersions).length === 0) {\n // Empty newVersions means no channels changed - store empty channel_values\n storedCheckpoint.channel_values = {};\n } else {\n // Only store the channels that are in newVersions\n const filteredChannelValues: Record<string, any> = {};\n for (const channel of Object.keys(newVersions)) {\n if (channel in storedCheckpoint.channel_values) {\n filteredChannelValues[channel] =\n storedCheckpoint.channel_values[channel];\n }\n }\n storedCheckpoint.channel_values = filteredChannelValues;\n }\n }\n // If newVersions is undefined, keep all channel_values as-is (for backward compatibility)\n\n // Structure matching Python implementation\n const jsonDoc: CheckpointDocument = {\n thread_id: threadId,\n // Store empty namespace as \"__empty__\" for RediSearch compatibility\n checkpoint_ns: checkpointNs === \"\" ? \"__empty__\" : checkpointNs,\n checkpoint_id: checkpointId,\n parent_checkpoint_id: parentCheckpointId || null,\n checkpoint: storedCheckpoint,\n metadata: metadata,\n checkpoint_ts: Date.now(),\n has_writes: \"false\",\n };\n\n // Store metadata fields at top-level for searching\n this.addSearchableMetadataFields(jsonDoc, metadata);\n\n // Use Redis JSON commands\n await this.client.json.set(key, \"$\", jsonDoc as any);\n\n // Apply TTL if configured\n if (this.ttlConfig?.defaultTTL) {\n await this.applyTTL(key);\n }\n\n return {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNs,\n checkpoint_id: checkpointId,\n },\n };\n }\n\n async *list(\n config: RunnableConfig | null,\n options?: CheckpointListOptions & { filter?: CheckpointMetadata }\n ): AsyncGenerator<CheckpointTuple> {\n await this.ensureIndexes();\n\n // If filter is provided (even if empty), use search functionality\n if (options?.filter !== undefined) {\n // Check if we have null values in the filter which RediSearch can't handle\n const hasNullFilter = Object.values(options.filter).some(\n (v) => v === null\n );\n\n // Build search query\n const queryParts: string[] = [];\n\n // Add thread_id constraint if provided\n if (config?.configurable?.thread_id) {\n const threadId = config.configurable.thread_id.replace(\n /[-.@]/g,\n \"\\\\$&\"\n );\n queryParts.push(`(@thread_id:{${threadId}})`);\n }\n\n // Add checkpoint_ns constraint if provided\n if (config?.configurable?.checkpoint_ns !== undefined) {\n const checkpointNs = config.configurable.checkpoint_ns;\n if (checkpointNs === \"\") {\n // Empty string needs special handling in RediSearch\n // We'll store it as \"__empty__\" in the index\n queryParts.push(`(@checkpoint_ns:{__empty__})`);\n } else {\n const escapedNs = checkpointNs.replace(/[-.@]/g, \"\\\\$&\");\n queryParts.push(`(@checkpoint_ns:{${escapedNs}})`);\n }\n }\n\n // Skip metadata filters in search query when 'before' parameter is used\n // We'll apply them after the before filtering to get correct results\n if (!options?.before && options?.filter) {\n // Add metadata filters (but skip null values)\n for (const [key, value] of Object.entries(options.filter)) {\n if (value === undefined) {\n // Skip undefined filters\n } else if (value === null) {\n // Skip null values for RediSearch query, will handle in post-processing\n } else if (typeof value === \"string\") {\n // Escape both key and value to prevent RediSearch query injection\n const escapedKey = escapeRediSearchTagValue(key);\n const escapedValue = escapeRediSearchTagValue(value);\n queryParts.push(`(@${escapedKey}:{${escapedValue}})`);\n } else if (typeof value === \"number\") {\n // Escape key to prevent injection; numbers don't need value escaping\n const escapedKey = escapeRediSearchTagValue(key);\n queryParts.push(`(@${escapedKey}:[${value} ${value}])`);\n } else if (\n typeof value === \"object\" &&\n Object.keys(value).length === 0\n ) {\n // Skip empty objects\n }\n }\n }\n\n if (queryParts.length === 0) {\n queryParts.push(\"*\");\n }\n\n const query = queryParts.join(\" \");\n const limit = options?.limit ?? 10;\n\n try {\n // Fetch more results than the limit to handle post-filtering for 'before'\n // When no thread_id is specified but 'before' is used, we need to fetch all results\n const fetchLimit =\n options?.before && !config?.configurable?.thread_id\n ? 1000 // Fetch many results for global search with 'before' filtering\n : options?.before\n ? limit * 10\n : limit;\n\n const results = await this.client.ft.search(\"checkpoints\", query, {\n LIMIT: { from: 0, size: fetchLimit },\n SORTBY: { BY: \"checkpoint_ts\", DIRECTION: \"DESC\" },\n });\n\n let documents = results.documents;\n\n let yieldedCount = 0;\n\n for (const doc of documents) {\n if (yieldedCount >= limit) break;\n\n // Handle 'before' parameter - filter based on checkpoint_id comparison\n // UUID6 IDs are time-sortable, so string comparison works for ordering\n if (options?.before?.configurable?.checkpoint_id) {\n const currentCheckpointId = doc.value.checkpoint_id as string;\n const beforeCheckpointId =\n options.before.configurable.checkpoint_id;\n\n // Skip checkpoints that are not before the specified checkpoint\n if (currentCheckpointId >= beforeCheckpointId) {\n continue;\n }\n }\n\n const jsonDoc = doc.value;\n\n // Apply metadata filters manually (either for null filters or when before parameter was used)\n let matches = true;\n if ((hasNullFilter || options?.before) && options?.filter) {\n for (const [filterKey, filterValue] of Object.entries(\n options.filter\n )) {\n if (filterValue === null) {\n // Check if the field exists and is null in metadata\n // This should only match explicit null, not missing fields\n const metadataValue = (jsonDoc.metadata as any)?.[filterKey];\n if (metadataValue !== null) {\n matches = false;\n break;\n }\n } else if (filterValue !== undefined) {\n // Check other metadata values\n const metadataValue = (jsonDoc.metadata as any)?.[filterKey];\n // For objects, do deep equality check with deterministic key ordering\n if (typeof filterValue === \"object\" && filterValue !== null) {\n if (\n deterministicStringify(metadataValue) !==\n deterministicStringify(filterValue)\n ) {\n matches = false;\n break;\n }\n } else if (metadataValue !== filterValue) {\n matches = false;\n break;\n }\n }\n }\n if (!matches) continue;\n }\n\n // Load checkpoint with pending writes and migrate sends\n const { checkpoint, pendingWrites } =\n await this.loadCheckpointWithWrites(jsonDoc);\n yield await this.createCheckpointTuple(\n jsonDoc,\n checkpoint,\n pendingWrites\n );\n yieldedCount++;\n }\n\n // Search succeeded, return without falling through\n return;\n } catch (error: any) {\n if (error.message?.includes(\"no such index\")) {\n // Index doesn't exist yet, fall through to regular listing\n } else {\n throw error;\n }\n }\n\n // If search failed due to missing index, fall through to regular listing\n if (config?.configurable?.thread_id) {\n // Fall back to regular listing with manual filtering when thread_id is specified\n const threadId = config.configurable.thread_id;\n const checkpointNs = config.configurable.checkpoint_ns ?? \"\";\n const pattern = `checkpoint:${threadId}:${checkpointNs}:*`;\n // Use scan for better performance and cluster compatibility\n // Use keys for simplicity - scan would be better for large datasets\n const keys = await (this.client as any).keys(pattern);\n\n keys.sort().reverse();\n\n let filteredKeys = keys;\n\n // Handle 'before' parameter\n if (options?.before?.configurable?.checkpoint_id) {\n const beforeThreadId =\n options.before.configurable.thread_id || threadId;\n const beforeCheckpointNs =\n options.before.configurable.checkpoint_ns ?? checkpointNs;\n const beforeKey = `checkpoint:${beforeThreadId}:${beforeCheckpointNs}:${options.before.configurable.checkpoint_id}`;\n\n const beforeIndex = keys.indexOf(beforeKey);\n if (beforeIndex > 0) {\n // Return all items that come after the found index (i.e., before in time)\n filteredKeys = keys.slice(beforeIndex + 1);\n } else if (beforeIndex === 0) {\n // Nothing before the first item (most recent)\n filteredKeys = [];\n }\n // If not found, return all\n }\n\n const limit = options?.limit ?? 10;\n const limitedKeys = filteredKeys.slice(0, limit);\n\n for (const key of limitedKeys) {\n const jsonDoc = (await this.client.json.get(\n key\n )) as CheckpointDocument | null;\n if (jsonDoc) {\n // Check if metadata matches filter\n let matches = true;\n for (const [filterKey, filterValue] of Object.entries(\n options.filter\n )) {\n const metadataValue = (jsonDoc.metadata as any)?.[filterKey];\n if (filterValue === null) {\n if (metadataValue !== null) {\n matches = false;\n break;\n }\n } else if (metadataValue !== filterValue) {\n matches = false;\n break;\n }\n }\n\n if (!matches) continue;\n\n // Load checkpoint with pending writes and migrate sends\n const { checkpoint, pendingWrites } =\n await this.loadCheckpointWithWrites(jsonDoc);\n yield await this.createCheckpointTuple(\n jsonDoc,\n checkpoint,\n pendingWrites\n );\n }\n }\n } else {\n // Fall back to global search when thread_id is undefined\n // This is needed for validation tests that search globally with 'before' parameter\n const globalPattern =\n config?.configurable?.checkpoint_ns !== undefined\n ? `checkpoint:*:${\n config.configurable.checkpoint_ns === \"\"\n ? \"__empty__\"\n : config.configurable.checkpoint_ns\n }:*`\n : \"checkpoint:*\";\n\n const allKeys = await (this.client as any).keys(globalPattern);\n const allDocuments: { key: string; doc: CheckpointDocument }[] = [];\n\n // Load all matching documents\n for (const key of allKeys) {\n const jsonDoc = (await this.client.json.get(\n key\n )) as CheckpointDocument | null;\n if (jsonDoc) {\n allDocuments.push({ key, doc: jsonDoc });\n }\n }\n\n // Sort by timestamp (descending) to match the search behavior\n allDocuments.sort((a, b) => b.doc.checkpoint_ts - a.doc.checkpoint_ts);\n\n let yieldedCount = 0;\n const limit = options?.limit ?? 10;\n\n for (const { doc: jsonDoc } of allDocuments) {\n if (yieldedCount >= limit) break;\n\n // Handle 'before' parameter - filter based on checkpoint_id comparison\n if (options?.before?.configurable?.checkpoint_id) {\n const currentCheckpointId = jsonDoc.checkpoint_id;\n const beforeCheckpointId =\n options.before.configurable.checkpoint_id;\n\n // Skip checkpoints that are not before the specified checkpoint\n if (currentCheckpointId >= beforeCheckpointId) {\n continue;\n }\n }\n\n // Apply metadata filters manually\n let matches = true;\n if (options?.filter) {\n for (const [filterKey, filterValue] of Object.entries(\n options.filter\n )) {\n if (filterValue === null) {\n // Check if the field exists and is null in metadata\n const metadataValue = (jsonDoc.metadata as any)?.[filterKey];\n if (metadataValue !== null) {\n matches = false;\n break;\n }\n } else if (filterValue !== undefined) {\n // Check other metadata values\n const metadataValue = (jsonDoc.metadata as any)?.[filterKey];\n // For objects, do deep equality check with deterministic key ordering\n if (typeof filterValue === \"object\" && filterValue !== null) {\n if (\n deterministicStringify(metadataValue) !==\n deterministicStringify(filterValue)\n ) {\n matches = false;\n break;\n }\n } else if (metadataValue !== filterValue) {\n matches = false;\n break;\n }\n }\n }\n if (!matches) continue;\n }\n\n // Load checkpoint with pending writes and migrate sends\n const { checkpoint, pendingWrites } =\n await this.loadCheckpointWithWrites(jsonDoc);\n yield await this.createCheckpointTuple(\n jsonDoc,\n checkpoint,\n pendingWrites\n );\n yieldedCount++;\n }\n }\n\n return;\n }\n\n // Regular listing without filter - use search with empty filter instead\n // This ensures consistent behavior between filter={} and filter=undefined\n const searchOptions: CheckpointListOptions & {\n filter?: CheckpointMetadata;\n } = {\n ...options,\n filter: {} as CheckpointMetadata,\n };\n\n // Delegate to the search path\n yield* this.list(config, searchOptions);\n return;\n }\n\n async putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void> {\n await this.ensureIndexes();\n\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? \"\";\n const checkpointId = config.configurable?.checkpoint_id;\n\n if (!threadId || !checkpointId) {\n throw new Error(\"thread_id and checkpoint_id are required\");\n }\n\n // Collect write keys for sorted set tracking\n const writeKeys: string[] = [];\n\n // Use high-resolution timestamp to ensure unique ordering across putWrites calls\n const baseTimestamp = performance.now() * 1000; // Microsecond precision\n\n // Store each write as a separate indexed JSON document\n for (let idx = 0; idx < writes.length; idx++) {\n const [channel, value] = writes[idx];\n const writeKey = `checkpoint_write:${threadId}:${checkpointNs}:${checkpointId}:${taskId}:${idx}`;\n writeKeys.push(writeKey);\n\n const writeDoc = {\n thread_id: threadId,\n checkpoint_ns: checkpointNs,\n checkpoint_id: checkpointId,\n task_id: taskId,\n idx: idx,\n channel: channel,\n type: typeof value === \"object\" ? \"json\" : \"string\",\n value: value,\n timestamp: baseTimestamp,\n global_idx: baseTimestamp + idx, // Add microseconds for sub-millisecond ordering\n };\n\n await this.client.json.set(writeKey, \"$\", writeDoc as any);\n }\n\n // Register write keys in sorted set for efficient retrieval\n if (writeKeys.length > 0) {\n const zsetKey = `write_keys_zset:${threadId}:${checkpointNs}:${checkpointId}`;\n\n // Use timestamp + idx for scoring to maintain correct order\n const zaddArgs: Record<string, number> = {};\n writeKeys.forEach((key, idx) => {\n zaddArgs[key] = baseTimestamp + idx;\n });\n await this.client.zAdd(\n zsetKey,\n Object.entries(zaddArgs).map(([key, score]) => ({ score, value: key }))\n );\n\n // Apply TTL to write keys and zset if configured\n if (this.ttlConfig?.defaultTTL) {\n // Apply TTL to write keys and zset\n await this.applyTTL(...writeKeys, zsetKey);\n }\n }\n\n // Update checkpoint to indicate it has writes\n const checkpointKey = `checkpoint:${threadId}:${checkpointNs}:${checkpointId}`;\n const checkpointExists = await this.client.exists(checkpointKey);\n if (checkpointExists) {\n // Get the current document and update it\n const currentDoc = (await this.client.json.get(\n checkpointKey\n )) as CheckpointDocument | null;\n if (currentDoc) {\n currentDoc.has_writes = \"true\";\n await this.client.json.set(checkpointKey, \"$\", currentDoc as any);\n }\n }\n }\n\n async deleteThread(threadId: string): Promise<void> {\n // Delete checkpoints\n const checkpointPattern = `checkpoint:${threadId}:*`;\n // Use scan for better performance and cluster compatibility\n // Use keys for simplicity - scan would be better for large datasets\n const checkpointKeys = await (this.client as any).keys(checkpointPattern);\n\n if (checkpointKeys.length > 0) {\n await this.client.del(checkpointKeys);\n }\n\n // Delete writes\n const writesPattern = `writes:${threadId}:*`;\n // Use scan for better performance and cluster compatibility\n // Use keys for simplicity - scan would be better for large datasets\n const writesKeys = await (this.client as any).keys(writesPattern);\n\n if (writesKeys.length > 0) {\n await this.client.del(writesKeys);\n }\n }\n\n async end(): Promise<void> {\n await this.client.quit();\n }\n\n // Helper method to load channel blobs (simplified - no blob support for now)\n // private async loadChannelBlobs(\n // checkpoint: Checkpoint & { channel_blobs?: any }\n // ): Promise<Checkpoint> {\n // // Since we're not using blobs anymore, just return the checkpoint as-is\n // return checkpoint;\n // }\n\n // Helper method to load pending writes\n private async loadPendingWrites(\n threadId: string,\n checkpointNs: string,\n checkpointId: string\n ): Promise<Array<[string, string, any]> | undefined> {\n // Search for all write documents for this checkpoint\n const pattern = `checkpoint_write:${threadId}:${checkpointNs}:${checkpointId}:*`;\n const writeKeys = await (this.client as any).keys(pattern);\n\n if (writeKeys.length === 0) {\n return undefined;\n }\n\n const writeDocuments: any[] = [];\n for (const writeKey of writeKeys) {\n const writeDoc = (await this.client.json.get(writeKey)) as any;\n if (writeDoc) {\n writeDocuments.push(writeDoc);\n }\n }\n\n // Sort by global_idx (which represents insertion order across all putWrites calls)\n // This matches how SQLite would naturally order by insertion time + idx\n writeDocuments.sort((a, b) => (a.global_idx || 0) - (b.global_idx || 0));\n\n const pendingWrites: Array<[string, string, any]> = [];\n for (const writeDoc of writeDocuments) {\n // Deserialize write value using serde to restore LangChain objects\n const deserializedValue = await this.serde.loadsTyped(\n \"json\",\n JSON.stringify(writeDoc.value)\n );\n pendingWrites.push([\n writeDoc.task_id,\n writeDoc.channel,\n deserializedValue,\n ]);\n }\n\n return pendingWrites;\n }\n\n // Helper method to load checkpoint with pending writes\n private async loadCheckpointWithWrites(jsonDoc: any): Promise<{\n checkpoint: Checkpoint;\n pendingWrites?: Array<[string, string, any]>;\n }> {\n // Deserialize checkpoint using serde to restore LangChain objects\n const checkpoint: Checkpoint = await this.serde.loadsTyped(\n \"json\",\n JSON.stringify(jsonDoc.checkpoint)\n );\n\n // Migrate pending sends ONLY for OLD checkpoint versions (v < 4) with parents\n // Modern checkpoints (v >= 4) should NEVER have pending sends migrated\n if (checkpoint.v < 4 && jsonDoc.parent_checkpoint_id != null) {\n // Convert back from \"__empty__\" to empty string for migration\n const actualNs =\n jsonDoc.checkpoint_ns === \"__empty__\" ? \"\" : jsonDoc.checkpoint_ns;\n await this.migratePendingSends(\n checkpoint,\n jsonDoc.thread_id,\n actualNs,\n jsonDoc.parent_checkpoint_id\n );\n }\n\n // Load this checkpoint's own pending writes (but don't migrate them)\n let pendingWrites: Array<[string, string, any]> | undefined;\n if (jsonDoc.has_writes === \"true\") {\n // Convert back from \"__empty__\" to empty string for key lookup\n const actualNs =\n jsonDoc.checkpoint_ns === \"__empty__\" ? \"\" : jsonDoc.checkpoint_ns;\n pendingWrites = await this.loadPendingWrites(\n jsonDoc.thread_id,\n actualNs,\n jsonDoc.checkpoint_id\n );\n }\n\n return { checkpoint, pendingWrites };\n }\n\n // Migrate pending sends from parent checkpoint (matches SQLite implementation)\n private async migratePendingSends(\n checkpoint: Checkpoint,\n threadId: string,\n checkpointNs: string,\n parentCheckpointId: string\n ): Promise<void> {\n // Load pending writes from parent checkpoint that have TASKS channel\n const parentWrites = await this.loadPendingWrites(\n threadId,\n checkpointNs,\n parentCheckpointId\n );\n\n if (!parentWrites || parentWrites.length === 0) {\n return;\n }\n\n // Filter for TASKS channel writes only\n const taskWrites = parentWrites.filter(([, channel]) => channel === TASKS);\n\n if (taskWrites.length === 0) {\n return;\n }\n\n // Collect all task values in order\n const allTasks: any[] = [];\n for (const [, , value] of taskWrites) {\n allTasks.push(value);\n }\n\n // Add pending sends to checkpoint\n checkpoint.channel_values ??= {};\n checkpoint.channel_values[TASKS] = allTasks;\n\n // Add to versions (matches SQLite logic)\n checkpoint.channel_versions[TASKS] =\n Object.keys(checkpoint.channel_versions).length > 0\n ? maxChannelVersion(...Object.values(checkpoint.channel_versions))\n : 1;\n }\n\n // Helper method to create checkpoint tuple from json document\n private async createCheckpointTuple(\n jsonDoc: any,\n checkpoint: Checkpoint,\n pendingWrites?: Array<[string, string, any]>\n ): Promise<CheckpointTuple> {\n // Convert back from \"__empty__\" to empty string\n const checkpointNs =\n jsonDoc.checkpoint_ns === \"__empty__\" ? \"\" : jsonDoc.checkpoint_ns;\n\n // Deserialize metadata using serde\n const metadata = (await this.serde.loadsTyped(\n \"json\",\n JSON.stringify(jsonDoc.metadata)\n )) as CheckpointMetadata;\n\n return {\n config: {\n configurable: {\n thread_id: jsonDoc.thread_id,\n checkpoint_ns: checkpointNs,\n checkpoint_id: jsonDoc.checkpoint_id,\n },\n },\n checkpoint,\n metadata,\n parentConfig: jsonDoc.parent_checkpoint_id\n ? {\n configurable: {\n thread_id: jsonDoc.thread_id,\n checkpoint_ns: checkpointNs,\n checkpoint_id: jsonDoc.parent_checkpoint_id,\n },\n }\n : undefined,\n pendingWrites,\n };\n }\n\n // Helper method to add searchable metadata fields\n private addSearchableMetadataFields(\n jsonDoc: any,\n metadata?: CheckpointMetadata\n ): void {\n if (!metadata) return;\n\n // Add common searchable fields at top level\n if (\"source\" in metadata) {\n jsonDoc.source = metadata.source;\n }\n if (\"step\" in metadata) {\n jsonDoc.step = metadata.step;\n }\n if (\"writes\" in metadata) {\n // Writes field needs to be JSON stringified for TAG search\n jsonDoc.writes =\n typeof metadata.writes === \"object\"\n ? JSON.stringify(metadata.writes)\n : metadata.writes;\n }\n if (\"score\" in metadata) {\n jsonDoc.score = metadata.score;\n }\n }\n\n // Helper method to apply TTL to keys\n private async applyTTL(...keys: string[]): Promise<void> {\n if (!this.ttlConfig?.defaultTTL) return;\n\n const ttlSeconds = Math.floor(this.ttlConfig.defaultTTL * 60);\n const results = await Promise.allSettled(\n keys.map((key) => this.client.expire(key, ttlSeconds))\n );\n\n // Log any failures but don't throw - TTL is best effort\n for (let i = 0; i < results.length; i++) {\n if (results[i].status === \"rejected\") {\n console.warn(\n `Failed to set TTL for key ${keys[i]}:`,\n (results[i] as PromiseRejectedResult).reason\n );\n }\n }\n }\n\n private async ensureIndexes(): Promise<void> {\n for (const schema of SCHEMAS) {\n try {\n // Try to create the index\n await this.client.ft.create(schema.index, schema.schema as any, {\n ON: \"JSON\",\n PREFIX: schema.prefix,\n });\n } catch (error: any) {\n // Ignore if index already exists\n if (!error.message?.includes(\"Index already exists\")) {\n console.error(\n `Failed to create index ${schema.index}:`,\n error.message\n );\n }\n }\n }\n }\n}\n\n// Helper function for deterministic object comparison\nfunction deterministicStringify(obj: any): string {\n if (obj === null || typeof obj !== \"object\") {\n return JSON.stringify(obj);\n }\n if (Array.isArray(obj)) {\n return JSON.stringify(obj.map((item) => deterministicStringify(item)));\n }\n const sortedObj: Record<string, any> = {};\n const sortedKeys = Object.keys(obj).sort();\n for (const key of sortedKeys) {\n sortedObj[key] = obj[key];\n }\n return JSON.stringify(sortedObj, (_, value) => {\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n const sorted: Record<string, any> = {};\n const keys = Object.keys(value).sort();\n for (const k of keys) {\n sorted[k] = value[k];\n }\n return sorted;\n }\n return value;\n });\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -3,11 +3,10 @@ import { RunnableConfig } from "@langchain/core/runnables";
3
3
  import { createClient, createCluster } from "redis";
4
4
 
5
5
  //#region src/index.d.ts
6
- // Type for Redis client - supports both standalone and cluster
7
6
  type RedisClientType = ReturnType<typeof createClient> | ReturnType<typeof createCluster>;
8
7
  interface TTLConfig {
9
- defaultTTL?: number; // TTL in minutes
10
- refreshOnRead?: boolean; // Whether to refresh TTL when reading
8
+ defaultTTL?: number;
9
+ refreshOnRead?: boolean;
11
10
  }
12
11
  declare class RedisSaver extends BaseCheckpointSaver {
13
12
  private client;
@@ -29,9 +28,7 @@ declare class RedisSaver extends BaseCheckpointSaver {
29
28
  private loadPendingWrites;
30
29
  private loadCheckpointWithWrites;
31
30
  private migratePendingSends;
32
- // Helper method to create checkpoint tuple from json document
33
31
  private createCheckpointTuple;
34
- // Helper method to add searchable metadata fields
35
32
  private addSearchableMetadataFields;
36
33
  private applyTTL;
37
34
  private ensureIndexes;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":["BaseCheckpointSaver","ChannelVersions","Checkpoint","CheckpointListOptions","CheckpointMetadata","CheckpointTuple","PendingWrite","RunnableConfig","createClient","createCluster","RedisClientType","ReturnType","TTLConfig","RedisSaver","Promise","Array","AsyncGenerator"],"sources":["../src/index.d.ts"],"sourcesContent":["import { BaseCheckpointSaver, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointMetadata, CheckpointTuple, PendingWrite } from \"@langchain/langgraph-checkpoint\";\nimport { RunnableConfig } from \"@langchain/core/runnables\";\nimport { createClient, createCluster } from \"redis\";\n// Type for Redis client - supports both standalone and cluster\nexport type RedisClientType = ReturnType<typeof createClient> | ReturnType<typeof createCluster>;\nexport interface TTLConfig {\n defaultTTL?: number; // TTL in minutes\n refreshOnRead?: boolean; // Whether to refresh TTL when reading\n}\nexport declare class RedisSaver extends BaseCheckpointSaver {\n private client;\n private ttlConfig?;\n constructor(client: RedisClientType, ttlConfig?: TTLConfig);\n static fromUrl(url: string, ttlConfig?: TTLConfig): Promise<RedisSaver>;\n static fromCluster(rootNodes: Array<{\n url: string;\n }>, ttlConfig?: TTLConfig): Promise<RedisSaver>;\n get(config: RunnableConfig): Promise<Checkpoint | undefined>;\n getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;\n put(config: RunnableConfig, checkpoint: Checkpoint, metadata: CheckpointMetadata, newVersions: ChannelVersions): Promise<RunnableConfig>;\n list(config: RunnableConfig | null, options?: CheckpointListOptions & {\n filter?: CheckpointMetadata;\n }): AsyncGenerator<CheckpointTuple>;\n putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;\n deleteThread(threadId: string): Promise<void>;\n end(): Promise<void>;\n private loadPendingWrites;\n private loadCheckpointWithWrites;\n private migratePendingSends;\n // Helper method to create checkpoint tuple from json document\n private createCheckpointTuple;\n // Helper method to add searchable metadata fields\n private addSearchableMetadataFields;\n private applyTTL;\n private ensureIndexes;\n}\n//# sourceMappingURL=index.d.ts.map"],"mappings":";;;;;;KAIYU,eAAAA,GAAkBC,kBAAkBH,gBAAgBG,kBAAkBF;AAAtEC,UACKE,SAAAA,CADU;EAAA,UAAA,CAAA,EAAA,MAAA,CAAA,CAAA;eAAqBJ,CAAAA,EAAAA,OAAAA,CAAAA,CAAAA;;AAAkCC,cAK7DI,UAAAA,SAAmBb,mBAAAA,CAL0CS;UAAlBE,MAAAA;;EAC/CC,WAAAA,CAAS,MAAA,EAOFF,eAPE,EAAA,SAAA,CAAA,EAO2BE,SAP3B;EAILC,OAAAA,OAAU,CAAA,GAAA,EAAA,MAAA,EAAA,SAAA,CAAA,EAIaD,SAJb,CAAA,EAIyBE,OAJzB,CAIiCD,UAJjC,CAAA;EAAA,OAAA,WAAA,CAAA,SAAA,EAKGE,KALH,CAAA;IAGPL,GAAAA,EAAAA,MAAAA;MAA6BE,SAAAA,CAAAA,EAIjCA,SAJiCA,CAAAA,EAIrBE,OAJqBF,CAIbC,UAJaD,CAAAA;KACTA,CAAAA,MAAAA,EAI5BL,cAJ4BK,CAAAA,EAIXE,OAJWF,CAIHV,UAJGU,GAAAA,SAAAA,CAAAA;UAAoBC,CAAAA,MAAAA,EAK3CN,cAL2CM,CAAAA,EAK1BC,OAL0BD,CAKlBR,eALkBQ,GAAAA,SAAAA,CAAAA;KAARC,CAAAA,MAAAA,EAMxCP,cANwCO,EAAAA,UAAAA,EAMZZ,UANYY,EAAAA,QAAAA,EAMUV,kBANVU,EAAAA,WAAAA,EAM2Cb,eAN3Ca,CAAAA,EAM6DA,OAN7DA,CAMqEP,cANrEO,CAAAA;MACtBC,CAAAA,MAAAA,EAMjBR,cANiBQ,GAAAA,IAAAA,EAAAA,QAAAA,EAMgBZ,qBANhBY,GAAAA;IAEdH,MAAAA,CAAAA,EAKHR,kBALGQ;MAMZI,cANgCH,CAMjBR,eANiBQ,CAAAA;WAARC,CAAAA,MAAAA,EAOVP,cAPUO,EAAAA,MAAAA,EAOcR,YAPdQ,EAAAA,EAAAA,MAAAA,EAAAA,MAAAA,CAAAA,EAO+CA,OAP/CA,CAAAA,IAAAA,CAAAA;cAChBP,CAAAA,QAAAA,EAAAA,MAAAA,CAAAA,EAOoBO,OAPpBP,CAAAA,IAAAA,CAAAA;KAAyBL,CAAAA,CAAAA,EAQ9BY,OAR8BZ,CAAAA,IAAAA,CAAAA;UAARY,iBAAAA;UACZP,wBAAAA;UAAyBF,mBAAAA;;UAC9BE,qBAAAA;;UAAkDH,2BAAAA;UAAiCH,QAAAA;UAA0BM,aAAAA"}
1
+ {"version":3,"file":"index.d.cts","names":["BaseCheckpointSaver","ChannelVersions","Checkpoint","CheckpointListOptions","CheckpointMetadata","CheckpointTuple","PendingWrite","RunnableConfig","createClient","createCluster","RedisClientType","ReturnType","TTLConfig","RedisSaver","Promise","Array","AsyncGenerator"],"sources":["../src/index.d.ts"],"sourcesContent":["import { BaseCheckpointSaver, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointMetadata, CheckpointTuple, PendingWrite } from \"@langchain/langgraph-checkpoint\";\nimport { RunnableConfig } from \"@langchain/core/runnables\";\nimport { createClient, createCluster } from \"redis\";\nexport type RedisClientType = ReturnType<typeof createClient> | ReturnType<typeof createCluster>;\nexport interface TTLConfig {\n defaultTTL?: number;\n refreshOnRead?: boolean;\n}\nexport declare class RedisSaver extends BaseCheckpointSaver {\n private client;\n private ttlConfig?;\n constructor(client: RedisClientType, ttlConfig?: TTLConfig);\n static fromUrl(url: string, ttlConfig?: TTLConfig): Promise<RedisSaver>;\n static fromCluster(rootNodes: Array<{\n url: string;\n }>, ttlConfig?: TTLConfig): Promise<RedisSaver>;\n get(config: RunnableConfig): Promise<Checkpoint | undefined>;\n getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;\n put(config: RunnableConfig, checkpoint: Checkpoint, metadata: CheckpointMetadata, newVersions: ChannelVersions): Promise<RunnableConfig>;\n list(config: RunnableConfig | null, options?: CheckpointListOptions & {\n filter?: CheckpointMetadata;\n }): AsyncGenerator<CheckpointTuple>;\n putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;\n deleteThread(threadId: string): Promise<void>;\n end(): Promise<void>;\n private loadPendingWrites;\n private loadCheckpointWithWrites;\n private migratePendingSends;\n private createCheckpointTuple;\n private addSearchableMetadataFields;\n private applyTTL;\n private ensureIndexes;\n}\n//# sourceMappingURL=index.d.ts.map"],"mappings":";;;;;KAGYU,eAAAA,GAAkBC,kBAAkBH,gBAAgBG,kBAAkBF;UACjEG,SAAAA;EADLF,UAAAA,CAAAA,EAAAA,MAAe;EAAA,aAAA,CAAA,EAAA,OAAA;;AAAGC,cAKTE,UAAAA,SAAmBb,mBAAAA,CALVW;UAAoDF,MAAAA;UAAlBE,SAAAA;EAAU,WAAA,CAAA,MAAA,EAQlDD,eARkD,EAAA,SAAA,CAAA,EAQrBE,SARqB;EACzDA,OAAAA,OAAS,CAAA,GAAA,EAAA,MAAA,EAAA,SAAA,CAAA,EAQkBA,SARlB,CAAA,EAQ8BE,OAR9B,CAQsCD,UARtC,CAAA;EAILA,OAAAA,WAAU,CAAA,SAAA,EAKGE,KALH,CAAA;IAAA,GAAA,EAAA,MAAA;MAGPL,SAAAA,CAAAA,EAIJE,SAJIF,CAAAA,EAIQI,OAJRJ,CAIgBG,UAJhBH,CAAAA;KAA6BE,CAAAA,MAAAA,EAKrCL,cALqCK,CAAAA,EAKpBE,OALoBF,CAKZV,UALYU,GAAAA,SAAAA,CAAAA;UACTA,CAAAA,MAAAA,EAKvBL,cALuBK,CAAAA,EAKNE,OALMF,CAKEP,eALFO,GAAAA,SAAAA,CAAAA;KAAoBC,CAAAA,MAAAA,EAMhDN,cANgDM,EAAAA,UAAAA,EAMpBX,UANoBW,EAAAA,QAAAA,EAMET,kBANFS,EAAAA,WAAAA,EAMmCZ,eANnCY,CAAAA,EAMqDC,OANrDD,CAM6DN,cAN7DM,CAAAA;MAARC,CAAAA,MAAAA,EAOvCP,cAPuCO,GAAAA,IAAAA,EAAAA,QAAAA,EAONX,qBAPMW,GAAAA;IACtBC,MAAAA,CAAAA,EAOjBX,kBAPiBW;MAQ1BC,cANYJ,CAMGP,eANHO,CAAAA;WAAoBC,CAAAA,MAAAA,EAOlBN,cAPkBM,EAAAA,MAAAA,EAOMP,YAPNO,EAAAA,EAAAA,MAAAA,EAAAA,MAAAA,CAAAA,EAOuCC,OAPvCD,CAAAA,IAAAA,CAAAA;cAARC,CAAAA,QAAAA,EAAAA,MAAAA,CAAAA,EAQIA,OARJA,CAAAA,IAAAA,CAAAA;KAChBP,CAAAA,CAAAA,EAQLO,OARKP,CAAAA,IAAAA,CAAAA;UAAyBL,iBAAAA;UAARY,wBAAAA;UACZP,mBAAAA;UAAyBF,qBAAAA;UAARS,2BAAAA;UACtBP,QAAAA;UAA4BL,aAAAA"}
package/dist/index.d.ts CHANGED
@@ -3,11 +3,10 @@ import { createClient, createCluster } from "redis";
3
3
  import { RunnableConfig } from "@langchain/core/runnables";
4
4
 
5
5
  //#region src/index.d.ts
6
- // Type for Redis client - supports both standalone and cluster
7
6
  type RedisClientType = ReturnType<typeof createClient> | ReturnType<typeof createCluster>;
8
7
  interface TTLConfig {
9
- defaultTTL?: number; // TTL in minutes
10
- refreshOnRead?: boolean; // Whether to refresh TTL when reading
8
+ defaultTTL?: number;
9
+ refreshOnRead?: boolean;
11
10
  }
12
11
  declare class RedisSaver extends BaseCheckpointSaver {
13
12
  private client;
@@ -29,9 +28,7 @@ declare class RedisSaver extends BaseCheckpointSaver {
29
28
  private loadPendingWrites;
30
29
  private loadCheckpointWithWrites;
31
30
  private migratePendingSends;
32
- // Helper method to create checkpoint tuple from json document
33
31
  private createCheckpointTuple;
34
- // Helper method to add searchable metadata fields
35
32
  private addSearchableMetadataFields;
36
33
  private applyTTL;
37
34
  private ensureIndexes;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":["BaseCheckpointSaver","ChannelVersions","Checkpoint","CheckpointListOptions","CheckpointMetadata","CheckpointTuple","PendingWrite","RunnableConfig","createClient","createCluster","RedisClientType","ReturnType","TTLConfig","RedisSaver","Promise","Array","AsyncGenerator"],"sources":["../src/index.d.ts"],"sourcesContent":["import { BaseCheckpointSaver, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointMetadata, CheckpointTuple, PendingWrite } from \"@langchain/langgraph-checkpoint\";\nimport { RunnableConfig } from \"@langchain/core/runnables\";\nimport { createClient, createCluster } from \"redis\";\n// Type for Redis client - supports both standalone and cluster\nexport type RedisClientType = ReturnType<typeof createClient> | ReturnType<typeof createCluster>;\nexport interface TTLConfig {\n defaultTTL?: number; // TTL in minutes\n refreshOnRead?: boolean; // Whether to refresh TTL when reading\n}\nexport declare class RedisSaver extends BaseCheckpointSaver {\n private client;\n private ttlConfig?;\n constructor(client: RedisClientType, ttlConfig?: TTLConfig);\n static fromUrl(url: string, ttlConfig?: TTLConfig): Promise<RedisSaver>;\n static fromCluster(rootNodes: Array<{\n url: string;\n }>, ttlConfig?: TTLConfig): Promise<RedisSaver>;\n get(config: RunnableConfig): Promise<Checkpoint | undefined>;\n getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;\n put(config: RunnableConfig, checkpoint: Checkpoint, metadata: CheckpointMetadata, newVersions: ChannelVersions): Promise<RunnableConfig>;\n list(config: RunnableConfig | null, options?: CheckpointListOptions & {\n filter?: CheckpointMetadata;\n }): AsyncGenerator<CheckpointTuple>;\n putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;\n deleteThread(threadId: string): Promise<void>;\n end(): Promise<void>;\n private loadPendingWrites;\n private loadCheckpointWithWrites;\n private migratePendingSends;\n // Helper method to create checkpoint tuple from json document\n private createCheckpointTuple;\n // Helper method to add searchable metadata fields\n private addSearchableMetadataFields;\n private applyTTL;\n private ensureIndexes;\n}\n//# sourceMappingURL=index.d.ts.map"],"mappings":";;;;;;KAIYU,eAAAA,GAAkBC,kBAAkBH,gBAAgBG,kBAAkBF;AAAtEC,UACKE,SAAAA,CADU;EAAA,UAAA,CAAA,EAAA,MAAA,CAAA,CAAA;eAAqBJ,CAAAA,EAAAA,OAAAA,CAAAA,CAAAA;;AAAkCC,cAK7DI,UAAAA,SAAmBb,mBAAAA,CAL0CS;UAAlBE,MAAAA;;EAC/CC,WAAAA,CAAS,MAAA,EAOFF,eAPE,EAAA,SAAA,CAAA,EAO2BE,SAP3B;EAILC,OAAAA,OAAU,CAAA,GAAA,EAAA,MAAA,EAAA,SAAA,CAAA,EAIaD,SAJb,CAAA,EAIyBE,OAJzB,CAIiCD,UAJjC,CAAA;EAAA,OAAA,WAAA,CAAA,SAAA,EAKGE,KALH,CAAA;IAGPL,GAAAA,EAAAA,MAAAA;MAA6BE,SAAAA,CAAAA,EAIjCA,SAJiCA,CAAAA,EAIrBE,OAJqBF,CAIbC,UAJaD,CAAAA;KACTA,CAAAA,MAAAA,EAI5BL,cAJ4BK,CAAAA,EAIXE,OAJWF,CAIHV,UAJGU,GAAAA,SAAAA,CAAAA;UAAoBC,CAAAA,MAAAA,EAK3CN,cAL2CM,CAAAA,EAK1BC,OAL0BD,CAKlBR,eALkBQ,GAAAA,SAAAA,CAAAA;KAARC,CAAAA,MAAAA,EAMxCP,cANwCO,EAAAA,UAAAA,EAMZZ,UANYY,EAAAA,QAAAA,EAMUV,kBANVU,EAAAA,WAAAA,EAM2Cb,eAN3Ca,CAAAA,EAM6DA,OAN7DA,CAMqEP,cANrEO,CAAAA;MACtBC,CAAAA,MAAAA,EAMjBR,cANiBQ,GAAAA,IAAAA,EAAAA,QAAAA,EAMgBZ,qBANhBY,GAAAA;IAEdH,MAAAA,CAAAA,EAKHR,kBALGQ;MAMZI,cANgCH,CAMjBR,eANiBQ,CAAAA;WAARC,CAAAA,MAAAA,EAOVP,cAPUO,EAAAA,MAAAA,EAOcR,YAPdQ,EAAAA,EAAAA,MAAAA,EAAAA,MAAAA,CAAAA,EAO+CA,OAP/CA,CAAAA,IAAAA,CAAAA;cAChBP,CAAAA,QAAAA,EAAAA,MAAAA,CAAAA,EAOoBO,OAPpBP,CAAAA,IAAAA,CAAAA;KAAyBL,CAAAA,CAAAA,EAQ9BY,OAR8BZ,CAAAA,IAAAA,CAAAA;UAARY,iBAAAA;UACZP,wBAAAA;UAAyBF,mBAAAA;;UAC9BE,qBAAAA;;UAAkDH,2BAAAA;UAAiCH,QAAAA;UAA0BM,aAAAA"}
1
+ {"version":3,"file":"index.d.ts","names":["BaseCheckpointSaver","ChannelVersions","Checkpoint","CheckpointListOptions","CheckpointMetadata","CheckpointTuple","PendingWrite","RunnableConfig","createClient","createCluster","RedisClientType","ReturnType","TTLConfig","RedisSaver","Promise","Array","AsyncGenerator"],"sources":["../src/index.d.ts"],"sourcesContent":["import { BaseCheckpointSaver, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointMetadata, CheckpointTuple, PendingWrite } from \"@langchain/langgraph-checkpoint\";\nimport { RunnableConfig } from \"@langchain/core/runnables\";\nimport { createClient, createCluster } from \"redis\";\nexport type RedisClientType = ReturnType<typeof createClient> | ReturnType<typeof createCluster>;\nexport interface TTLConfig {\n defaultTTL?: number;\n refreshOnRead?: boolean;\n}\nexport declare class RedisSaver extends BaseCheckpointSaver {\n private client;\n private ttlConfig?;\n constructor(client: RedisClientType, ttlConfig?: TTLConfig);\n static fromUrl(url: string, ttlConfig?: TTLConfig): Promise<RedisSaver>;\n static fromCluster(rootNodes: Array<{\n url: string;\n }>, ttlConfig?: TTLConfig): Promise<RedisSaver>;\n get(config: RunnableConfig): Promise<Checkpoint | undefined>;\n getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;\n put(config: RunnableConfig, checkpoint: Checkpoint, metadata: CheckpointMetadata, newVersions: ChannelVersions): Promise<RunnableConfig>;\n list(config: RunnableConfig | null, options?: CheckpointListOptions & {\n filter?: CheckpointMetadata;\n }): AsyncGenerator<CheckpointTuple>;\n putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;\n deleteThread(threadId: string): Promise<void>;\n end(): Promise<void>;\n private loadPendingWrites;\n private loadCheckpointWithWrites;\n private migratePendingSends;\n private createCheckpointTuple;\n private addSearchableMetadataFields;\n private applyTTL;\n private ensureIndexes;\n}\n//# sourceMappingURL=index.d.ts.map"],"mappings":";;;;;KAGYU,eAAAA,GAAkBC,kBAAkBH,gBAAgBG,kBAAkBF;UACjEG,SAAAA;EADLF,UAAAA,CAAAA,EAAAA,MAAe;EAAA,aAAA,CAAA,EAAA,OAAA;;AAAGC,cAKTE,UAAAA,SAAmBb,mBAAAA,CALVW;UAAoDF,MAAAA;UAAlBE,SAAAA;EAAU,WAAA,CAAA,MAAA,EAQlDD,eARkD,EAAA,SAAA,CAAA,EAQrBE,SARqB;EACzDA,OAAAA,OAAS,CAAA,GAAA,EAAA,MAAA,EAAA,SAAA,CAAA,EAQkBA,SARlB,CAAA,EAQ8BE,OAR9B,CAQsCD,UARtC,CAAA;EAILA,OAAAA,WAAU,CAAA,SAAA,EAKGE,KALH,CAAA;IAAA,GAAA,EAAA,MAAA;MAGPL,SAAAA,CAAAA,EAIJE,SAJIF,CAAAA,EAIQI,OAJRJ,CAIgBG,UAJhBH,CAAAA;KAA6BE,CAAAA,MAAAA,EAKrCL,cALqCK,CAAAA,EAKpBE,OALoBF,CAKZV,UALYU,GAAAA,SAAAA,CAAAA;UACTA,CAAAA,MAAAA,EAKvBL,cALuBK,CAAAA,EAKNE,OALMF,CAKEP,eALFO,GAAAA,SAAAA,CAAAA;KAAoBC,CAAAA,MAAAA,EAMhDN,cANgDM,EAAAA,UAAAA,EAMpBX,UANoBW,EAAAA,QAAAA,EAMET,kBANFS,EAAAA,WAAAA,EAMmCZ,eANnCY,CAAAA,EAMqDC,OANrDD,CAM6DN,cAN7DM,CAAAA;MAARC,CAAAA,MAAAA,EAOvCP,cAPuCO,GAAAA,IAAAA,EAAAA,QAAAA,EAONX,qBAPMW,GAAAA;IACtBC,MAAAA,CAAAA,EAOjBX,kBAPiBW;MAQ1BC,cANYJ,CAMGP,eANHO,CAAAA;WAAoBC,CAAAA,MAAAA,EAOlBN,cAPkBM,EAAAA,MAAAA,EAOMP,YAPNO,EAAAA,EAAAA,MAAAA,EAAAA,MAAAA,CAAAA,EAOuCC,OAPvCD,CAAAA,IAAAA,CAAAA;cAARC,CAAAA,QAAAA,EAAAA,MAAAA,CAAAA,EAQIA,OARJA,CAAAA,IAAAA,CAAAA;KAChBP,CAAAA,CAAAA,EAQLO,OARKP,CAAAA,IAAAA,CAAAA;UAAyBL,iBAAAA;UAARY,wBAAAA;UACZP,mBAAAA;UAAyBF,qBAAAA;UAARS,2BAAAA;UACtBP,QAAAA;UAA4BL,aAAAA"}