@ixo/sqlite-saver 1.0.4

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.
Files changed (35) hide show
  1. package/.eslintrc.js +9 -0
  2. package/.prettierignore +3 -0
  3. package/.prettierrc.js +4 -0
  4. package/.turbo/turbo-build.log +4 -0
  5. package/CHANGELOG.md +25 -0
  6. package/README.md +0 -0
  7. package/dist/index.d.ts +38 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +567 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/migrations/001_add_created_at_to_messages.d.ts +8 -0
  12. package/dist/migrations/001_add_created_at_to_messages.d.ts.map +1 -0
  13. package/dist/migrations/001_add_created_at_to_messages.js +32 -0
  14. package/dist/migrations/001_add_created_at_to_messages.js.map +1 -0
  15. package/dist/tests/agent-with-checkpoiner.test.d.ts +2 -0
  16. package/dist/tests/agent-with-checkpoiner.test.d.ts.map +1 -0
  17. package/dist/tests/agent-with-checkpoiner.test.js +206 -0
  18. package/dist/tests/agent-with-checkpoiner.test.js.map +1 -0
  19. package/dist/tests/checkpointer.test.d.ts +2 -0
  20. package/dist/tests/checkpointer.test.d.ts.map +1 -0
  21. package/dist/tests/checkpointer.test.js +426 -0
  22. package/dist/tests/checkpointer.test.js.map +1 -0
  23. package/dist/utils.d.ts +15 -0
  24. package/dist/utils.d.ts.map +1 -0
  25. package/dist/utils.js +284 -0
  26. package/dist/utils.js.map +1 -0
  27. package/jest.config.js +6 -0
  28. package/package.json +41 -0
  29. package/src/index.ts +929 -0
  30. package/src/migrations/001_add_created_at_to_messages.ts +48 -0
  31. package/src/tests/agent-with-checkpoiner.test.ts +264 -0
  32. package/src/tests/checkpointer.test.ts +628 -0
  33. package/src/utils.ts +358 -0
  34. package/tsconfig.json +11 -0
  35. package/tsconfig.tsbuildinfo +1 -0
package/.eslintrc.js ADDED
@@ -0,0 +1,9 @@
1
+ /** @type {import("eslint").Linter.Config} */
2
+ module.exports = {
3
+ extends: ['@ixo/eslint-config/nest.js'],
4
+ parserOptions: {
5
+ project: 'tsconfig.json',
6
+ tsconfigRootDir: __dirname,
7
+ sourceType: 'module',
8
+ },
9
+ };
@@ -0,0 +1,3 @@
1
+ pnpm-lock.yaml
2
+
3
+ node_modules
package/.prettierrc.js ADDED
@@ -0,0 +1,4 @@
1
+ /** @type {import("prettier").Config} */
2
+ module.exports = {
3
+ ...require('@ixo/eslint-config/prettier-base'),
4
+ };
@@ -0,0 +1,4 @@
1
+
2
+ > @ixo/sqlite-saver@1.0.4 build /home/runner/actions-runner/_work/ixo-oracles-boilerplate/ixo-oracles-boilerplate/packages/sqlite-saver
3
+ > tsc
4
+
package/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ # @ixo/sqlite-saver
2
+
3
+ ## 1.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [`c643779`](https://github.com/ixoworld/companion/commit/c6437794acd28c833074763449502daf61e40a4c) Thanks [@youssefhany-ixo](https://github.com/youssefhany-ixo)! - matrix fix
8
+
9
+ ## 1.0.3
10
+
11
+ ### Patch Changes
12
+
13
+ - [`b5799ee`](https://github.com/ixoworld/companion/commit/b5799ee19a0957ad38e2374ae18e11278295a1ab) Thanks [@youssefhany-ixo](https://github.com/youssefhany-ixo)! - Fix testnet signed mnemonics
14
+
15
+ ## 1.0.2
16
+
17
+ ### Patch Changes
18
+
19
+ - [#84](https://github.com/ixoworld/companion/pull/84) [`3117e8d`](https://github.com/ixoworld/companion/commit/3117e8d2f753811511de4eda8e99b18c3888e083) Thanks [@youssefhany-ixo](https://github.com/youssefhany-ixo)! - update
20
+
21
+ ## 1.0.1
22
+
23
+ ### Patch Changes
24
+
25
+ - [#79](https://github.com/ixoworld/companion/pull/79) [`0fe4fab`](https://github.com/ixoworld/companion/commit/0fe4fabaea19e081cec76e740c2e935a92eae338) Thanks [@youssefhany-ixo](https://github.com/youssefhany-ixo)! - Add Gzip -- optmizeDB by removing unused indexes -- make token limit deduct more in devnet for easy tesing
package/README.md ADDED
File without changes
@@ -0,0 +1,38 @@
1
+ import type { RunnableConfig } from '@langchain/core/runnables';
2
+ import { BaseCheckpointSaver, type Checkpoint, type CheckpointListOptions, type CheckpointMetadata, type CheckpointTuple, type PendingWrite, type SerializerProtocol } from '@langchain/langgraph-checkpoint';
3
+ import { Database as DatabaseType, Statement } from 'better-sqlite3';
4
+ interface Migration {
5
+ version: number;
6
+ name: string;
7
+ up: (db: DatabaseType) => void;
8
+ }
9
+ export declare class SqliteSaver extends BaseCheckpointSaver {
10
+ db: DatabaseType;
11
+ protected isSetup: boolean;
12
+ protected withoutCheckpoint: Statement;
13
+ protected withCheckpoint: Statement;
14
+ protected putCheckpointStmt: Statement;
15
+ protected putWritesStmt: Statement;
16
+ protected deleteCheckpointsStmt: Statement;
17
+ protected putMessageStmt: Statement;
18
+ protected getMessageStmt: Statement;
19
+ protected deleteWritesStmt: Statement;
20
+ constructor(db: DatabaseType, serde?: SerializerProtocol);
21
+ static fromConnString(connStringOrLocalPath: string): SqliteSaver;
22
+ static fromDatabase(db: DatabaseType, serde?: SerializerProtocol): SqliteSaver;
23
+ close(): void;
24
+ protected createSchemaMigrationsTable(): void;
25
+ protected getAppliedMigrations(): number[];
26
+ protected recordMigration(migration: Migration): void;
27
+ protected loadMigrations(): Migration[];
28
+ protected runMigrations(): void;
29
+ protected setup(): void;
30
+ getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;
31
+ list(config: RunnableConfig, options?: CheckpointListOptions): AsyncGenerator<CheckpointTuple>;
32
+ put(config: RunnableConfig, _checkpoint: Checkpoint, metadata: CheckpointMetadata): Promise<RunnableConfig>;
33
+ putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;
34
+ deleteThread(threadId: string): Promise<void>;
35
+ protected migratePendingSends(checkpoint: Checkpoint, threadId: string, parentCheckpointId: string): Promise<void>;
36
+ }
37
+ export {};
38
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EACL,mBAAmB,EACnB,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAGpB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EAExB,MAAM,iCAAiC,CAAC;AACzC,OAAiB,EAAE,QAAQ,IAAI,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAyD/E,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,IAAI,CAAC;CAChC;AA+ED,qBAAa,WAAY,SAAQ,mBAAmB;IAClD,EAAE,EAAE,YAAY,CAAC;IAEjB,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC;IAE3B,SAAS,CAAC,iBAAiB,EAAE,SAAS,CAAC;IAEvC,SAAS,CAAC,cAAc,EAAE,SAAS,CAAC;IAEpC,SAAS,CAAC,iBAAiB,EAAE,SAAS,CAAC;IAEvC,SAAS,CAAC,aAAa,EAAE,SAAS,CAAC;IAEnC,SAAS,CAAC,qBAAqB,EAAE,SAAS,CAAC;IAC3C,SAAS,CAAC,cAAc,EAAE,SAAS,CAAC;IAEpC,SAAS,CAAC,cAAc,EAAE,SAAS,CAAC;IAEpC,SAAS,CAAC,gBAAgB,EAAE,SAAS,CAAC;gBAE1B,EAAE,EAAE,YAAY,EAAE,KAAK,CAAC,EAAE,kBAAkB;IAMxD,MAAM,CAAC,cAAc,CAAC,qBAAqB,EAAE,MAAM,GAAG,WAAW;IAIjE,MAAM,CAAC,YAAY,CAAC,EAAE,EAAE,YAAY,EAAE,KAAK,CAAC,EAAE,kBAAkB,GAAG,WAAW;IAI9E,KAAK,IAAI,IAAI;IASb,SAAS,CAAC,2BAA2B,IAAI,IAAI;IAa7C,SAAS,CAAC,oBAAoB,IAAI,MAAM,EAAE;IAe1C,SAAS,CAAC,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAerD,SAAS,CAAC,cAAc,IAAI,SAAS,EAAE;IAiBvC,SAAS,CAAC,aAAa,IAAI,IAAI;IAoC/B,SAAS,CAAC,KAAK,IAAI,IAAI;IA8GjB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAkGrE,IAAI,CACT,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,cAAc,CAAC,eAAe,CAAC;IAyK5B,GAAG,CACP,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,UAAU,EACvB,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAAC,cAAc,CAAC;IA8GpB,SAAS,CACb,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IA8DV,YAAY,CAAC,QAAQ,EAAE,MAAM;cASnB,mBAAmB,CACjC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,kBAAkB,EAAE,MAAM;CAsC7B"}
package/dist/index.js ADDED
@@ -0,0 +1,567 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SqliteSaver = void 0;
7
+ const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
8
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
9
+ const _001_add_created_at_to_messages_1 = __importDefault(require("./migrations/001_add_created_at_to_messages"));
10
+ const utils_1 = require("./utils");
11
+ const checkpointMetadataKeys = ['source', 'step', 'parents'];
12
+ function validateKeys(keys) {
13
+ return keys;
14
+ }
15
+ const validCheckpointMetadataKeys = validateKeys(checkpointMetadataKeys);
16
+ function prepareSql(db, checkpointId) {
17
+ const sql = `
18
+ SELECT
19
+ thread_id,
20
+ checkpoint_ns,
21
+ checkpoint_id,
22
+ parent_checkpoint_id,
23
+ type,
24
+ checkpoint,
25
+ metadata,
26
+ (
27
+ SELECT
28
+ json_group_array(
29
+ json_object(
30
+ 'task_id', pw.task_id,
31
+ 'channel', pw.channel,
32
+ 'type', pw.type,
33
+ 'value', CAST(pw.value AS TEXT)
34
+ )
35
+ )
36
+ FROM writes as pw
37
+ WHERE pw.thread_id = checkpoints.thread_id
38
+ AND pw.checkpoint_ns = checkpoints.checkpoint_ns
39
+ AND pw.checkpoint_id = checkpoints.checkpoint_id
40
+ ) as pending_writes,
41
+ (
42
+ SELECT
43
+ json_group_array(
44
+ json_object(
45
+ 'type', ps.type,
46
+ 'value', CAST(ps.value AS TEXT)
47
+ )
48
+ )
49
+ FROM writes as ps
50
+ WHERE ps.thread_id = checkpoints.thread_id
51
+ AND ps.checkpoint_ns = checkpoints.checkpoint_ns
52
+ AND ps.checkpoint_id = checkpoints.parent_checkpoint_id
53
+ AND ps.channel = '${langgraph_checkpoint_1.TASKS}'
54
+ ORDER BY ps.idx
55
+ ) as pending_sends
56
+ FROM checkpoints
57
+ WHERE thread_id = ? AND checkpoint_ns = ? ${checkpointId
58
+ ? 'AND checkpoint_id = ?'
59
+ : 'ORDER BY checkpoint_id DESC LIMIT 1'}`;
60
+ return db.prepare(sql);
61
+ }
62
+ class SqliteSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
63
+ db;
64
+ isSetup;
65
+ withoutCheckpoint;
66
+ withCheckpoint;
67
+ putCheckpointStmt;
68
+ putWritesStmt;
69
+ deleteCheckpointsStmt;
70
+ putMessageStmt;
71
+ getMessageStmt;
72
+ deleteWritesStmt;
73
+ constructor(db, serde) {
74
+ super(serde);
75
+ this.db = db;
76
+ this.isSetup = false;
77
+ }
78
+ static fromConnString(connStringOrLocalPath) {
79
+ return new SqliteSaver(new better_sqlite3_1.default(connStringOrLocalPath));
80
+ }
81
+ static fromDatabase(db, serde) {
82
+ return new SqliteSaver(db, serde);
83
+ }
84
+ close() {
85
+ if (this.db.open) {
86
+ this.db.close();
87
+ }
88
+ }
89
+ createSchemaMigrationsTable() {
90
+ this.db.exec(`
91
+ CREATE TABLE IF NOT EXISTS schema_migrations (
92
+ version INTEGER NOT NULL PRIMARY KEY,
93
+ name TEXT NOT NULL,
94
+ applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
95
+ );
96
+ `);
97
+ }
98
+ getAppliedMigrations() {
99
+ try {
100
+ const rows = this.db
101
+ .prepare('SELECT version FROM schema_migrations ORDER BY version')
102
+ .all();
103
+ return rows.map((row) => row.version);
104
+ }
105
+ catch {
106
+ return [];
107
+ }
108
+ }
109
+ recordMigration(migration) {
110
+ this.db
111
+ .prepare('INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, CURRENT_TIMESTAMP)')
112
+ .run(migration.version, migration.name);
113
+ }
114
+ loadMigrations() {
115
+ const migrations = [
116
+ _001_add_created_at_to_messages_1.default,
117
+ ];
118
+ migrations.sort((a, b) => a.version - b.version);
119
+ return migrations;
120
+ }
121
+ runMigrations() {
122
+ this.createSchemaMigrationsTable();
123
+ const appliedVersions = this.getAppliedMigrations();
124
+ const allMigrations = this.loadMigrations();
125
+ const pendingMigrations = allMigrations.filter((migration) => !appliedVersions.includes(migration.version));
126
+ if (pendingMigrations.length === 0) {
127
+ return;
128
+ }
129
+ console.log(`Running ${pendingMigrations.length} pending migration(s)...`);
130
+ for (const migration of pendingMigrations) {
131
+ try {
132
+ console.log(`Applying migration ${migration.version}: ${migration.name}`);
133
+ migration.up(this.db);
134
+ this.recordMigration(migration);
135
+ console.log(`✓ Migration ${migration.version}: ${migration.name} applied successfully`);
136
+ }
137
+ catch (error) {
138
+ console.error(`✗ Failed to apply migration ${migration.version}: ${migration.name}`, error);
139
+ throw error;
140
+ }
141
+ }
142
+ }
143
+ setup() {
144
+ if (this.isSetup) {
145
+ return;
146
+ }
147
+ this.db.pragma('journal_mode = WAL');
148
+ this.db.pragma('busy_timeout = 5000');
149
+ this.db.exec(`
150
+ CREATE TABLE IF NOT EXISTS checkpoints (
151
+ thread_id TEXT NOT NULL,
152
+ checkpoint_ns TEXT NOT NULL DEFAULT '',
153
+ checkpoint_id TEXT NOT NULL,
154
+ parent_checkpoint_id TEXT,
155
+ type TEXT,
156
+ checkpoint BLOB,
157
+ metadata BLOB,
158
+ PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
159
+ );`);
160
+ this.db.exec(`
161
+ CREATE TABLE IF NOT EXISTS writes (
162
+ thread_id TEXT NOT NULL,
163
+ checkpoint_ns TEXT NOT NULL DEFAULT '',
164
+ checkpoint_id TEXT NOT NULL,
165
+ task_id TEXT NOT NULL,
166
+ idx INTEGER NOT NULL,
167
+ channel TEXT NOT NULL,
168
+ type TEXT,
169
+ value BLOB,
170
+ PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id, task_id, idx)
171
+ );`);
172
+ this.db.exec(`
173
+ CREATE TABLE IF NOT EXISTS messages (
174
+ thread_id TEXT NOT NULL,
175
+ checkpoint_ns TEXT NOT NULL DEFAULT '',
176
+ checkpoint_id TEXT NOT NULL,
177
+ message_id TEXT NOT NULL,
178
+ message_type TEXT NOT NULL,
179
+ message_content TEXT NOT NULL,
180
+ message BLOB,
181
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
182
+ PRIMARY KEY (message_id)
183
+ );
184
+ `);
185
+ this.runMigrations();
186
+ this.db.exec(`
187
+ CREATE INDEX IF NOT EXISTS idx_messages_thread_id
188
+ ON messages(thread_id);
189
+ `);
190
+ this.db.exec(`
191
+ CREATE INDEX IF NOT EXISTS idx_messages_checkpoint_id
192
+ ON messages(checkpoint_id);
193
+ `);
194
+ this.db.exec(`
195
+ CREATE INDEX IF NOT EXISTS idx_messages_lookup
196
+ ON messages(thread_id, checkpoint_ns, checkpoint_id);
197
+ `);
198
+ this.db.exec(`
199
+ CREATE INDEX IF NOT EXISTS idx_messages_thread_created
200
+ ON messages(thread_id, created_at);
201
+ `);
202
+ this.db.exec(`
203
+ CREATE INDEX IF NOT EXISTS idx_writes_channel
204
+ ON writes(thread_id, checkpoint_id, channel);
205
+ `);
206
+ this.withoutCheckpoint = prepareSql(this.db, false);
207
+ this.withCheckpoint = prepareSql(this.db, true);
208
+ this.putCheckpointStmt = this.db.prepare(`INSERT OR REPLACE INTO checkpoints (thread_id, checkpoint_ns, checkpoint_id, parent_checkpoint_id, type, checkpoint, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)`);
209
+ this.putWritesStmt = this.db.prepare(`
210
+ INSERT OR REPLACE INTO writes
211
+ (thread_id, checkpoint_ns, checkpoint_id, task_id, idx, channel, type, value)
212
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
213
+ `);
214
+ this.deleteCheckpointsStmt = this.db.prepare(`DELETE FROM checkpoints WHERE thread_id = ?`);
215
+ this.deleteWritesStmt = this.db.prepare(`DELETE FROM writes WHERE thread_id = ?`);
216
+ this.putMessageStmt = this.db.prepare(`INSERT OR REPLACE INTO messages (thread_id, checkpoint_ns, checkpoint_id, message_id, message_type, message_content, message, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
217
+ this.getMessageStmt = this.db.prepare(`SELECT * FROM messages WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id = ?`);
218
+ this.isSetup = true;
219
+ }
220
+ async getTuple(config) {
221
+ this.setup();
222
+ const { thread_id, checkpoint_ns = '', checkpoint_id, } = config.configurable ?? {};
223
+ const args = [thread_id, checkpoint_ns];
224
+ if (checkpoint_id)
225
+ args.push(checkpoint_id);
226
+ const stm = checkpoint_id ? this.withCheckpoint : this.withoutCheckpoint;
227
+ const row = stm.get(...args);
228
+ if (row === undefined)
229
+ return undefined;
230
+ let finalConfig = config;
231
+ if (!checkpoint_id) {
232
+ finalConfig = {
233
+ configurable: {
234
+ thread_id: row.thread_id,
235
+ checkpoint_ns,
236
+ checkpoint_id: row.checkpoint_id,
237
+ },
238
+ };
239
+ }
240
+ if (finalConfig.configurable?.thread_id === undefined ||
241
+ finalConfig.configurable?.checkpoint_id === undefined) {
242
+ throw new Error('Missing thread_id or checkpoint_id');
243
+ }
244
+ const messages = this.getMessageStmt.all(finalConfig.configurable?.thread_id, finalConfig.configurable?.checkpoint_ns, finalConfig.configurable?.checkpoint_id);
245
+ const pendingWrites = await Promise.all(JSON.parse(row.pending_writes).map(async (write) => {
246
+ return [
247
+ write.task_id,
248
+ write.channel,
249
+ await this.serde.loadsTyped(write.type ?? 'json', write.value ?? ''),
250
+ ];
251
+ }));
252
+ const parsedMessages = await Promise.all(messages.map(async (message) => {
253
+ return this.serde.loadsTyped('json', message.message);
254
+ }));
255
+ const checkpoint = (await this.serde.loadsTyped(row.type ?? 'json', row.checkpoint));
256
+ if (parsedMessages.length > 0) {
257
+ checkpoint.channel_values.messages = parsedMessages;
258
+ }
259
+ if (checkpoint.v < 4 && row.parent_checkpoint_id != null) {
260
+ await this.migratePendingSends(checkpoint, row.thread_id, row.parent_checkpoint_id);
261
+ }
262
+ return {
263
+ checkpoint,
264
+ config: finalConfig,
265
+ metadata: (await this.serde.loadsTyped(row.type ?? 'json', row.metadata)),
266
+ parentConfig: row.parent_checkpoint_id
267
+ ? {
268
+ configurable: {
269
+ thread_id: row.thread_id,
270
+ checkpoint_ns,
271
+ checkpoint_id: row.parent_checkpoint_id,
272
+ },
273
+ }
274
+ : undefined,
275
+ pendingWrites,
276
+ };
277
+ }
278
+ async *list(config, options) {
279
+ const { limit, before, filter } = options ?? {};
280
+ this.setup();
281
+ const thread_id = config.configurable?.thread_id;
282
+ const checkpoint_ns = config.configurable?.checkpoint_ns;
283
+ let sql = `
284
+ SELECT
285
+ thread_id,
286
+ checkpoint_ns,
287
+ checkpoint_id,
288
+ parent_checkpoint_id,
289
+ type,
290
+ checkpoint,
291
+ metadata,
292
+ (
293
+ SELECT
294
+ json_group_array(
295
+ json_object(
296
+ 'task_id', pw.task_id,
297
+ 'channel', pw.channel,
298
+ 'type', pw.type,
299
+ 'value', CAST(pw.value AS TEXT)
300
+ )
301
+ )
302
+ FROM writes as pw
303
+ WHERE pw.thread_id = checkpoints.thread_id
304
+ AND pw.checkpoint_ns = checkpoints.checkpoint_ns
305
+ AND pw.checkpoint_id = checkpoints.checkpoint_id
306
+ ) as pending_writes,
307
+ (
308
+ SELECT
309
+ json_group_array(
310
+ json_object(
311
+ 'type', ps.type,
312
+ 'value', CAST(ps.value AS TEXT)
313
+ )
314
+ )
315
+ FROM writes as ps
316
+ WHERE ps.thread_id = checkpoints.thread_id
317
+ AND ps.checkpoint_ns = checkpoints.checkpoint_ns
318
+ AND ps.checkpoint_id = checkpoints.parent_checkpoint_id
319
+ AND ps.channel = '${langgraph_checkpoint_1.TASKS}'
320
+ ORDER BY ps.idx
321
+ ) as pending_sends
322
+ FROM checkpoints\n`;
323
+ const whereClause = [];
324
+ if (thread_id) {
325
+ whereClause.push('thread_id = ?');
326
+ }
327
+ if (checkpoint_ns !== undefined && checkpoint_ns !== null) {
328
+ whereClause.push('checkpoint_ns = ?');
329
+ }
330
+ if (before?.configurable?.checkpoint_id !== undefined) {
331
+ whereClause.push('checkpoint_id < ?');
332
+ }
333
+ const sanitizedFilter = Object.fromEntries(Object.entries(filter ?? {}).filter(([key, value]) => value !== undefined &&
334
+ validCheckpointMetadataKeys.includes(key)));
335
+ whereClause.push(...Object.entries(sanitizedFilter).map(([key]) => `jsonb(CAST(metadata AS TEXT))->'$.${key}' = ?`));
336
+ if (whereClause.length > 0) {
337
+ sql += `WHERE\n ${whereClause.join(' AND\n ')}\n`;
338
+ }
339
+ sql += '\nORDER BY checkpoint_id DESC';
340
+ if (limit) {
341
+ sql += ` LIMIT ${parseInt(limit, 10)}`;
342
+ }
343
+ const args = [
344
+ thread_id,
345
+ checkpoint_ns,
346
+ before?.configurable?.checkpoint_id,
347
+ ...Object.values(sanitizedFilter).map((value) => JSON.stringify(value)),
348
+ ].filter((value) => value !== undefined && value !== null);
349
+ const rows = this.db
350
+ .prepare(sql)
351
+ .all(...args);
352
+ if (rows) {
353
+ for (const row of rows) {
354
+ const pendingWrites = await Promise.all(JSON.parse(row.pending_writes).map(async (write) => {
355
+ return [
356
+ write.task_id,
357
+ write.channel,
358
+ await this.serde.loadsTyped(write.type ?? 'json', write.value ?? ''),
359
+ ];
360
+ }));
361
+ const messages = this.getMessageStmt.all(row.thread_id, row.checkpoint_ns, row.checkpoint_id);
362
+ const parsedMessages = await Promise.all(messages.map(async (message) => {
363
+ return this.serde.loadsTyped('json', message.message);
364
+ }));
365
+ const checkpoint = (await this.serde.loadsTyped(row.type ?? 'json', row.checkpoint));
366
+ if (parsedMessages.length > 0) {
367
+ checkpoint.channel_values.messages = parsedMessages;
368
+ }
369
+ if (checkpoint.v < 4 && row.parent_checkpoint_id != null) {
370
+ await this.migratePendingSends(checkpoint, row.thread_id, row.parent_checkpoint_id);
371
+ }
372
+ yield {
373
+ config: {
374
+ configurable: {
375
+ thread_id: row.thread_id,
376
+ checkpoint_ns: row.checkpoint_ns,
377
+ checkpoint_id: row.checkpoint_id,
378
+ },
379
+ },
380
+ checkpoint,
381
+ metadata: (await this.serde.loadsTyped(row.type ?? 'json', row.metadata)),
382
+ parentConfig: row.parent_checkpoint_id
383
+ ? {
384
+ configurable: {
385
+ thread_id: row.thread_id,
386
+ checkpoint_ns: row.checkpoint_ns,
387
+ checkpoint_id: row.parent_checkpoint_id,
388
+ },
389
+ }
390
+ : undefined,
391
+ pendingWrites,
392
+ };
393
+ }
394
+ }
395
+ }
396
+ async put(config, _checkpoint, metadata) {
397
+ this.setup();
398
+ if (!config.configurable) {
399
+ throw new Error('Empty configuration supplied.');
400
+ }
401
+ const thread_id = config.configurable?.thread_id;
402
+ const checkpoint_ns = config.configurable?.checkpoint_ns ?? '';
403
+ const parent_checkpoint_id = config.configurable?.checkpoint_id;
404
+ if (!thread_id) {
405
+ throw new Error(`Missing "thread_id" field in passed "config.configurable".`);
406
+ }
407
+ const { checkpoint, messages } = removeMessagesFromCheckpoint(_checkpoint);
408
+ const [[type1, serializedCheckpoint], [type2, serializedMetadata]] = await Promise.all([
409
+ this.serde.dumpsTyped(checkpoint),
410
+ this.serde.dumpsTyped(metadata),
411
+ ]);
412
+ if (type1 !== type2) {
413
+ throw new Error('Failed to serialized checkpoint and metadata to the same type.');
414
+ }
415
+ const row = [
416
+ thread_id,
417
+ checkpoint_ns,
418
+ checkpoint.id,
419
+ parent_checkpoint_id,
420
+ type1,
421
+ serializedCheckpoint,
422
+ serializedMetadata,
423
+ ];
424
+ const transaction = this.db.transaction(() => {
425
+ this.putCheckpointStmt.run(...row);
426
+ if (messages) {
427
+ for (const message of messages) {
428
+ const encoder = new TextEncoder();
429
+ const msgFromMatrixRoom = message.additional_kwargs
430
+ .msgFromMatrixRoom;
431
+ const _additionalKwargs = message.additional_kwargs;
432
+ const cleanedAdditionalKwargs = (0, utils_1.cleanAdditionalKwargs)(message.additional_kwargs, msgFromMatrixRoom ?? false);
433
+ message.additional_kwargs = {
434
+ ...cleanedAdditionalKwargs,
435
+ reasoning: cleanedAdditionalKwargs.reasoning ?? _additionalKwargs.reasoning,
436
+ reasoningDetails: cleanedAdditionalKwargs.reasoningDetails ??
437
+ _additionalKwargs.reasoningDetails,
438
+ };
439
+ if (message.type !== 'ai') {
440
+ delete message.additional_kwargs.reasoning;
441
+ delete message.additional_kwargs.reasoningDetails;
442
+ }
443
+ const serializedMessage = encoder.encode((0, utils_1.stringify)(message, (_, value) => {
444
+ return (0, utils_1._default)(value);
445
+ }));
446
+ const messageRow = {
447
+ thread_id,
448
+ checkpoint_ns,
449
+ checkpoint_id: checkpoint.id,
450
+ message_id: message.id ?? message.lc_kwargs?.id,
451
+ message_type: message.type,
452
+ message_content: message.content.toString(),
453
+ message: serializedMessage,
454
+ created_at: message.additional_kwargs?.timestamp ??
455
+ new Date().toISOString(),
456
+ };
457
+ this.putMessageStmt.run(messageRow.thread_id, messageRow.checkpoint_ns, messageRow.checkpoint_id, messageRow.message_id, messageRow.message_type, messageRow.message_content, messageRow.message, messageRow.created_at);
458
+ }
459
+ }
460
+ });
461
+ transaction();
462
+ return {
463
+ configurable: {
464
+ thread_id,
465
+ checkpoint_ns,
466
+ checkpoint_id: checkpoint.id,
467
+ },
468
+ };
469
+ }
470
+ async putWrites(config, writes, taskId) {
471
+ this.setup();
472
+ if (!config.configurable) {
473
+ throw new Error('Empty configuration supplied.');
474
+ }
475
+ if (!config.configurable?.thread_id) {
476
+ console.error('Missing thread_id field in config.configurable.', {
477
+ configurable: config.configurable,
478
+ });
479
+ const threadId = this.db
480
+ .prepare('SELECT thread_id FROM checkpoints WHERE checkpoint_id = ?')
481
+ .get(config.configurable?.checkpoint_id);
482
+ if (!threadId) {
483
+ throw new Error('Missing thread_id field in config.configurable. config: ' +
484
+ JSON.stringify(config.configurable));
485
+ }
486
+ config.configurable.thread_id = threadId.thread_id;
487
+ }
488
+ if (!config.configurable?.checkpoint_id) {
489
+ console.error('Missing checkpoint_id field in config.configurable.', {
490
+ configurable: config.configurable,
491
+ });
492
+ throw new Error('Missing checkpoint_id field in config.configurable. config: ' +
493
+ JSON.stringify(config.configurable));
494
+ }
495
+ const transaction = this.db.transaction((rows) => {
496
+ for (const row of rows) {
497
+ this.putWritesStmt.run(...row);
498
+ }
499
+ });
500
+ const rows = await Promise.all(writes.map(async (write, idx) => {
501
+ const [type, serializedWrite] = await this.serde.dumpsTyped(write[1]);
502
+ return [
503
+ config.configurable?.thread_id,
504
+ config.configurable?.checkpoint_ns,
505
+ config.configurable?.checkpoint_id,
506
+ taskId,
507
+ idx,
508
+ write[0],
509
+ type,
510
+ serializedWrite,
511
+ ];
512
+ }));
513
+ transaction(rows);
514
+ }
515
+ async deleteThread(threadId) {
516
+ const transaction = this.db.transaction(() => {
517
+ this.deleteCheckpointsStmt.run(threadId);
518
+ this.deleteWritesStmt.run(threadId);
519
+ });
520
+ transaction();
521
+ }
522
+ async migratePendingSends(checkpoint, threadId, parentCheckpointId) {
523
+ const { pending_sends } = this.db
524
+ .prepare(`
525
+ SELECT
526
+ checkpoint_id,
527
+ json_group_array(
528
+ json_object(
529
+ 'type', ps.type,
530
+ 'value', CAST(ps.value AS TEXT)
531
+ )
532
+ ) as pending_sends
533
+ FROM writes as ps
534
+ WHERE ps.thread_id = ?
535
+ AND ps.checkpoint_id = ?
536
+ AND ps.channel = '${langgraph_checkpoint_1.TASKS}'
537
+ ORDER BY ps.idx
538
+ `)
539
+ .get(threadId, parentCheckpointId);
540
+ const mutableCheckpoint = checkpoint;
541
+ mutableCheckpoint.channel_values ??= {};
542
+ mutableCheckpoint.channel_values[langgraph_checkpoint_1.TASKS] = await Promise.all(JSON.parse(pending_sends).map(({ type, value }) => this.serde.loadsTyped(type, value)));
543
+ mutableCheckpoint.channel_versions[langgraph_checkpoint_1.TASKS] =
544
+ Object.keys(checkpoint.channel_versions).length > 0
545
+ ? (0, langgraph_checkpoint_1.maxChannelVersion)(...Object.values(checkpoint.channel_versions))
546
+ : this.getNextVersion(undefined);
547
+ }
548
+ }
549
+ exports.SqliteSaver = SqliteSaver;
550
+ const isCheckpointWithMessages = (checkpoint) => {
551
+ return 'messages' in checkpoint.channel_values;
552
+ };
553
+ const removeMessagesFromCheckpoint = (checkpoint) => {
554
+ if (isCheckpointWithMessages(checkpoint)) {
555
+ const newCheckpoint = (0, langgraph_checkpoint_1.copyCheckpoint)(checkpoint);
556
+ delete newCheckpoint.channel_values.messages;
557
+ return {
558
+ checkpoint: newCheckpoint,
559
+ messages: checkpoint.channel_values.messages,
560
+ };
561
+ }
562
+ return {
563
+ checkpoint,
564
+ messages: undefined,
565
+ };
566
+ };
567
+ //# sourceMappingURL=index.js.map