@huyooo/ai-chat-storage 0.1.0

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.
@@ -0,0 +1,678 @@
1
+ // src/adapters/sqlite/index.ts
2
+ import { drizzle } from "drizzle-orm/better-sqlite3";
3
+ import Database from "better-sqlite3";
4
+
5
+ // src/schema.ts
6
+ import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
7
+ var sessions = sqliteTable("sessions", {
8
+ id: text("id").primaryKey(),
9
+ appId: text("app_id"),
10
+ userId: text("user_id"),
11
+ title: text("title").notNull(),
12
+ model: text("model").notNull(),
13
+ mode: text("mode").notNull(),
14
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
15
+ updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
16
+ }, (table) => [
17
+ index("idx_sessions_tenant").on(table.appId, table.userId),
18
+ index("idx_sessions_updated").on(table.updatedAt)
19
+ ]);
20
+ var messages = sqliteTable("messages", {
21
+ id: text("id").primaryKey(),
22
+ sessionId: text("session_id").notNull(),
23
+ appId: text("app_id"),
24
+ userId: text("user_id"),
25
+ role: text("role").notNull(),
26
+ content: text("content").notNull(),
27
+ thinking: text("thinking"),
28
+ toolCalls: text("tool_calls"),
29
+ searchResults: text("search_results"),
30
+ operationIds: text("operation_ids"),
31
+ timestamp: integer("timestamp", { mode: "timestamp" }).notNull()
32
+ }, (table) => [
33
+ index("idx_messages_session").on(table.sessionId),
34
+ index("idx_messages_tenant").on(table.appId, table.userId)
35
+ ]);
36
+ var operations = sqliteTable("operations", {
37
+ id: text("id").primaryKey(),
38
+ sessionId: text("session_id").notNull(),
39
+ messageId: text("message_id"),
40
+ appId: text("app_id"),
41
+ userId: text("user_id"),
42
+ command: text("command").notNull(),
43
+ operationType: text("operation_type").notNull(),
44
+ affectedFiles: text("affected_files").notNull(),
45
+ backupPath: text("backup_path"),
46
+ status: text("status").notNull().default("pending"),
47
+ errorMessage: text("error_message"),
48
+ timestamp: integer("timestamp", { mode: "timestamp" }).notNull()
49
+ }, (table) => [
50
+ index("idx_operations_session").on(table.sessionId),
51
+ index("idx_operations_message").on(table.messageId),
52
+ index("idx_operations_tenant").on(table.appId, table.userId)
53
+ ]);
54
+ var backups = sqliteTable("backups", {
55
+ id: text("id").primaryKey(),
56
+ operationId: text("operation_id").notNull(),
57
+ originalPath: text("original_path").notNull(),
58
+ backupPath: text("backup_path").notNull(),
59
+ fileSize: integer("file_size").notNull(),
60
+ fileHash: text("file_hash").notNull(),
61
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
62
+ expiresAt: integer("expires_at", { mode: "timestamp" }).notNull()
63
+ }, (table) => [
64
+ index("idx_backups_operation").on(table.operationId),
65
+ index("idx_backups_expires").on(table.expiresAt)
66
+ ]);
67
+ var trash = sqliteTable("trash", {
68
+ id: text("id").primaryKey(),
69
+ sessionId: text("session_id").notNull(),
70
+ appId: text("app_id"),
71
+ userId: text("user_id"),
72
+ originalPath: text("original_path").notNull(),
73
+ trashPath: text("trash_path").notNull(),
74
+ deletedAt: integer("deleted_at", { mode: "timestamp" }).notNull(),
75
+ autoDeleteAt: integer("auto_delete_at", { mode: "timestamp" }).notNull()
76
+ }, (table) => [
77
+ index("idx_trash_tenant").on(table.appId, table.userId),
78
+ index("idx_trash_auto_delete").on(table.autoDeleteAt)
79
+ ]);
80
+ var embeddings = sqliteTable("embeddings", {
81
+ id: text("id").primaryKey(),
82
+ sessionId: text("session_id").notNull(),
83
+ messageId: text("message_id"),
84
+ appId: text("app_id"),
85
+ userId: text("user_id"),
86
+ content: text("content").notNull(),
87
+ contentType: text("content_type").notNull(),
88
+ embedding: text("embedding"),
89
+ // JSON 字符串存储
90
+ metadata: text("metadata"),
91
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull()
92
+ }, (table) => [
93
+ index("idx_embeddings_session").on(table.sessionId),
94
+ index("idx_embeddings_tenant").on(table.appId, table.userId)
95
+ ]);
96
+
97
+ // src/adapters/sqlite/base.ts
98
+ import { eq, and } from "drizzle-orm";
99
+ function initSchema(sqlite) {
100
+ sqlite.exec(`
101
+ CREATE TABLE IF NOT EXISTS sessions (
102
+ id TEXT PRIMARY KEY,
103
+ app_id TEXT,
104
+ user_id TEXT,
105
+ title TEXT NOT NULL,
106
+ model TEXT NOT NULL,
107
+ mode TEXT NOT NULL,
108
+ created_at INTEGER NOT NULL,
109
+ updated_at INTEGER NOT NULL
110
+ );
111
+
112
+ CREATE TABLE IF NOT EXISTS messages (
113
+ id TEXT PRIMARY KEY,
114
+ session_id TEXT NOT NULL,
115
+ app_id TEXT,
116
+ user_id TEXT,
117
+ role TEXT NOT NULL,
118
+ content TEXT NOT NULL,
119
+ thinking TEXT,
120
+ tool_calls TEXT,
121
+ search_results TEXT,
122
+ operation_ids TEXT,
123
+ timestamp INTEGER NOT NULL
124
+ );
125
+
126
+ CREATE TABLE IF NOT EXISTS operations (
127
+ id TEXT PRIMARY KEY,
128
+ session_id TEXT NOT NULL,
129
+ message_id TEXT,
130
+ app_id TEXT,
131
+ user_id TEXT,
132
+ command TEXT NOT NULL,
133
+ operation_type TEXT NOT NULL,
134
+ affected_files TEXT NOT NULL,
135
+ backup_path TEXT,
136
+ status TEXT NOT NULL DEFAULT 'pending',
137
+ error_message TEXT,
138
+ timestamp INTEGER NOT NULL
139
+ );
140
+
141
+ CREATE TABLE IF NOT EXISTS backups (
142
+ id TEXT PRIMARY KEY,
143
+ operation_id TEXT NOT NULL,
144
+ original_path TEXT NOT NULL,
145
+ backup_path TEXT NOT NULL,
146
+ file_size INTEGER NOT NULL,
147
+ file_hash TEXT NOT NULL,
148
+ created_at INTEGER NOT NULL,
149
+ expires_at INTEGER NOT NULL
150
+ );
151
+
152
+ CREATE TABLE IF NOT EXISTS trash (
153
+ id TEXT PRIMARY KEY,
154
+ session_id TEXT NOT NULL,
155
+ app_id TEXT,
156
+ user_id TEXT,
157
+ original_path TEXT NOT NULL,
158
+ trash_path TEXT NOT NULL,
159
+ deleted_at INTEGER NOT NULL,
160
+ auto_delete_at INTEGER NOT NULL
161
+ );
162
+
163
+ CREATE TABLE IF NOT EXISTS embeddings (
164
+ id TEXT PRIMARY KEY,
165
+ session_id TEXT NOT NULL,
166
+ message_id TEXT,
167
+ app_id TEXT,
168
+ user_id TEXT,
169
+ content TEXT NOT NULL,
170
+ content_type TEXT NOT NULL,
171
+ embedding TEXT,
172
+ metadata TEXT,
173
+ created_at INTEGER NOT NULL
174
+ );
175
+
176
+ -- \u7D22\u5F15
177
+ CREATE INDEX IF NOT EXISTS idx_sessions_tenant ON sessions(app_id, user_id);
178
+ CREATE INDEX IF NOT EXISTS idx_sessions_updated ON sessions(updated_at);
179
+ CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);
180
+ CREATE INDEX IF NOT EXISTS idx_messages_tenant ON messages(app_id, user_id);
181
+ CREATE INDEX IF NOT EXISTS idx_operations_session ON operations(session_id);
182
+ CREATE INDEX IF NOT EXISTS idx_operations_message ON operations(message_id);
183
+ CREATE INDEX IF NOT EXISTS idx_backups_operation ON backups(operation_id);
184
+ CREATE INDEX IF NOT EXISTS idx_backups_expires ON backups(expires_at);
185
+ CREATE INDEX IF NOT EXISTS idx_trash_tenant ON trash(app_id, user_id);
186
+ CREATE INDEX IF NOT EXISTS idx_trash_auto_delete ON trash(auto_delete_at);
187
+ CREATE INDEX IF NOT EXISTS idx_embeddings_session ON embeddings(session_id);
188
+ `);
189
+ }
190
+ function buildSessionsTenantCondition(ctx) {
191
+ const conditions = [];
192
+ if (ctx.appId) conditions.push(eq(sessions.appId, ctx.appId));
193
+ if (ctx.userId) conditions.push(eq(sessions.userId, ctx.userId));
194
+ return conditions.length > 0 ? and(...conditions) : void 0;
195
+ }
196
+ function buildMessagesTenantCondition(ctx) {
197
+ const conditions = [];
198
+ if (ctx.appId) conditions.push(eq(messages.appId, ctx.appId));
199
+ if (ctx.userId) conditions.push(eq(messages.userId, ctx.userId));
200
+ return conditions.length > 0 ? and(...conditions) : void 0;
201
+ }
202
+ function buildOperationsTenantCondition(ctx) {
203
+ const conditions = [];
204
+ if (ctx.appId) conditions.push(eq(operations.appId, ctx.appId));
205
+ if (ctx.userId) conditions.push(eq(operations.userId, ctx.userId));
206
+ return conditions.length > 0 ? and(...conditions) : void 0;
207
+ }
208
+ function buildTrashTenantCondition(ctx) {
209
+ const conditions = [];
210
+ if (ctx.appId) conditions.push(eq(trash.appId, ctx.appId));
211
+ if (ctx.userId) conditions.push(eq(trash.userId, ctx.userId));
212
+ return conditions.length > 0 ? and(...conditions) : void 0;
213
+ }
214
+ function cosineSimilarity(a, b) {
215
+ if (a.length !== b.length) return 0;
216
+ let dotProduct = 0;
217
+ let normA = 0;
218
+ let normB = 0;
219
+ for (let i = 0; i < a.length; i++) {
220
+ dotProduct += a[i] * b[i];
221
+ normA += a[i] * a[i];
222
+ normB += b[i] * b[i];
223
+ }
224
+ if (normA === 0 || normB === 0) return 0;
225
+ return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
226
+ }
227
+
228
+ // src/adapters/sqlite/sessions.ts
229
+ import { eq as eq2, and as and2, desc } from "drizzle-orm";
230
+ function toSessionRecord(row) {
231
+ return {
232
+ id: row.id,
233
+ appId: row.appId,
234
+ userId: row.userId,
235
+ title: row.title,
236
+ model: row.model,
237
+ mode: row.mode,
238
+ createdAt: row.createdAt,
239
+ updatedAt: row.updatedAt
240
+ };
241
+ }
242
+ function getSessions(db, ctx) {
243
+ const tenantCondition = buildSessionsTenantCondition(ctx);
244
+ const rows = db.select().from(sessions).where(tenantCondition).orderBy(desc(sessions.updatedAt)).all();
245
+ return rows.map(toSessionRecord);
246
+ }
247
+ function getSession(db, id, ctx) {
248
+ const tenantCondition = buildSessionsTenantCondition(ctx);
249
+ const condition = tenantCondition ? and2(eq2(sessions.id, id), tenantCondition) : eq2(sessions.id, id);
250
+ const row = db.select().from(sessions).where(condition).get();
251
+ return row ? toSessionRecord(row) : null;
252
+ }
253
+ function createSession(db, input, ctx) {
254
+ const now = /* @__PURE__ */ new Date();
255
+ const record = {
256
+ id: input.id,
257
+ appId: ctx.appId || null,
258
+ userId: ctx.userId || null,
259
+ title: input.title,
260
+ model: input.model,
261
+ mode: input.mode,
262
+ createdAt: now,
263
+ updatedAt: now
264
+ };
265
+ db.insert(sessions).values({
266
+ id: record.id,
267
+ appId: record.appId,
268
+ userId: record.userId,
269
+ title: record.title,
270
+ model: record.model,
271
+ mode: record.mode,
272
+ createdAt: record.createdAt,
273
+ updatedAt: record.updatedAt
274
+ }).run();
275
+ return record;
276
+ }
277
+ function updateSession(db, id, data, ctx) {
278
+ const tenantCondition = buildSessionsTenantCondition(ctx);
279
+ const condition = tenantCondition ? and2(eq2(sessions.id, id), tenantCondition) : eq2(sessions.id, id);
280
+ const updateData = { updatedAt: /* @__PURE__ */ new Date() };
281
+ if (data.title !== void 0) updateData.title = data.title;
282
+ if (data.model !== void 0) updateData.model = data.model;
283
+ if (data.mode !== void 0) updateData.mode = data.mode;
284
+ db.update(sessions).set(updateData).where(condition).run();
285
+ }
286
+ function deleteSession(db, id, ctx) {
287
+ const tenantCondition = buildSessionsTenantCondition(ctx);
288
+ const condition = tenantCondition ? and2(eq2(sessions.id, id), tenantCondition) : eq2(sessions.id, id);
289
+ db.delete(sessions).where(condition).run();
290
+ }
291
+
292
+ // src/adapters/sqlite/messages.ts
293
+ import { eq as eq3, and as and3, gt } from "drizzle-orm";
294
+ function toMessageRecord(row) {
295
+ return {
296
+ id: row.id,
297
+ sessionId: row.sessionId,
298
+ appId: row.appId,
299
+ userId: row.userId,
300
+ role: row.role,
301
+ content: row.content,
302
+ thinking: row.thinking,
303
+ toolCalls: row.toolCalls,
304
+ searchResults: row.searchResults,
305
+ operationIds: row.operationIds,
306
+ timestamp: row.timestamp
307
+ };
308
+ }
309
+ function getMessages(db, sessionId) {
310
+ const rows = db.select().from(messages).where(eq3(messages.sessionId, sessionId)).orderBy(messages.timestamp).all();
311
+ return rows.map(toMessageRecord);
312
+ }
313
+ function getMessage(db, id, ctx) {
314
+ const tenantCondition = buildMessagesTenantCondition(ctx);
315
+ const condition = tenantCondition ? and3(eq3(messages.id, id), tenantCondition) : eq3(messages.id, id);
316
+ const row = db.select().from(messages).where(condition).get();
317
+ return row ? toMessageRecord(row) : null;
318
+ }
319
+ function saveMessage(db, input, ctx) {
320
+ const now = /* @__PURE__ */ new Date();
321
+ const record = {
322
+ id: input.id,
323
+ sessionId: input.sessionId,
324
+ appId: ctx.appId || null,
325
+ userId: ctx.userId || null,
326
+ role: input.role,
327
+ content: input.content,
328
+ thinking: input.thinking,
329
+ toolCalls: input.toolCalls,
330
+ searchResults: input.searchResults,
331
+ operationIds: input.operationIds,
332
+ timestamp: now
333
+ };
334
+ db.insert(messages).values({
335
+ id: record.id,
336
+ sessionId: record.sessionId,
337
+ appId: record.appId,
338
+ userId: record.userId,
339
+ role: record.role,
340
+ content: record.content,
341
+ thinking: record.thinking,
342
+ toolCalls: record.toolCalls,
343
+ searchResults: record.searchResults,
344
+ operationIds: record.operationIds,
345
+ timestamp: record.timestamp
346
+ }).run();
347
+ db.update(sessions).set({ updatedAt: now }).where(eq3(sessions.id, input.sessionId)).run();
348
+ return record;
349
+ }
350
+ function deleteMessagesAfter(db, sessionId, timestamp) {
351
+ db.delete(messages).where(and3(
352
+ eq3(messages.sessionId, sessionId),
353
+ gt(messages.timestamp, timestamp)
354
+ )).run();
355
+ }
356
+ function deleteSessionMessages(db, sessionId) {
357
+ db.delete(messages).where(eq3(messages.sessionId, sessionId)).run();
358
+ }
359
+
360
+ // src/adapters/sqlite/operations.ts
361
+ import { eq as eq4, and as and4 } from "drizzle-orm";
362
+ function toOperationRecord(row) {
363
+ return {
364
+ id: row.id,
365
+ sessionId: row.sessionId,
366
+ messageId: row.messageId,
367
+ appId: row.appId,
368
+ userId: row.userId,
369
+ command: row.command,
370
+ operationType: row.operationType,
371
+ affectedFiles: row.affectedFiles,
372
+ backupPath: row.backupPath,
373
+ status: row.status,
374
+ errorMessage: row.errorMessage,
375
+ timestamp: row.timestamp
376
+ };
377
+ }
378
+ function getOperations(db, sessionId) {
379
+ const rows = db.select().from(operations).where(eq4(operations.sessionId, sessionId)).orderBy(operations.timestamp).all();
380
+ return rows.map(toOperationRecord);
381
+ }
382
+ function getOperationsByMessage(db, messageId, ctx) {
383
+ const tenantCondition = buildOperationsTenantCondition(ctx);
384
+ const condition = tenantCondition ? and4(eq4(operations.messageId, messageId), tenantCondition) : eq4(operations.messageId, messageId);
385
+ const rows = db.select().from(operations).where(condition).all();
386
+ return rows.map(toOperationRecord);
387
+ }
388
+ function saveOperation(db, input, ctx) {
389
+ const now = /* @__PURE__ */ new Date();
390
+ const record = {
391
+ id: input.id,
392
+ sessionId: input.sessionId,
393
+ messageId: input.messageId,
394
+ appId: ctx.appId || null,
395
+ userId: ctx.userId || null,
396
+ command: input.command,
397
+ operationType: input.operationType,
398
+ affectedFiles: input.affectedFiles,
399
+ backupPath: input.backupPath,
400
+ status: input.status || "pending",
401
+ errorMessage: null,
402
+ timestamp: now
403
+ };
404
+ db.insert(operations).values({
405
+ id: record.id,
406
+ sessionId: record.sessionId,
407
+ messageId: record.messageId,
408
+ appId: record.appId,
409
+ userId: record.userId,
410
+ command: record.command,
411
+ operationType: record.operationType,
412
+ affectedFiles: record.affectedFiles,
413
+ backupPath: record.backupPath,
414
+ status: record.status,
415
+ errorMessage: record.errorMessage,
416
+ timestamp: record.timestamp
417
+ }).run();
418
+ return record;
419
+ }
420
+ function updateOperationStatus(db, id, status, errorMessage) {
421
+ const updateData = { status };
422
+ if (errorMessage !== void 0) {
423
+ updateData.errorMessage = errorMessage;
424
+ }
425
+ db.update(operations).set(updateData).where(eq4(operations.id, id)).run();
426
+ }
427
+ function deleteSessionOperations(db, sessionId) {
428
+ db.delete(operations).where(eq4(operations.sessionId, sessionId)).run();
429
+ }
430
+
431
+ // src/adapters/sqlite/backups.ts
432
+ import { eq as eq5, lt } from "drizzle-orm";
433
+ function toBackupRecord(row) {
434
+ return {
435
+ id: row.id,
436
+ operationId: row.operationId,
437
+ originalPath: row.originalPath,
438
+ backupPath: row.backupPath,
439
+ fileSize: row.fileSize,
440
+ fileHash: row.fileHash,
441
+ createdAt: row.createdAt,
442
+ expiresAt: row.expiresAt
443
+ };
444
+ }
445
+ function getBackups(db, operationId) {
446
+ const rows = db.select().from(backups).where(eq5(backups.operationId, operationId)).all();
447
+ return rows.map(toBackupRecord);
448
+ }
449
+ function saveBackup(db, input) {
450
+ const now = /* @__PURE__ */ new Date();
451
+ const record = {
452
+ ...input,
453
+ createdAt: now
454
+ };
455
+ db.insert(backups).values({
456
+ id: record.id,
457
+ operationId: record.operationId,
458
+ originalPath: record.originalPath,
459
+ backupPath: record.backupPath,
460
+ fileSize: record.fileSize,
461
+ fileHash: record.fileHash,
462
+ createdAt: record.createdAt,
463
+ expiresAt: record.expiresAt
464
+ }).run();
465
+ return record;
466
+ }
467
+ function deleteExpiredBackups(db) {
468
+ const now = /* @__PURE__ */ new Date();
469
+ const result = db.delete(backups).where(lt(backups.expiresAt, now)).run();
470
+ return result.changes;
471
+ }
472
+
473
+ // src/adapters/sqlite/trash.ts
474
+ import { eq as eq6, and as and5, desc as desc2, lt as lt2 } from "drizzle-orm";
475
+ function toTrashRecord(row) {
476
+ return {
477
+ id: row.id,
478
+ sessionId: row.sessionId,
479
+ appId: row.appId,
480
+ userId: row.userId,
481
+ originalPath: row.originalPath,
482
+ trashPath: row.trashPath,
483
+ deletedAt: row.deletedAt,
484
+ autoDeleteAt: row.autoDeleteAt
485
+ };
486
+ }
487
+ function getTrashItems(db, ctx) {
488
+ const tenantCondition = buildTrashTenantCondition(ctx);
489
+ const rows = db.select().from(trash).where(tenantCondition).orderBy(desc2(trash.deletedAt)).all();
490
+ return rows.map(toTrashRecord);
491
+ }
492
+ function moveToTrash(db, input, ctx, config) {
493
+ const now = /* @__PURE__ */ new Date();
494
+ const retentionDays = config.trashRetentionDays || 30;
495
+ const autoDeleteAt = new Date(now.getTime() + retentionDays * 24 * 60 * 60 * 1e3);
496
+ const record = {
497
+ ...input,
498
+ appId: ctx.appId || null,
499
+ userId: ctx.userId || null,
500
+ deletedAt: now,
501
+ autoDeleteAt
502
+ };
503
+ db.insert(trash).values({
504
+ id: record.id,
505
+ sessionId: record.sessionId,
506
+ appId: record.appId,
507
+ userId: record.userId,
508
+ originalPath: record.originalPath,
509
+ trashPath: record.trashPath,
510
+ deletedAt: record.deletedAt,
511
+ autoDeleteAt: record.autoDeleteAt
512
+ }).run();
513
+ return record;
514
+ }
515
+ function restoreFromTrash(db, id, ctx) {
516
+ const tenantCondition = buildTrashTenantCondition(ctx);
517
+ const condition = tenantCondition ? and5(eq6(trash.id, id), tenantCondition) : eq6(trash.id, id);
518
+ const row = db.select().from(trash).where(condition).get();
519
+ if (!row) {
520
+ throw new Error("\u56DE\u6536\u7AD9\u8BB0\u5F55\u4E0D\u5B58\u5728");
521
+ }
522
+ db.delete(trash).where(eq6(trash.id, id)).run();
523
+ return toTrashRecord(row);
524
+ }
525
+ function emptyExpiredTrash(db) {
526
+ const now = /* @__PURE__ */ new Date();
527
+ const result = db.delete(trash).where(lt2(trash.autoDeleteAt, now)).run();
528
+ return result.changes;
529
+ }
530
+
531
+ // src/adapters/sqlite/embeddings.ts
532
+ import { eq as eq7, and as and6 } from "drizzle-orm";
533
+ function saveEmbedding(db, id, content, embedding, metadata, ctx) {
534
+ const now = /* @__PURE__ */ new Date();
535
+ db.insert(embeddings).values({
536
+ id,
537
+ sessionId: metadata.sessionId,
538
+ messageId: metadata.messageId || null,
539
+ appId: ctx.appId || null,
540
+ userId: ctx.userId || null,
541
+ content,
542
+ contentType: metadata.contentType,
543
+ embedding: JSON.stringify(embedding),
544
+ metadata: JSON.stringify(metadata),
545
+ createdAt: now
546
+ }).run();
547
+ }
548
+ function searchSimilar(db, queryEmbedding, options, ctx) {
549
+ const { limit = 10, threshold = 0.7, sessionId } = options;
550
+ const conditions = [];
551
+ if (ctx.appId) conditions.push(eq7(embeddings.appId, ctx.appId));
552
+ if (ctx.userId) conditions.push(eq7(embeddings.userId, ctx.userId));
553
+ if (sessionId) conditions.push(eq7(embeddings.sessionId, sessionId));
554
+ const rows = db.select().from(embeddings).where(conditions.length > 0 ? and6(...conditions) : void 0).all();
555
+ const results = rows.map((row) => {
556
+ const storedEmbedding = row.embedding ? JSON.parse(row.embedding) : [];
557
+ const similarity = cosineSimilarity(queryEmbedding, storedEmbedding);
558
+ return {
559
+ id: row.id,
560
+ content: row.content,
561
+ contentType: row.contentType,
562
+ similarity,
563
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0
564
+ };
565
+ }).filter((r) => r.similarity >= threshold).sort((a, b) => b.similarity - a.similarity).slice(0, limit);
566
+ return results;
567
+ }
568
+ function deleteSessionEmbeddings(db, sessionId) {
569
+ db.delete(embeddings).where(eq7(embeddings.sessionId, sessionId)).run();
570
+ }
571
+
572
+ // src/adapters/sqlite/index.ts
573
+ var SqliteAdapter = class {
574
+ sqlite;
575
+ db;
576
+ config;
577
+ constructor(dbPath, config = { type: "sqlite" }) {
578
+ this.sqlite = new Database(dbPath);
579
+ this.db = drizzle(this.sqlite);
580
+ this.config = config;
581
+ initSchema(this.sqlite);
582
+ }
583
+ // ============ 会话 ============
584
+ async getSessions(ctx) {
585
+ return getSessions(this.db, ctx);
586
+ }
587
+ async getSession(id, ctx) {
588
+ return getSession(this.db, id, ctx);
589
+ }
590
+ async createSession(input, ctx) {
591
+ return createSession(this.db, input, ctx);
592
+ }
593
+ async updateSession(id, data, ctx) {
594
+ updateSession(this.db, id, data, ctx);
595
+ }
596
+ async deleteSession(id, ctx) {
597
+ deleteSessionMessages(this.db, id);
598
+ deleteSessionOperations(this.db, id);
599
+ deleteSessionEmbeddings(this.db, id);
600
+ deleteSession(this.db, id, ctx);
601
+ }
602
+ // ============ 消息 ============
603
+ async getMessages(sessionId, ctx) {
604
+ const session = await this.getSession(sessionId, ctx);
605
+ if (!session) return [];
606
+ return getMessages(this.db, sessionId);
607
+ }
608
+ async getMessage(id, ctx) {
609
+ return getMessage(this.db, id, ctx);
610
+ }
611
+ async saveMessage(input, ctx) {
612
+ return saveMessage(this.db, input, ctx);
613
+ }
614
+ async deleteMessagesAfter(sessionId, timestamp, ctx) {
615
+ const session = await this.getSession(sessionId, ctx);
616
+ if (!session) return;
617
+ deleteMessagesAfter(this.db, sessionId, timestamp);
618
+ }
619
+ // ============ 操作日志 ============
620
+ async getOperations(sessionId, ctx) {
621
+ const session = await this.getSession(sessionId, ctx);
622
+ if (!session) return [];
623
+ return getOperations(this.db, sessionId);
624
+ }
625
+ async getOperationsByMessage(messageId, ctx) {
626
+ return getOperationsByMessage(this.db, messageId, ctx);
627
+ }
628
+ async saveOperation(input, ctx) {
629
+ return saveOperation(this.db, input, ctx);
630
+ }
631
+ async updateOperationStatus(id, status, errorMessage) {
632
+ updateOperationStatus(this.db, id, status, errorMessage);
633
+ }
634
+ // ============ 备份 ============
635
+ async getBackups(operationId) {
636
+ return getBackups(this.db, operationId);
637
+ }
638
+ async saveBackup(input) {
639
+ return saveBackup(this.db, input);
640
+ }
641
+ async deleteExpiredBackups() {
642
+ return deleteExpiredBackups(this.db);
643
+ }
644
+ // ============ 回收站 ============
645
+ async getTrashItems(ctx) {
646
+ return getTrashItems(this.db, ctx);
647
+ }
648
+ async moveToTrash(input, ctx) {
649
+ return moveToTrash(this.db, input, ctx, this.config);
650
+ }
651
+ async restoreFromTrash(id, ctx) {
652
+ return restoreFromTrash(this.db, id, ctx);
653
+ }
654
+ async emptyExpiredTrash() {
655
+ return emptyExpiredTrash(this.db);
656
+ }
657
+ // ============ 向量搜索 ============
658
+ async saveEmbedding(id, content, embedding, metadata, ctx) {
659
+ saveEmbedding(this.db, id, content, embedding, metadata, ctx);
660
+ }
661
+ async searchSimilar(queryEmbedding, options, ctx) {
662
+ return searchSimilar(this.db, queryEmbedding, options, ctx);
663
+ }
664
+ // ============ 生命周期 ============
665
+ async close() {
666
+ this.sqlite.close();
667
+ }
668
+ };
669
+
670
+ export {
671
+ sessions,
672
+ messages,
673
+ operations,
674
+ backups,
675
+ trash,
676
+ embeddings,
677
+ SqliteAdapter
678
+ };