family-ai-agent 1.0.6 → 1.0.8

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 (149) hide show
  1. package/.letta/settings.local.json +3 -0
  2. package/dist/cli/index.js +1 -1
  3. package/dist/database/adapters/base-adapter.d.ts +81 -0
  4. package/dist/database/adapters/base-adapter.d.ts.map +1 -0
  5. package/dist/database/adapters/base-adapter.js +105 -0
  6. package/dist/database/adapters/base-adapter.js.map +1 -0
  7. package/dist/database/adapters/index.d.ts +49 -0
  8. package/dist/database/adapters/index.d.ts.map +1 -0
  9. package/dist/database/adapters/index.js +200 -0
  10. package/dist/database/adapters/index.js.map +1 -0
  11. package/dist/database/adapters/postgres-adapter.d.ts +75 -0
  12. package/dist/database/adapters/postgres-adapter.d.ts.map +1 -0
  13. package/dist/database/adapters/postgres-adapter.js +225 -0
  14. package/dist/database/adapters/postgres-adapter.js.map +1 -0
  15. package/dist/database/adapters/sqlite-adapter.d.ts +72 -0
  16. package/dist/database/adapters/sqlite-adapter.d.ts.map +1 -0
  17. package/dist/database/adapters/sqlite-adapter.js +368 -0
  18. package/dist/database/adapters/sqlite-adapter.js.map +1 -0
  19. package/dist/database/cache/cache-keys.d.ts +180 -0
  20. package/dist/database/cache/cache-keys.d.ts.map +1 -0
  21. package/dist/database/cache/cache-keys.js +107 -0
  22. package/dist/database/cache/cache-keys.js.map +1 -0
  23. package/dist/database/cache/index.d.ts +24 -0
  24. package/dist/database/cache/index.d.ts.map +1 -0
  25. package/dist/database/cache/index.js +34 -0
  26. package/dist/database/cache/index.js.map +1 -0
  27. package/dist/database/cache/query-cache.d.ts +67 -0
  28. package/dist/database/cache/query-cache.d.ts.map +1 -0
  29. package/dist/database/cache/query-cache.js +177 -0
  30. package/dist/database/cache/query-cache.js.map +1 -0
  31. package/dist/database/client.d.ts +63 -4
  32. package/dist/database/client.d.ts.map +1 -1
  33. package/dist/database/client.js +147 -59
  34. package/dist/database/client.js.map +1 -1
  35. package/dist/database/db-config.d.ts +104 -0
  36. package/dist/database/db-config.d.ts.map +1 -0
  37. package/dist/database/db-config.js +167 -0
  38. package/dist/database/db-config.js.map +1 -0
  39. package/dist/database/drizzle/index.d.ts +42 -0
  40. package/dist/database/drizzle/index.d.ts.map +1 -0
  41. package/dist/database/drizzle/index.js +48 -0
  42. package/dist/database/drizzle/index.js.map +1 -0
  43. package/dist/database/drizzle/schema/audit.d.ts +533 -0
  44. package/dist/database/drizzle/schema/audit.d.ts.map +1 -0
  45. package/dist/database/drizzle/schema/audit.js +71 -0
  46. package/dist/database/drizzle/schema/audit.js.map +1 -0
  47. package/dist/database/drizzle/schema/checkpoints.d.ts +665 -0
  48. package/dist/database/drizzle/schema/checkpoints.d.ts.map +1 -0
  49. package/dist/database/drizzle/schema/checkpoints.js +110 -0
  50. package/dist/database/drizzle/schema/checkpoints.js.map +1 -0
  51. package/dist/database/drizzle/schema/conversations.d.ts +449 -0
  52. package/dist/database/drizzle/schema/conversations.d.ts.map +1 -0
  53. package/dist/database/drizzle/schema/conversations.js +91 -0
  54. package/dist/database/drizzle/schema/conversations.js.map +1 -0
  55. package/dist/database/drizzle/schema/documents.d.ts +600 -0
  56. package/dist/database/drizzle/schema/documents.d.ts.map +1 -0
  57. package/dist/database/drizzle/schema/documents.js +100 -0
  58. package/dist/database/drizzle/schema/documents.js.map +1 -0
  59. package/dist/database/drizzle/schema/index.d.ts +3084 -0
  60. package/dist/database/drizzle/schema/index.d.ts.map +1 -0
  61. package/dist/database/drizzle/schema/index.js +46 -0
  62. package/dist/database/drizzle/schema/index.js.map +1 -0
  63. package/dist/database/drizzle/schema/memories.d.ts +435 -0
  64. package/dist/database/drizzle/schema/memories.d.ts.map +1 -0
  65. package/dist/database/drizzle/schema/memories.js +73 -0
  66. package/dist/database/drizzle/schema/memories.js.map +1 -0
  67. package/dist/database/drizzle/schema/tasks.d.ts +565 -0
  68. package/dist/database/drizzle/schema/tasks.d.ts.map +1 -0
  69. package/dist/database/drizzle/schema/tasks.js +84 -0
  70. package/dist/database/drizzle/schema/tasks.js.map +1 -0
  71. package/dist/database/health/circuit-breaker.d.ts +81 -0
  72. package/dist/database/health/circuit-breaker.d.ts.map +1 -0
  73. package/dist/database/health/circuit-breaker.js +184 -0
  74. package/dist/database/health/circuit-breaker.js.map +1 -0
  75. package/dist/database/health/health-monitor.d.ts +69 -0
  76. package/dist/database/health/health-monitor.d.ts.map +1 -0
  77. package/dist/database/health/health-monitor.js +174 -0
  78. package/dist/database/health/health-monitor.js.map +1 -0
  79. package/dist/database/health/index.d.ts +27 -0
  80. package/dist/database/health/index.d.ts.map +1 -0
  81. package/dist/database/health/index.js +23 -0
  82. package/dist/database/health/index.js.map +1 -0
  83. package/dist/database/index.d.ts +16 -0
  84. package/dist/database/index.d.ts.map +1 -0
  85. package/dist/database/index.js +41 -0
  86. package/dist/database/index.js.map +1 -0
  87. package/dist/database/migrations/index.d.ts +34 -0
  88. package/dist/database/migrations/index.d.ts.map +1 -0
  89. package/dist/database/migrations/index.js +45 -0
  90. package/dist/database/migrations/index.js.map +1 -0
  91. package/dist/database/migrations/migrator.d.ts +77 -0
  92. package/dist/database/migrations/migrator.d.ts.map +1 -0
  93. package/dist/database/migrations/migrator.js +258 -0
  94. package/dist/database/migrations/migrator.js.map +1 -0
  95. package/dist/database/migrations/versions/001_initial.d.ts +9 -0
  96. package/dist/database/migrations/versions/001_initial.d.ts.map +1 -0
  97. package/dist/database/migrations/versions/001_initial.js +183 -0
  98. package/dist/database/migrations/versions/001_initial.js.map +1 -0
  99. package/dist/database/types.d.ts +255 -0
  100. package/dist/database/types.d.ts.map +1 -0
  101. package/dist/database/types.js +8 -0
  102. package/dist/database/types.js.map +1 -0
  103. package/dist/database/vector/embedding-cache.d.ts +92 -0
  104. package/dist/database/vector/embedding-cache.d.ts.map +1 -0
  105. package/dist/database/vector/embedding-cache.js +185 -0
  106. package/dist/database/vector/embedding-cache.js.map +1 -0
  107. package/dist/database/vector/hnsw-index.d.ts +111 -0
  108. package/dist/database/vector/hnsw-index.d.ts.map +1 -0
  109. package/dist/database/vector/hnsw-index.js +337 -0
  110. package/dist/database/vector/hnsw-index.js.map +1 -0
  111. package/dist/database/vector/index.d.ts +75 -0
  112. package/dist/database/vector/index.d.ts.map +1 -0
  113. package/dist/database/vector/index.js +213 -0
  114. package/dist/database/vector/index.js.map +1 -0
  115. package/dist/database/vector/similarity.d.ts +67 -0
  116. package/dist/database/vector/similarity.d.ts.map +1 -0
  117. package/dist/database/vector/similarity.js +176 -0
  118. package/dist/database/vector/similarity.js.map +1 -0
  119. package/package.json +6 -3
  120. package/src/cli/index.ts +1 -1
  121. package/src/database/adapters/base-adapter.ts +171 -0
  122. package/src/database/adapters/index.ts +224 -0
  123. package/src/database/adapters/postgres-adapter.ts +285 -0
  124. package/src/database/adapters/sqlite-adapter.ts +420 -0
  125. package/src/database/cache/cache-keys.ts +150 -0
  126. package/src/database/cache/index.ts +44 -0
  127. package/src/database/cache/query-cache.ts +213 -0
  128. package/src/database/client.ts +166 -64
  129. package/src/database/db-config.ts +194 -0
  130. package/src/database/drizzle/index.ts +66 -0
  131. package/src/database/drizzle/schema/audit.ts +127 -0
  132. package/src/database/drizzle/schema/checkpoints.ts +164 -0
  133. package/src/database/drizzle/schema/conversations.ts +138 -0
  134. package/src/database/drizzle/schema/documents.ts +157 -0
  135. package/src/database/drizzle/schema/index.ts +139 -0
  136. package/src/database/drizzle/schema/memories.ts +127 -0
  137. package/src/database/drizzle/schema/tasks.ts +129 -0
  138. package/src/database/health/circuit-breaker.ts +214 -0
  139. package/src/database/health/health-monitor.ts +224 -0
  140. package/src/database/health/index.ts +41 -0
  141. package/src/database/index.ts +157 -0
  142. package/src/database/migrations/index.ts +52 -0
  143. package/src/database/migrations/migrator.ts +325 -0
  144. package/src/database/migrations/versions/001_initial.ts +198 -0
  145. package/src/database/types.ts +324 -0
  146. package/src/database/vector/embedding-cache.ts +234 -0
  147. package/src/database/vector/hnsw-index.ts +452 -0
  148. package/src/database/vector/index.ts +292 -0
  149. package/src/database/vector/similarity.ts +198 -0
@@ -0,0 +1,452 @@
1
+ /**
2
+ * HNSW (Hierarchical Navigable Small World) Index
3
+ *
4
+ * In-memory approximate nearest neighbor search for SQLite mode.
5
+ * Provides fast vector similarity search without pgvector.
6
+ */
7
+
8
+ import { cosineSimilarity, euclideanDistance } from './similarity.js';
9
+ import { createLogger } from '../../utils/logger.js';
10
+
11
+ const logger = createLogger('HNSWIndex');
12
+
13
+ /**
14
+ * HNSW node representing a vector in the graph
15
+ */
16
+ interface HNSWNode {
17
+ id: string;
18
+ vector: number[];
19
+ neighbors: Map<number, Set<string>>; // level -> neighbor IDs
20
+ level: number;
21
+ }
22
+
23
+ /**
24
+ * Search result with similarity score
25
+ */
26
+ export interface SearchResult {
27
+ id: string;
28
+ similarity: number;
29
+ }
30
+
31
+ /**
32
+ * HNSW index configuration
33
+ */
34
+ export interface HNSWConfig {
35
+ /** Vector dimension */
36
+ dimension: number;
37
+ /** Similarity metric: 'cosine' or 'euclidean' */
38
+ similarity: 'cosine' | 'euclidean';
39
+ /** Maximum connections per layer */
40
+ M: number;
41
+ /** Maximum connections for layer 0 */
42
+ M0: number;
43
+ /** Size of dynamic candidate list during construction */
44
+ efConstruction: number;
45
+ /** Level generation factor (1/ln(M)) */
46
+ mL: number;
47
+ }
48
+
49
+ /**
50
+ * Default HNSW configuration
51
+ */
52
+ const DEFAULT_CONFIG: HNSWConfig = {
53
+ dimension: 1536,
54
+ similarity: 'cosine',
55
+ M: 16,
56
+ M0: 32,
57
+ efConstruction: 200,
58
+ mL: 1 / Math.log(16),
59
+ };
60
+
61
+ /**
62
+ * HNSW Index for fast approximate nearest neighbor search
63
+ */
64
+ export class HNSWIndex {
65
+ private nodes: Map<string, HNSWNode> = new Map();
66
+ private entryPoint: string | null = null;
67
+ private maxLevel: number = 0;
68
+ private config: HNSWConfig;
69
+
70
+ constructor(config: Partial<HNSWConfig> = {}) {
71
+ this.config = { ...DEFAULT_CONFIG, ...config };
72
+ }
73
+
74
+ /**
75
+ * Get similarity function based on config
76
+ */
77
+ private getSimilarity(): (a: number[], b: number[]) => number {
78
+ if (this.config.similarity === 'euclidean') {
79
+ // Convert distance to similarity (higher is better)
80
+ return (a, b) => 1 / (1 + euclideanDistance(a, b));
81
+ }
82
+ return cosineSimilarity;
83
+ }
84
+
85
+ /**
86
+ * Generate random level for new node
87
+ */
88
+ private randomLevel(): number {
89
+ let level = 0;
90
+ while (Math.random() < this.config.mL && level < 32) {
91
+ level++;
92
+ }
93
+ return level;
94
+ }
95
+
96
+ /**
97
+ * Get maximum connections for a level
98
+ */
99
+ private getMaxConnections(level: number): number {
100
+ return level === 0 ? this.config.M0 : this.config.M;
101
+ }
102
+
103
+ /**
104
+ * Add a vector to the index
105
+ */
106
+ add(id: string, vector: number[]): void {
107
+ if (vector.length !== this.config.dimension) {
108
+ throw new Error(`Expected dimension ${this.config.dimension}, got ${vector.length}`);
109
+ }
110
+
111
+ // Check if already exists
112
+ if (this.nodes.has(id)) {
113
+ // Update existing node
114
+ const existingNode = this.nodes.get(id)!;
115
+ existingNode.vector = vector;
116
+ return;
117
+ }
118
+
119
+ const level = this.randomLevel();
120
+ const node: HNSWNode = {
121
+ id,
122
+ vector,
123
+ neighbors: new Map(),
124
+ level,
125
+ };
126
+
127
+ // Initialize neighbor sets for each level
128
+ for (let l = 0; l <= level; l++) {
129
+ node.neighbors.set(l, new Set());
130
+ }
131
+
132
+ this.nodes.set(id, node);
133
+
134
+ // First node becomes entry point
135
+ if (!this.entryPoint) {
136
+ this.entryPoint = id;
137
+ this.maxLevel = level;
138
+ return;
139
+ }
140
+
141
+ const similarity = this.getSimilarity();
142
+ let currentNode = this.entryPoint;
143
+
144
+ // Traverse from top level down to level + 1
145
+ for (let l = this.maxLevel; l > level; l--) {
146
+ currentNode = this.searchLayer(vector, currentNode, 1, l, similarity)[0]?.id ?? currentNode;
147
+ }
148
+
149
+ // Insert at each level from level down to 0
150
+ for (let l = Math.min(level, this.maxLevel); l >= 0; l--) {
151
+ const candidates = this.searchLayer(
152
+ vector,
153
+ currentNode,
154
+ this.config.efConstruction,
155
+ l,
156
+ similarity
157
+ );
158
+
159
+ const neighbors = this.selectNeighbors(candidates, this.getMaxConnections(l));
160
+
161
+ // Connect new node to neighbors
162
+ for (const neighbor of neighbors) {
163
+ node.neighbors.get(l)!.add(neighbor.id);
164
+
165
+ // Connect neighbors back to new node (bidirectional)
166
+ const neighborNode = this.nodes.get(neighbor.id);
167
+ if (neighborNode) {
168
+ neighborNode.neighbors.get(l)?.add(id);
169
+
170
+ // Prune if too many connections
171
+ const maxConn = this.getMaxConnections(l);
172
+ if ((neighborNode.neighbors.get(l)?.size ?? 0) > maxConn) {
173
+ this.pruneConnections(neighborNode, l, maxConn, similarity);
174
+ }
175
+ }
176
+ }
177
+
178
+ if (candidates.length > 0) {
179
+ currentNode = candidates[0]!.id;
180
+ }
181
+ }
182
+
183
+ // Update entry point if new node has higher level
184
+ if (level > this.maxLevel) {
185
+ this.entryPoint = id;
186
+ this.maxLevel = level;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Search for k nearest neighbors
192
+ */
193
+ search(query: number[], k: number, ef?: number): SearchResult[] {
194
+ if (query.length !== this.config.dimension) {
195
+ throw new Error(`Expected dimension ${this.config.dimension}, got ${query.length}`);
196
+ }
197
+
198
+ if (!this.entryPoint) {
199
+ return [];
200
+ }
201
+
202
+ const effectiveEf = ef ?? Math.max(k, 100);
203
+ const similarity = this.getSimilarity();
204
+ let currentNode = this.entryPoint;
205
+
206
+ // Traverse from top level down to level 1
207
+ for (let l = this.maxLevel; l > 0; l--) {
208
+ currentNode = this.searchLayer(query, currentNode, 1, l, similarity)[0]?.id ?? currentNode;
209
+ }
210
+
211
+ // Search at level 0 with ef candidates
212
+ const candidates = this.searchLayer(query, currentNode, effectiveEf, 0, similarity);
213
+
214
+ // Return top k
215
+ return candidates.slice(0, k);
216
+ }
217
+
218
+ /**
219
+ * Search within a layer
220
+ */
221
+ private searchLayer(
222
+ query: number[],
223
+ entryId: string,
224
+ ef: number,
225
+ level: number,
226
+ similarity: (a: number[], b: number[]) => number
227
+ ): SearchResult[] {
228
+ const entryNode = this.nodes.get(entryId);
229
+ if (!entryNode) return [];
230
+
231
+ const visited = new Set<string>([entryId]);
232
+ const candidates: SearchResult[] = [
233
+ { id: entryId, similarity: similarity(query, entryNode.vector) },
234
+ ];
235
+
236
+ // Priority queue (sorted by similarity descending)
237
+ const toExplore: SearchResult[] = [...candidates];
238
+
239
+ while (toExplore.length > 0) {
240
+ // Get best candidate
241
+ toExplore.sort((a, b) => b.similarity - a.similarity);
242
+ const current = toExplore.shift()!;
243
+
244
+ // Get worst in results
245
+ candidates.sort((a, b) => b.similarity - a.similarity);
246
+ const worstSimilarity = candidates.length >= ef
247
+ ? candidates[candidates.length - 1]!.similarity
248
+ : -Infinity;
249
+
250
+ // Stop if current is worse than worst result
251
+ if (current.similarity < worstSimilarity) {
252
+ break;
253
+ }
254
+
255
+ // Explore neighbors
256
+ const currentNode = this.nodes.get(current.id);
257
+ const neighbors = currentNode?.neighbors.get(level) ?? new Set();
258
+
259
+ for (const neighborId of neighbors) {
260
+ if (visited.has(neighborId)) continue;
261
+ visited.add(neighborId);
262
+
263
+ const neighborNode = this.nodes.get(neighborId);
264
+ if (!neighborNode) continue;
265
+
266
+ const sim = similarity(query, neighborNode.vector);
267
+ const result = { id: neighborId, similarity: sim };
268
+
269
+ if (candidates.length < ef || sim > worstSimilarity) {
270
+ candidates.push(result);
271
+ candidates.sort((a, b) => b.similarity - a.similarity);
272
+ if (candidates.length > ef) {
273
+ candidates.pop();
274
+ }
275
+ toExplore.push(result);
276
+ }
277
+ }
278
+ }
279
+
280
+ return candidates;
281
+ }
282
+
283
+ /**
284
+ * Select best neighbors from candidates
285
+ */
286
+ private selectNeighbors(candidates: SearchResult[], maxNeighbors: number): SearchResult[] {
287
+ // Simple approach: take top maxNeighbors by similarity
288
+ return candidates
289
+ .sort((a, b) => b.similarity - a.similarity)
290
+ .slice(0, maxNeighbors);
291
+ }
292
+
293
+ /**
294
+ * Prune connections to maintain max connections
295
+ */
296
+ private pruneConnections(
297
+ node: HNSWNode,
298
+ level: number,
299
+ maxConnections: number,
300
+ similarity: (a: number[], b: number[]) => number
301
+ ): void {
302
+ const neighbors = node.neighbors.get(level);
303
+ if (!neighbors || neighbors.size <= maxConnections) return;
304
+
305
+ // Calculate similarities and keep best
306
+ const scored: SearchResult[] = [];
307
+ for (const neighborId of neighbors) {
308
+ const neighborNode = this.nodes.get(neighborId);
309
+ if (neighborNode) {
310
+ scored.push({
311
+ id: neighborId,
312
+ similarity: similarity(node.vector, neighborNode.vector),
313
+ });
314
+ }
315
+ }
316
+
317
+ scored.sort((a, b) => b.similarity - a.similarity);
318
+ const keep = new Set(scored.slice(0, maxConnections).map((s) => s.id));
319
+
320
+ // Remove excess connections
321
+ for (const neighborId of neighbors) {
322
+ if (!keep.has(neighborId)) {
323
+ neighbors.delete(neighborId);
324
+ // Also remove back-link
325
+ this.nodes.get(neighborId)?.neighbors.get(level)?.delete(node.id);
326
+ }
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Remove a vector from the index
332
+ */
333
+ remove(id: string): boolean {
334
+ const node = this.nodes.get(id);
335
+ if (!node) return false;
336
+
337
+ // Remove from all neighbors
338
+ for (const [level, neighbors] of node.neighbors) {
339
+ for (const neighborId of neighbors) {
340
+ this.nodes.get(neighborId)?.neighbors.get(level)?.delete(id);
341
+ }
342
+ }
343
+
344
+ this.nodes.delete(id);
345
+
346
+ // Update entry point if needed
347
+ if (this.entryPoint === id) {
348
+ if (this.nodes.size === 0) {
349
+ this.entryPoint = null;
350
+ this.maxLevel = 0;
351
+ } else {
352
+ // Find new entry point with highest level
353
+ let maxLvl = 0;
354
+ let newEntry: string | null = null;
355
+ for (const [nodeId, n] of this.nodes) {
356
+ if (n.level >= maxLvl) {
357
+ maxLvl = n.level;
358
+ newEntry = nodeId;
359
+ }
360
+ }
361
+ this.entryPoint = newEntry;
362
+ this.maxLevel = maxLvl;
363
+ }
364
+ }
365
+
366
+ return true;
367
+ }
368
+
369
+ /**
370
+ * Check if an ID exists in the index
371
+ */
372
+ has(id: string): boolean {
373
+ return this.nodes.has(id);
374
+ }
375
+
376
+ /**
377
+ * Get vector by ID
378
+ */
379
+ get(id: string): number[] | undefined {
380
+ return this.nodes.get(id)?.vector;
381
+ }
382
+
383
+ /**
384
+ * Get index size
385
+ */
386
+ get size(): number {
387
+ return this.nodes.size;
388
+ }
389
+
390
+ /**
391
+ * Get index statistics
392
+ */
393
+ getStats(): {
394
+ size: number;
395
+ dimension: number;
396
+ maxLevel: number;
397
+ avgConnections: number;
398
+ } {
399
+ let totalConnections = 0;
400
+ let connectionCount = 0;
401
+
402
+ for (const node of this.nodes.values()) {
403
+ for (const neighbors of node.neighbors.values()) {
404
+ totalConnections += neighbors.size;
405
+ connectionCount++;
406
+ }
407
+ }
408
+
409
+ return {
410
+ size: this.nodes.size,
411
+ dimension: this.config.dimension,
412
+ maxLevel: this.maxLevel,
413
+ avgConnections: connectionCount > 0 ? totalConnections / connectionCount : 0,
414
+ };
415
+ }
416
+
417
+ /**
418
+ * Clear the index
419
+ */
420
+ clear(): void {
421
+ this.nodes.clear();
422
+ this.entryPoint = null;
423
+ this.maxLevel = 0;
424
+ }
425
+
426
+ /**
427
+ * Get all IDs in the index
428
+ */
429
+ getAllIds(): string[] {
430
+ return Array.from(this.nodes.keys());
431
+ }
432
+
433
+ /**
434
+ * Rebuild index from scratch (for optimization)
435
+ */
436
+ rebuild(): void {
437
+ const vectors = Array.from(this.nodes.entries()).map(([id, node]) => ({
438
+ id,
439
+ vector: node.vector,
440
+ }));
441
+
442
+ this.clear();
443
+
444
+ for (const { id, vector } of vectors) {
445
+ this.add(id, vector);
446
+ }
447
+
448
+ logger.debug('Index rebuilt', { size: this.size });
449
+ }
450
+ }
451
+
452
+ export default HNSWIndex;