UNPKG

@langchain/langgraph-checkpoint-sqlite

Version:
1 lines 21.1 kB
{"version":3,"file":"index.cjs","names":["TASKS","BaseCheckpointSaver","Database"],"sources":["../src/index.ts"],"sourcesContent":["import Database, { Database as DatabaseType, Statement } from \"better-sqlite3\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseCheckpointSaver,\n type Checkpoint,\n type CheckpointListOptions,\n type CheckpointTuple,\n type SerializerProtocol,\n type PendingWrite,\n type CheckpointMetadata,\n TASKS,\n copyCheckpoint,\n maxChannelVersion,\n} from \"@langchain/langgraph-checkpoint\";\n\ninterface CheckpointRow {\n checkpoint: string;\n metadata: string;\n parent_checkpoint_id?: string;\n thread_id: string;\n checkpoint_id: string;\n checkpoint_ns?: string;\n type?: string;\n pending_writes: string;\n}\n\ninterface PendingWriteColumn {\n task_id: string;\n channel: string;\n type: string;\n value: string;\n}\n\ninterface PendingSendColumn {\n type: string;\n value: string;\n}\n\n// In the `SqliteSaver.list` method, we need to sanitize the `options.filter` argument to ensure it only contains keys\n// that are part of the `CheckpointMetadata` type. The lines below ensure that we get compile-time errors if the list\n// of keys that we use is out of sync with the `CheckpointMetadata` type.\nconst checkpointMetadataKeys = [\"source\", \"step\", \"parents\"] as const;\n\ntype CheckKeys<T, K extends readonly (keyof T)[]> = [K[number]] extends [\n keyof T\n]\n ? [keyof T] extends [K[number]]\n ? K\n : never\n : never;\n\nfunction validateKeys<T, K extends readonly (keyof T)[]>(\n keys: CheckKeys<T, K>\n): K {\n return keys;\n}\n\n// If this line fails to compile, the list of keys that we use in the `SqliteSaver.list` method is out of sync with the\n// `CheckpointMetadata` type. In that case, just update `checkpointMetadataKeys` to contain all the keys in\n// `CheckpointMetadata`\nconst validCheckpointMetadataKeys = validateKeys<\n CheckpointMetadata,\n typeof checkpointMetadataKeys\n>(checkpointMetadataKeys);\n\nfunction prepareSql(db: DatabaseType, checkpointId: boolean) {\n const sql = `\n SELECT\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n parent_checkpoint_id,\n type,\n checkpoint,\n metadata,\n (\n SELECT\n json_group_array(\n json_object(\n 'task_id', pw.task_id,\n 'channel', pw.channel,\n 'type', pw.type,\n 'value', CAST(pw.value AS TEXT)\n )\n )\n FROM writes as pw\n WHERE pw.thread_id = checkpoints.thread_id\n AND pw.checkpoint_ns = checkpoints.checkpoint_ns\n AND pw.checkpoint_id = checkpoints.checkpoint_id\n ) as pending_writes,\n (\n SELECT\n json_group_array(\n json_object(\n 'type', ps.type,\n 'value', CAST(ps.value AS TEXT)\n )\n )\n FROM writes as ps\n WHERE ps.thread_id = checkpoints.thread_id\n AND ps.checkpoint_ns = checkpoints.checkpoint_ns\n AND ps.checkpoint_id = checkpoints.parent_checkpoint_id\n AND ps.channel = '${TASKS}'\n ORDER BY ps.idx\n ) as pending_sends\n FROM checkpoints\n WHERE thread_id = ? AND checkpoint_ns = ? ${\n checkpointId\n ? \"AND checkpoint_id = ?\"\n : \"ORDER BY checkpoint_id DESC LIMIT 1\"\n }`;\n\n return db.prepare(sql);\n}\n\nexport class SqliteSaver extends BaseCheckpointSaver {\n db: DatabaseType;\n\n protected isSetup: boolean;\n\n protected withoutCheckpoint: Statement;\n\n protected withCheckpoint: Statement;\n\n constructor(db: DatabaseType, serde?: SerializerProtocol) {\n super(serde);\n this.db = db;\n this.isSetup = false;\n }\n\n static fromConnString(connStringOrLocalPath: string): SqliteSaver {\n return new SqliteSaver(new Database(connStringOrLocalPath));\n }\n\n protected setup(): void {\n if (this.isSetup) {\n return;\n }\n\n this.db.pragma(\"journal_mode=WAL\");\n this.db.exec(`\nCREATE TABLE IF NOT EXISTS checkpoints (\n thread_id TEXT NOT NULL,\n checkpoint_ns TEXT NOT NULL DEFAULT '',\n checkpoint_id TEXT NOT NULL,\n parent_checkpoint_id TEXT,\n type TEXT,\n checkpoint BLOB,\n metadata BLOB,\n PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)\n);`);\n this.db.exec(`\nCREATE TABLE IF NOT EXISTS writes (\n thread_id TEXT NOT NULL,\n checkpoint_ns TEXT NOT NULL DEFAULT '',\n checkpoint_id TEXT NOT NULL,\n task_id TEXT NOT NULL,\n idx INTEGER NOT NULL,\n channel TEXT NOT NULL,\n type TEXT,\n value BLOB,\n PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id, task_id, idx)\n);`);\n\n this.withoutCheckpoint = prepareSql(this.db, false);\n this.withCheckpoint = prepareSql(this.db, true);\n\n this.isSetup = true;\n }\n\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n this.setup();\n const {\n thread_id,\n checkpoint_ns = \"\",\n checkpoint_id,\n } = config.configurable ?? {};\n\n const args = [thread_id, checkpoint_ns];\n if (checkpoint_id) args.push(checkpoint_id);\n\n const stm = checkpoint_id ? this.withCheckpoint : this.withoutCheckpoint;\n const row = stm.get(...args) as CheckpointRow;\n if (row === undefined) return undefined;\n\n let finalConfig = config;\n\n if (!checkpoint_id) {\n finalConfig = {\n configurable: {\n thread_id: row.thread_id,\n checkpoint_ns,\n checkpoint_id: row.checkpoint_id,\n },\n };\n }\n\n if (\n finalConfig.configurable?.thread_id === undefined ||\n finalConfig.configurable?.checkpoint_id === undefined\n ) {\n throw new Error(\"Missing thread_id or checkpoint_id\");\n }\n\n const pendingWrites = await Promise.all(\n (JSON.parse(row.pending_writes) as PendingWriteColumn[]).map(\n async (write) => {\n return [\n write.task_id,\n write.channel,\n await this.serde.loadsTyped(\n write.type ?? \"json\",\n write.value ?? \"\"\n ),\n ] as [string, string, unknown];\n }\n )\n );\n\n const checkpoint = (await this.serde.loadsTyped(\n row.type ?? \"json\",\n row.checkpoint\n )) as Checkpoint;\n\n if (checkpoint.v < 4 && row.parent_checkpoint_id != null) {\n await this.migratePendingSends(\n checkpoint,\n row.thread_id,\n row.parent_checkpoint_id\n );\n }\n\n return {\n checkpoint,\n config: finalConfig,\n metadata: (await this.serde.loadsTyped(\n row.type ?? \"json\",\n row.metadata\n )) as CheckpointMetadata,\n parentConfig: row.parent_checkpoint_id\n ? {\n configurable: {\n thread_id: row.thread_id,\n checkpoint_ns,\n checkpoint_id: row.parent_checkpoint_id,\n },\n }\n : undefined,\n pendingWrites,\n };\n }\n\n async *list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple> {\n const { limit, before, filter } = options ?? {};\n this.setup();\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns;\n let sql = `\n SELECT\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n parent_checkpoint_id,\n type,\n checkpoint,\n metadata,\n (\n SELECT\n json_group_array(\n json_object(\n 'task_id', pw.task_id,\n 'channel', pw.channel,\n 'type', pw.type,\n 'value', CAST(pw.value AS TEXT)\n )\n )\n FROM writes as pw\n WHERE pw.thread_id = checkpoints.thread_id\n AND pw.checkpoint_ns = checkpoints.checkpoint_ns\n AND pw.checkpoint_id = checkpoints.checkpoint_id\n ) as pending_writes,\n (\n SELECT\n json_group_array(\n json_object(\n 'type', ps.type,\n 'value', CAST(ps.value AS TEXT)\n )\n )\n FROM writes as ps\n WHERE ps.thread_id = checkpoints.thread_id\n AND ps.checkpoint_ns = checkpoints.checkpoint_ns\n AND ps.checkpoint_id = checkpoints.parent_checkpoint_id\n AND ps.channel = '${TASKS}'\n ORDER BY ps.idx\n ) as pending_sends\n FROM checkpoints\\n`;\n\n const whereClause: string[] = [];\n\n if (thread_id) {\n whereClause.push(\"thread_id = ?\");\n }\n\n if (checkpoint_ns !== undefined && checkpoint_ns !== null) {\n whereClause.push(\"checkpoint_ns = ?\");\n }\n\n if (before?.configurable?.checkpoint_id !== undefined) {\n whereClause.push(\"checkpoint_id < ?\");\n }\n\n const sanitizedFilter = Object.fromEntries(\n Object.entries(filter ?? {}).filter(\n ([key, value]) =>\n value !== undefined &&\n validCheckpointMetadataKeys.includes(key as keyof CheckpointMetadata)\n )\n );\n\n whereClause.push(\n ...Object.entries(sanitizedFilter).map(\n ([key]) => `jsonb(CAST(metadata AS TEXT))->'$.${key}' = ?`\n )\n );\n\n if (whereClause.length > 0) {\n sql += `WHERE\\n ${whereClause.join(\" AND\\n \")}\\n`;\n }\n\n sql += \"\\nORDER BY checkpoint_id DESC\";\n\n if (limit) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n sql += ` LIMIT ${parseInt(limit as any, 10)}`; // parseInt here (with cast to make TS happy) to sanitize input, as limit may be user-provided\n }\n\n const args = [\n thread_id,\n checkpoint_ns,\n before?.configurable?.checkpoint_id,\n ...Object.values(sanitizedFilter).map((value) => JSON.stringify(value)),\n ].filter((value) => value !== undefined && value !== null);\n\n const rows: CheckpointRow[] = this.db\n .prepare(sql)\n .all(...args) as CheckpointRow[];\n\n if (rows) {\n for (const row of rows) {\n const pendingWrites = await Promise.all(\n (JSON.parse(row.pending_writes) as PendingWriteColumn[]).map(\n async (write) => {\n return [\n write.task_id,\n write.channel,\n await this.serde.loadsTyped(\n write.type ?? \"json\",\n write.value ?? \"\"\n ),\n ] as [string, string, unknown];\n }\n )\n );\n\n const checkpoint = (await this.serde.loadsTyped(\n row.type ?? \"json\",\n row.checkpoint\n )) as Checkpoint;\n\n if (checkpoint.v < 4 && row.parent_checkpoint_id != null) {\n await this.migratePendingSends(\n checkpoint,\n row.thread_id,\n row.parent_checkpoint_id\n );\n }\n\n yield {\n config: {\n configurable: {\n thread_id: row.thread_id,\n checkpoint_ns: row.checkpoint_ns,\n checkpoint_id: row.checkpoint_id,\n },\n },\n checkpoint,\n metadata: (await this.serde.loadsTyped(\n row.type ?? \"json\",\n row.metadata\n )) as CheckpointMetadata,\n parentConfig: row.parent_checkpoint_id\n ? {\n configurable: {\n thread_id: row.thread_id,\n checkpoint_ns: row.checkpoint_ns,\n checkpoint_id: row.parent_checkpoint_id,\n },\n }\n : undefined,\n pendingWrites,\n };\n }\n }\n }\n\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata\n ): Promise<RunnableConfig> {\n this.setup();\n\n if (!config.configurable) {\n throw new Error(\"Empty configuration supplied.\");\n }\n\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns ?? \"\";\n const parent_checkpoint_id = config.configurable?.checkpoint_id;\n\n if (!thread_id) {\n throw new Error(\n `Missing \"thread_id\" field in passed \"config.configurable\".`\n );\n }\n\n const preparedCheckpoint: Partial<Checkpoint> = copyCheckpoint(checkpoint);\n\n const [[type1, serializedCheckpoint], [type2, serializedMetadata]] =\n await Promise.all([\n this.serde.dumpsTyped(preparedCheckpoint),\n this.serde.dumpsTyped(metadata),\n ]);\n\n if (type1 !== type2) {\n throw new Error(\n \"Failed to serialized checkpoint and metadata to the same type.\"\n );\n }\n const row = [\n thread_id,\n checkpoint_ns,\n checkpoint.id,\n parent_checkpoint_id,\n type1,\n serializedCheckpoint,\n serializedMetadata,\n ];\n\n this.db\n .prepare(\n `INSERT OR REPLACE INTO checkpoints (thread_id, checkpoint_ns, checkpoint_id, parent_checkpoint_id, type, checkpoint, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)`\n )\n .run(...row);\n\n return {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: checkpoint.id,\n },\n };\n }\n\n async putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void> {\n this.setup();\n\n if (!config.configurable) {\n throw new Error(\"Empty configuration supplied.\");\n }\n\n if (!config.configurable?.thread_id) {\n throw new Error(\"Missing thread_id field in config.configurable.\");\n }\n\n if (!config.configurable?.checkpoint_id) {\n throw new Error(\"Missing checkpoint_id field in config.configurable.\");\n }\n\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO writes \n (thread_id, checkpoint_ns, checkpoint_id, task_id, idx, channel, type, value) \n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n const transaction = this.db.transaction((rows) => {\n for (const row of rows) {\n stmt.run(...row);\n }\n });\n\n const rows = await Promise.all(\n writes.map(async (write, idx) => {\n const [type, serializedWrite] = await this.serde.dumpsTyped(write[1]);\n return [\n config.configurable?.thread_id,\n config.configurable?.checkpoint_ns,\n config.configurable?.checkpoint_id,\n taskId,\n idx,\n write[0],\n type,\n serializedWrite,\n ];\n })\n );\n\n transaction(rows);\n }\n\n async deleteThread(threadId: string) {\n const transaction = this.db.transaction(() => {\n this.db\n .prepare(`DELETE FROM checkpoints WHERE thread_id = ?`)\n .run(threadId);\n this.db.prepare(`DELETE FROM writes WHERE thread_id = ?`).run(threadId);\n });\n\n transaction();\n }\n\n protected async migratePendingSends(\n checkpoint: Checkpoint,\n threadId: string,\n parentCheckpointId: string\n ) {\n const { pending_sends } = this.db\n .prepare(\n `\n SELECT\n checkpoint_id,\n json_group_array(\n json_object(\n 'type', ps.type,\n 'value', CAST(ps.value AS TEXT)\n )\n ) as pending_sends\n FROM writes as ps\n WHERE ps.thread_id = ?\n AND ps.checkpoint_id = ?\n AND ps.channel = '${TASKS}'\n ORDER BY ps.idx\n `\n )\n .get(threadId, parentCheckpointId) as { pending_sends: string };\n\n const mutableCheckpoint = checkpoint;\n\n // add pending sends to checkpoint\n mutableCheckpoint.channel_values ??= {};\n mutableCheckpoint.channel_values[TASKS] = await Promise.all(\n JSON.parse(pending_sends).map(({ type, value }: PendingSendColumn) =>\n this.serde.loadsTyped(type, value)\n )\n );\n\n // add to versions\n mutableCheckpoint.channel_versions[TASKS] =\n Object.keys(checkpoint.channel_versions).length > 0\n ? maxChannelVersion(...Object.values(checkpoint.channel_versions))\n : this.getNextVersion(undefined);\n }\n}\n"],"mappings":"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"}