@monoes/memory 1.0.0 → 1.0.1

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 (98) hide show
  1. package/README.md +20 -24
  2. package/dist/agent-memory-scope.d.ts +1 -1
  3. package/dist/agent-memory-scope.js +2 -2
  4. package/dist/agent-memory-scope.js.map +1 -1
  5. package/dist/auto-memory-bridge.d.ts +12 -12
  6. package/dist/auto-memory-bridge.d.ts.map +1 -1
  7. package/dist/auto-memory-bridge.js +24 -18
  8. package/dist/auto-memory-bridge.js.map +1 -1
  9. package/dist/cache-manager.d.ts.map +1 -1
  10. package/dist/cache-manager.js +7 -3
  11. package/dist/cache-manager.js.map +1 -1
  12. package/dist/checkpointer.d.ts.map +1 -1
  13. package/dist/checkpointer.js +14 -4
  14. package/dist/checkpointer.js.map +1 -1
  15. package/dist/controller-registry.d.ts +14 -20
  16. package/dist/controller-registry.d.ts.map +1 -1
  17. package/dist/controller-registry.js +52 -423
  18. package/dist/controller-registry.js.map +1 -1
  19. package/dist/database-provider.d.ts.map +1 -1
  20. package/dist/database-provider.js +3 -15
  21. package/dist/database-provider.js.map +1 -1
  22. package/dist/diskann-backend.d.ts.map +1 -1
  23. package/dist/diskann-backend.js +16 -4
  24. package/dist/diskann-backend.js.map +1 -1
  25. package/dist/episodic-store.js +5 -5
  26. package/dist/episodic-store.js.map +1 -1
  27. package/dist/hnsw-index.d.ts +0 -1
  28. package/dist/hnsw-index.d.ts.map +1 -1
  29. package/dist/hnsw-index.js +2 -48
  30. package/dist/hnsw-index.js.map +1 -1
  31. package/dist/hnsw-lite.d.ts.map +1 -1
  32. package/dist/hnsw-lite.js +5 -1
  33. package/dist/hnsw-lite.js.map +1 -1
  34. package/dist/hybrid-backend.d.ts +29 -31
  35. package/dist/hybrid-backend.d.ts.map +1 -1
  36. package/dist/hybrid-backend.js +153 -152
  37. package/dist/hybrid-backend.js.map +1 -1
  38. package/dist/index.d.ts +19 -31
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +24 -61
  41. package/dist/index.js.map +1 -1
  42. package/dist/lancedb-backend.d.ts +68 -0
  43. package/dist/lancedb-backend.d.ts.map +1 -0
  44. package/dist/lancedb-backend.js +468 -0
  45. package/dist/lancedb-backend.js.map +1 -0
  46. package/dist/learning-bridge.d.ts +19 -1
  47. package/dist/learning-bridge.d.ts.map +1 -1
  48. package/dist/learning-bridge.js +96 -52
  49. package/dist/learning-bridge.js.map +1 -1
  50. package/dist/math-utils.d.ts +6 -0
  51. package/dist/math-utils.d.ts.map +1 -0
  52. package/dist/math-utils.js +34 -0
  53. package/dist/math-utils.js.map +1 -0
  54. package/dist/memory-graph.d.ts +2 -0
  55. package/dist/memory-graph.d.ts.map +1 -1
  56. package/dist/memory-graph.js +22 -5
  57. package/dist/memory-graph.js.map +1 -1
  58. package/dist/migration.d.ts +5 -5
  59. package/dist/migration.d.ts.map +1 -1
  60. package/dist/migration.js +1 -1
  61. package/dist/persistent-sona.d.ts +1 -1
  62. package/dist/persistent-sona.d.ts.map +1 -1
  63. package/dist/persistent-sona.js +3 -25
  64. package/dist/persistent-sona.js.map +1 -1
  65. package/dist/rvf-backend.d.ts +2 -0
  66. package/dist/rvf-backend.d.ts.map +1 -1
  67. package/dist/rvf-backend.js +70 -62
  68. package/dist/rvf-backend.js.map +1 -1
  69. package/dist/sqlite-backend.d.ts +10 -2
  70. package/dist/sqlite-backend.d.ts.map +1 -1
  71. package/dist/sqlite-backend.js +124 -56
  72. package/dist/sqlite-backend.js.map +1 -1
  73. package/dist/sqljs-backend.d.ts +0 -1
  74. package/dist/sqljs-backend.d.ts.map +1 -1
  75. package/dist/sqljs-backend.js +3 -14
  76. package/dist/sqljs-backend.js.map +1 -1
  77. package/dist/tier-manager.d.ts.map +1 -1
  78. package/dist/tier-manager.js +10 -6
  79. package/dist/tier-manager.js.map +1 -1
  80. package/dist/tiers/contextual.d.ts +2 -0
  81. package/dist/tiers/contextual.d.ts.map +1 -1
  82. package/dist/tiers/contextual.js +8 -1
  83. package/dist/tiers/contextual.js.map +1 -1
  84. package/dist/tiers/short-term.js +4 -3
  85. package/dist/tiers/short-term.js.map +1 -1
  86. package/dist/types.d.ts +3 -1
  87. package/dist/types.d.ts.map +1 -1
  88. package/dist/types.js +1 -1
  89. package/dist/types.js.map +1 -1
  90. package/package.json +6 -3
  91. package/dist/agentdb-adapter.d.ts +0 -165
  92. package/dist/agentdb-adapter.d.ts.map +0 -1
  93. package/dist/agentdb-adapter.js +0 -836
  94. package/dist/agentdb-adapter.js.map +0 -1
  95. package/dist/agentdb-backend.d.ts +0 -218
  96. package/dist/agentdb-backend.d.ts.map +0 -1
  97. package/dist/agentdb-backend.js +0 -879
  98. package/dist/agentdb-backend.js.map +0 -1
@@ -1,836 +0,0 @@
1
- /**
2
- * V1 AgentDB Adapter
3
- *
4
- * Unified memory backend implementation using AgentDB with HNSW indexing
5
- * for 150x-12,500x faster vector search. Implements IMemoryBackend interface.
6
- *
7
- * @module v1/memory/agentdb-adapter
8
- */
9
- import { EventEmitter } from 'node:events';
10
- import { createDefaultEntry, } from './types.js';
11
- import { HNSWIndex } from './hnsw-index.js';
12
- import { CacheManager } from './cache-manager.js';
13
- /**
14
- * Default configuration values
15
- */
16
- const DEFAULT_CONFIG = {
17
- dimensions: 1536,
18
- maxEntries: 1000000,
19
- cacheEnabled: true,
20
- cacheSize: 10000,
21
- cacheTtl: 300000, // 5 minutes
22
- hnswM: 16,
23
- hnswEfConstruction: 200,
24
- defaultNamespace: 'default',
25
- persistenceEnabled: false,
26
- };
27
- /**
28
- * AgentDB Memory Backend Adapter
29
- *
30
- * Provides unified memory storage with:
31
- * - HNSW-based vector search (150x-12,500x faster than brute force)
32
- * - LRU caching with TTL support
33
- * - Namespace-based organization
34
- * - Full-text and metadata filtering
35
- * - Event-driven architecture
36
- */
37
- export class AgentDBAdapter extends EventEmitter {
38
- config;
39
- entries = new Map();
40
- index;
41
- cache;
42
- namespaceIndex = new Map();
43
- keyIndex = new Map(); // namespace:key -> id
44
- tagIndex = new Map();
45
- initialized = false;
46
- // Performance tracking
47
- stats = {
48
- queryCount: 0,
49
- totalQueryTime: 0,
50
- searchCount: 0,
51
- totalSearchTime: 0,
52
- writeCount: 0,
53
- totalWriteTime: 0,
54
- };
55
- constructor(config = {}) {
56
- super();
57
- this.config = { ...DEFAULT_CONFIG, ...config };
58
- // Initialize HNSW index
59
- this.index = new HNSWIndex({
60
- dimensions: this.config.dimensions,
61
- M: this.config.hnswM,
62
- efConstruction: this.config.hnswEfConstruction,
63
- maxElements: this.config.maxEntries,
64
- metric: 'cosine',
65
- });
66
- // Initialize cache
67
- this.cache = new CacheManager({
68
- maxSize: this.config.cacheSize,
69
- ttl: this.config.cacheTtl,
70
- lruEnabled: true,
71
- });
72
- // Forward events
73
- this.index.on('point:added', (data) => this.emit('index:added', data));
74
- this.cache.on('cache:hit', (data) => this.emit('cache:hit', data));
75
- this.cache.on('cache:miss', (data) => this.emit('cache:miss', data));
76
- }
77
- /**
78
- * Initialize the adapter
79
- */
80
- async initialize() {
81
- if (this.initialized)
82
- return;
83
- // Load persisted data if enabled
84
- if (this.config.persistenceEnabled && this.config.persistencePath) {
85
- await this.loadFromDisk();
86
- }
87
- this.initialized = true;
88
- this.emit('initialized');
89
- }
90
- /**
91
- * Shutdown the adapter
92
- */
93
- async shutdown() {
94
- if (!this.initialized)
95
- return;
96
- // Persist data if enabled
97
- if (this.config.persistenceEnabled && this.config.persistencePath) {
98
- await this.saveToDisk();
99
- }
100
- this.cache.shutdown();
101
- this.initialized = false;
102
- this.emit('shutdown');
103
- }
104
- /**
105
- * Store a memory entry
106
- */
107
- async store(entry) {
108
- const startTime = performance.now();
109
- // Generate embedding if content provided but no embedding
110
- if (entry.content && !entry.embedding && this.config.embeddingGenerator) {
111
- entry.embedding = await this.config.embeddingGenerator(entry.content);
112
- }
113
- // Normalise namespace so indexes and the entry object agree
114
- const namespace = entry.namespace || this.config.defaultNamespace;
115
- if (entry.namespace !== namespace) {
116
- entry = { ...entry, namespace };
117
- }
118
- // Store in main storage
119
- this.entries.set(entry.id, entry);
120
- // Update namespace index
121
- if (!this.namespaceIndex.has(namespace)) {
122
- this.namespaceIndex.set(namespace, new Set());
123
- }
124
- this.namespaceIndex.get(namespace).add(entry.id);
125
- // Update key index
126
- const keyIndexKey = `${namespace}:${entry.key}`;
127
- this.keyIndex.set(keyIndexKey, entry.id);
128
- // Update tag index
129
- for (const tag of entry.tags) {
130
- if (!this.tagIndex.has(tag)) {
131
- this.tagIndex.set(tag, new Set());
132
- }
133
- this.tagIndex.get(tag).add(entry.id);
134
- }
135
- // Index embedding if available
136
- if (entry.embedding) {
137
- await this.index.addPoint(entry.id, entry.embedding);
138
- }
139
- // Update cache
140
- if (this.config.cacheEnabled) {
141
- this.cache.set(entry.id, entry);
142
- }
143
- const duration = performance.now() - startTime;
144
- this.stats.writeCount++;
145
- this.stats.totalWriteTime += duration;
146
- this.emit('entry:stored', { id: entry.id, duration });
147
- }
148
- /**
149
- * Get a memory entry by ID
150
- */
151
- async get(id) {
152
- // Check cache first
153
- if (this.config.cacheEnabled) {
154
- const cached = this.cache.get(id);
155
- if (cached) {
156
- this.updateAccessStats(cached);
157
- return cached;
158
- }
159
- }
160
- const entry = this.entries.get(id);
161
- if (entry) {
162
- this.updateAccessStats(entry);
163
- if (this.config.cacheEnabled) {
164
- this.cache.set(id, entry);
165
- }
166
- }
167
- return entry || null;
168
- }
169
- /**
170
- * Get a memory entry by key within a namespace
171
- */
172
- async getByKey(namespace, key) {
173
- const keyIndexKey = `${namespace}:${key}`;
174
- const id = this.keyIndex.get(keyIndexKey);
175
- if (!id)
176
- return null;
177
- return this.get(id);
178
- }
179
- /**
180
- * Update a memory entry
181
- */
182
- async update(id, update) {
183
- const entry = this.entries.get(id);
184
- if (!entry)
185
- return null;
186
- // Apply updates
187
- if (update.content !== undefined) {
188
- entry.content = update.content;
189
- // Regenerate embedding if content changed
190
- if (this.config.embeddingGenerator) {
191
- entry.embedding = await this.config.embeddingGenerator(entry.content);
192
- // Re-index
193
- await this.index.removePoint(id);
194
- await this.index.addPoint(id, entry.embedding);
195
- }
196
- }
197
- if (update.tags !== undefined) {
198
- // Update tag index
199
- for (const oldTag of entry.tags) {
200
- this.tagIndex.get(oldTag)?.delete(id);
201
- }
202
- entry.tags = update.tags;
203
- for (const newTag of update.tags) {
204
- if (!this.tagIndex.has(newTag)) {
205
- this.tagIndex.set(newTag, new Set());
206
- }
207
- this.tagIndex.get(newTag).add(id);
208
- }
209
- }
210
- if (update.metadata !== undefined) {
211
- entry.metadata = { ...entry.metadata, ...update.metadata };
212
- }
213
- if (update.accessLevel !== undefined) {
214
- entry.accessLevel = update.accessLevel;
215
- }
216
- if (update.expiresAt !== undefined) {
217
- entry.expiresAt = update.expiresAt;
218
- }
219
- if (update.references !== undefined) {
220
- entry.references = update.references;
221
- }
222
- entry.updatedAt = Date.now();
223
- entry.version++;
224
- // Update cache
225
- if (this.config.cacheEnabled) {
226
- this.cache.set(id, entry);
227
- }
228
- this.emit('entry:updated', { id });
229
- return entry;
230
- }
231
- /**
232
- * Delete a memory entry
233
- */
234
- async delete(id) {
235
- const entry = this.entries.get(id);
236
- if (!entry)
237
- return false;
238
- // Remove from main storage
239
- this.entries.delete(id);
240
- // Remove from namespace index
241
- this.namespaceIndex.get(entry.namespace)?.delete(id);
242
- // Remove from key index
243
- const keyIndexKey = `${entry.namespace}:${entry.key}`;
244
- this.keyIndex.delete(keyIndexKey);
245
- // Remove from tag index
246
- for (const tag of entry.tags) {
247
- this.tagIndex.get(tag)?.delete(id);
248
- }
249
- // Remove from vector index
250
- if (entry.embedding) {
251
- await this.index.removePoint(id);
252
- }
253
- // Remove from cache
254
- if (this.config.cacheEnabled) {
255
- this.cache.delete(id);
256
- }
257
- this.emit('entry:deleted', { id });
258
- return true;
259
- }
260
- /**
261
- * Query memory entries with filters
262
- */
263
- async query(query) {
264
- const startTime = performance.now();
265
- let results = [];
266
- switch (query.type) {
267
- case 'exact':
268
- if (query.key && query.namespace) {
269
- const entry = await this.getByKey(query.namespace, query.key);
270
- if (entry)
271
- results = [entry];
272
- }
273
- break;
274
- case 'prefix':
275
- results = this.queryByPrefix(query);
276
- break;
277
- case 'tag':
278
- results = this.queryByTags(query);
279
- break;
280
- case 'semantic':
281
- case 'hybrid':
282
- results = await this.querySemanticWithFilters(query);
283
- break;
284
- default:
285
- results = this.queryWithFilters(query);
286
- }
287
- // Apply common filters
288
- results = this.applyFilters(results, query);
289
- // Apply sort if requested
290
- if (query.sortField && query.sortField !== 'score') {
291
- const field = query.sortField;
292
- const dir = query.sortDirection === 'asc' ? 1 : -1;
293
- results = results.slice().sort((a, b) => {
294
- const aVal = field === 'key' ? a.key
295
- : field === 'updatedAt' ? a.updatedAt
296
- : field === 'lastAccessedAt' ? (a.lastAccessedAt ?? 0)
297
- : field === 'accessCount' ? a.accessCount
298
- : a.createdAt;
299
- const bVal = field === 'key' ? b.key
300
- : field === 'updatedAt' ? b.updatedAt
301
- : field === 'lastAccessedAt' ? (b.lastAccessedAt ?? 0)
302
- : field === 'accessCount' ? b.accessCount
303
- : b.createdAt;
304
- if (aVal < bVal)
305
- return -dir;
306
- if (aVal > bVal)
307
- return dir;
308
- return 0;
309
- });
310
- }
311
- // Apply pagination
312
- const offset = query.offset || 0;
313
- results = results.slice(offset, offset + query.limit);
314
- const duration = performance.now() - startTime;
315
- this.stats.queryCount++;
316
- this.stats.totalQueryTime += duration;
317
- return results;
318
- }
319
- /**
320
- * Semantic vector search
321
- */
322
- async search(embedding, options) {
323
- const startTime = performance.now();
324
- const indexResults = await this.index.search(embedding, options.k, options.ef);
325
- const results = [];
326
- for (const { id, distance } of indexResults) {
327
- const entry = this.entries.get(id);
328
- if (!entry)
329
- continue;
330
- // Apply threshold filter
331
- const score = 1 - distance; // Convert distance to similarity
332
- if (options.threshold && score < options.threshold)
333
- continue;
334
- // Apply additional filters if provided
335
- if (options.filters) {
336
- const filtered = this.applyFilters([entry], options.filters);
337
- if (filtered.length === 0)
338
- continue;
339
- }
340
- results.push({ entry, score, distance });
341
- }
342
- const duration = performance.now() - startTime;
343
- this.stats.searchCount++;
344
- this.stats.totalSearchTime += duration;
345
- return results;
346
- }
347
- /**
348
- * Bulk insert entries (OPTIMIZED: 2-3x faster with batched operations)
349
- *
350
- * Performance improvements:
351
- * - Parallel embedding generation
352
- * - Batched index updates
353
- * - Deferred cache population
354
- * - Single event emission
355
- */
356
- async bulkInsert(entries, options) {
357
- const startTime = performance.now();
358
- const batchSize = options?.batchSize || 100;
359
- // Phase 1: Generate embeddings in parallel batches
360
- if (this.config.embeddingGenerator) {
361
- const needsEmbedding = entries.filter(e => e.content && !e.embedding);
362
- for (let i = 0; i < needsEmbedding.length; i += batchSize) {
363
- const batch = needsEmbedding.slice(i, i + batchSize);
364
- await Promise.all(batch.map(async (entry) => {
365
- entry.embedding = await this.config.embeddingGenerator(entry.content);
366
- }));
367
- }
368
- }
369
- // Phase 2: Store all entries (skip individual cache updates)
370
- const embeddings = [];
371
- for (let entry of entries) {
372
- // Normalise namespace so indexes and stored object agree
373
- const namespace = entry.namespace || this.config.defaultNamespace;
374
- if (entry.namespace !== namespace) {
375
- entry = { ...entry, namespace };
376
- }
377
- // Store in main storage
378
- this.entries.set(entry.id, entry);
379
- // Update namespace index
380
- if (!this.namespaceIndex.has(namespace)) {
381
- this.namespaceIndex.set(namespace, new Set());
382
- }
383
- this.namespaceIndex.get(namespace).add(entry.id);
384
- // Update key index
385
- const keyIndexKey = `${namespace}:${entry.key}`;
386
- this.keyIndex.set(keyIndexKey, entry.id);
387
- // Update tag index
388
- for (const tag of entry.tags) {
389
- if (!this.tagIndex.has(tag)) {
390
- this.tagIndex.set(tag, new Set());
391
- }
392
- this.tagIndex.get(tag).add(entry.id);
393
- }
394
- // Collect embeddings for batch indexing
395
- if (entry.embedding) {
396
- embeddings.push({ id: entry.id, embedding: entry.embedding });
397
- }
398
- }
399
- // Phase 3: Batch index embeddings
400
- for (let i = 0; i < embeddings.length; i += batchSize) {
401
- const batch = embeddings.slice(i, i + batchSize);
402
- await Promise.all(batch.map(({ id, embedding }) => this.index.addPoint(id, embedding)));
403
- }
404
- // Phase 4: Batch cache update (only populate hot entries)
405
- if (this.config.cacheEnabled && entries.length <= this.config.cacheSize) {
406
- for (const entry of entries) {
407
- this.cache.set(entry.id, entry);
408
- }
409
- }
410
- const duration = performance.now() - startTime;
411
- this.stats.writeCount += entries.length;
412
- this.stats.totalWriteTime += duration;
413
- this.emit('bulk:inserted', { count: entries.length, duration, avgPerEntry: duration / entries.length });
414
- }
415
- /**
416
- * Bulk delete entries (OPTIMIZED: parallel deletion)
417
- */
418
- async bulkDelete(ids) {
419
- const startTime = performance.now();
420
- let deleted = 0;
421
- // Batch delete from cache first (fast)
422
- if (this.config.cacheEnabled) {
423
- for (const id of ids) {
424
- this.cache.delete(id);
425
- }
426
- }
427
- // Process deletions in parallel batches
428
- const batchSize = 100;
429
- for (let i = 0; i < ids.length; i += batchSize) {
430
- const batch = ids.slice(i, i + batchSize);
431
- const results = await Promise.all(batch.map(async (id) => {
432
- const entry = this.entries.get(id);
433
- if (!entry)
434
- return false;
435
- // Remove from main storage
436
- this.entries.delete(id);
437
- // Remove from namespace index
438
- this.namespaceIndex.get(entry.namespace)?.delete(id);
439
- // Remove from key index
440
- const keyIndexKey = `${entry.namespace}:${entry.key}`;
441
- this.keyIndex.delete(keyIndexKey);
442
- // Remove from tag index
443
- for (const tag of entry.tags) {
444
- this.tagIndex.get(tag)?.delete(id);
445
- }
446
- // Remove from vector index
447
- if (entry.embedding) {
448
- await this.index.removePoint(id);
449
- }
450
- return true;
451
- }));
452
- deleted += results.filter(Boolean).length;
453
- }
454
- const duration = performance.now() - startTime;
455
- this.emit('bulk:deleted', { count: deleted, duration });
456
- return deleted;
457
- }
458
- /**
459
- * Bulk get entries by IDs (OPTIMIZED: parallel fetch with cache)
460
- */
461
- async bulkGet(ids) {
462
- const results = new Map();
463
- const uncached = [];
464
- // Check cache first
465
- if (this.config.cacheEnabled) {
466
- for (const id of ids) {
467
- const cached = this.cache.get(id);
468
- if (cached) {
469
- results.set(id, cached);
470
- }
471
- else {
472
- uncached.push(id);
473
- }
474
- }
475
- }
476
- else {
477
- uncached.push(...ids);
478
- }
479
- // Fetch uncached entries
480
- for (const id of uncached) {
481
- const entry = this.entries.get(id) || null;
482
- results.set(id, entry);
483
- if (entry && this.config.cacheEnabled) {
484
- this.cache.set(id, entry);
485
- }
486
- }
487
- return results;
488
- }
489
- /**
490
- * Bulk update entries (OPTIMIZED: batched updates)
491
- */
492
- async bulkUpdate(updates) {
493
- const results = new Map();
494
- // Process updates in parallel
495
- await Promise.all(updates.map(async ({ id, update }) => {
496
- const updated = await this.update(id, update);
497
- results.set(id, updated);
498
- }));
499
- return results;
500
- }
501
- /**
502
- * Get entry count
503
- */
504
- async count(namespace) {
505
- if (namespace) {
506
- return this.namespaceIndex.get(namespace)?.size || 0;
507
- }
508
- return this.entries.size;
509
- }
510
- /**
511
- * List all namespaces
512
- */
513
- async listNamespaces() {
514
- return Array.from(this.namespaceIndex.keys());
515
- }
516
- /**
517
- * Clear all entries in a namespace
518
- */
519
- async clearNamespace(namespace) {
520
- const ids = this.namespaceIndex.get(namespace);
521
- if (!ids)
522
- return 0;
523
- let deleted = 0;
524
- for (const id of ids) {
525
- if (await this.delete(id)) {
526
- deleted++;
527
- }
528
- }
529
- return deleted;
530
- }
531
- /**
532
- * Get backend statistics
533
- */
534
- async getStats() {
535
- const entriesByNamespace = {};
536
- for (const [namespace, ids] of this.namespaceIndex) {
537
- entriesByNamespace[namespace] = ids.size;
538
- }
539
- const entriesByType = {
540
- episodic: 0,
541
- semantic: 0,
542
- procedural: 0,
543
- working: 0,
544
- cache: 0,
545
- };
546
- for (const entry of this.entries.values()) {
547
- entriesByType[entry.type]++;
548
- }
549
- return {
550
- totalEntries: this.entries.size,
551
- entriesByNamespace,
552
- entriesByType,
553
- memoryUsage: this.estimateMemoryUsage(),
554
- hnswStats: this.index.getStats(),
555
- cacheStats: this.cache.getStats(),
556
- avgQueryTime: this.stats.queryCount > 0
557
- ? this.stats.totalQueryTime / this.stats.queryCount
558
- : 0,
559
- avgSearchTime: this.stats.searchCount > 0
560
- ? this.stats.totalSearchTime / this.stats.searchCount
561
- : 0,
562
- };
563
- }
564
- /**
565
- * Perform health check
566
- */
567
- async healthCheck() {
568
- const issues = [];
569
- const recommendations = [];
570
- // Check storage health
571
- const storageHealth = this.checkStorageHealth(issues, recommendations);
572
- // Check index health
573
- const indexHealth = this.checkIndexHealth(issues, recommendations);
574
- // Check cache health
575
- const cacheHealth = this.checkCacheHealth(issues, recommendations);
576
- // Determine overall status
577
- let status = 'healthy';
578
- if (storageHealth.status === 'unhealthy' ||
579
- indexHealth.status === 'unhealthy' ||
580
- cacheHealth.status === 'unhealthy') {
581
- status = 'unhealthy';
582
- }
583
- else if (storageHealth.status === 'degraded' ||
584
- indexHealth.status === 'degraded' ||
585
- cacheHealth.status === 'degraded') {
586
- status = 'degraded';
587
- }
588
- return {
589
- status,
590
- components: {
591
- storage: storageHealth,
592
- index: indexHealth,
593
- cache: cacheHealth,
594
- },
595
- timestamp: Date.now(),
596
- issues,
597
- recommendations,
598
- };
599
- }
600
- // ===== Convenience Methods =====
601
- /**
602
- * Store a new entry from input
603
- */
604
- async storeEntry(input) {
605
- const entry = createDefaultEntry(input);
606
- await this.store(entry);
607
- return entry;
608
- }
609
- /**
610
- * Semantic search by content string
611
- */
612
- async semanticSearch(content, k = 10, threshold) {
613
- if (!this.config.embeddingGenerator) {
614
- throw new Error('Embedding generator not configured');
615
- }
616
- const embedding = await this.config.embeddingGenerator(content);
617
- return this.search(embedding, { k, threshold });
618
- }
619
- // ===== Private Methods =====
620
- queryByPrefix(query) {
621
- const results = [];
622
- const prefix = query.keyPrefix || '';
623
- const namespace = query.namespace || this.config.defaultNamespace;
624
- for (const [key, id] of this.keyIndex) {
625
- if (key.startsWith(`${namespace}:${prefix}`)) {
626
- const entry = this.entries.get(id);
627
- if (entry)
628
- results.push(entry);
629
- }
630
- }
631
- return results;
632
- }
633
- queryByTags(query) {
634
- if (!query.tags || query.tags.length === 0) {
635
- return Array.from(this.entries.values());
636
- }
637
- // Get intersection of entries for all tags
638
- let matchingIds = null;
639
- for (const tag of query.tags) {
640
- const tagIds = this.tagIndex.get(tag);
641
- if (!tagIds) {
642
- return []; // Tag doesn't exist
643
- }
644
- if (matchingIds === null) {
645
- matchingIds = new Set(tagIds);
646
- }
647
- else {
648
- // Intersect with previous results
649
- for (const id of matchingIds) {
650
- if (!tagIds.has(id)) {
651
- matchingIds.delete(id);
652
- }
653
- }
654
- }
655
- }
656
- if (!matchingIds)
657
- return [];
658
- const results = [];
659
- for (const id of matchingIds) {
660
- const entry = this.entries.get(id);
661
- if (entry)
662
- results.push(entry);
663
- }
664
- return results;
665
- }
666
- async querySemanticWithFilters(query) {
667
- if (!query.content && !query.embedding) {
668
- return this.queryWithFilters(query);
669
- }
670
- let embedding = query.embedding;
671
- if (!embedding && query.content && this.config.embeddingGenerator) {
672
- embedding = await this.config.embeddingGenerator(query.content);
673
- }
674
- if (!embedding) {
675
- return this.queryWithFilters(query);
676
- }
677
- const searchResults = await this.search(embedding, {
678
- k: query.limit * 2, // Over-fetch for filtering
679
- threshold: query.threshold,
680
- filters: query,
681
- });
682
- return searchResults.map((r) => r.entry);
683
- }
684
- queryWithFilters(query) {
685
- let entries = [];
686
- // Start with namespace filter if provided
687
- if (query.namespace) {
688
- const namespaceIds = this.namespaceIndex.get(query.namespace);
689
- if (!namespaceIds)
690
- return [];
691
- for (const id of namespaceIds) {
692
- const entry = this.entries.get(id);
693
- if (entry)
694
- entries.push(entry);
695
- }
696
- }
697
- else {
698
- entries = Array.from(this.entries.values());
699
- }
700
- return entries;
701
- }
702
- applyFilters(entries, query) {
703
- return entries.filter((entry) => {
704
- // Namespace filter
705
- if (query.namespace && entry.namespace !== query.namespace) {
706
- return false;
707
- }
708
- // Memory type filter
709
- if (query.memoryType && entry.type !== query.memoryType) {
710
- return false;
711
- }
712
- // Access level filter
713
- if (query.accessLevel && entry.accessLevel !== query.accessLevel) {
714
- return false;
715
- }
716
- // Owner filter
717
- if (query.ownerId && entry.ownerId !== query.ownerId) {
718
- return false;
719
- }
720
- // Tags filter
721
- if (query.tags && query.tags.length > 0) {
722
- if (!query.tags.every((tag) => entry.tags.includes(tag))) {
723
- return false;
724
- }
725
- }
726
- // Time range filters
727
- if (query.createdAfter && entry.createdAt < query.createdAfter) {
728
- return false;
729
- }
730
- if (query.createdBefore && entry.createdAt > query.createdBefore) {
731
- return false;
732
- }
733
- if (query.updatedAfter && entry.updatedAt < query.updatedAfter) {
734
- return false;
735
- }
736
- if (query.updatedBefore && entry.updatedAt > query.updatedBefore) {
737
- return false;
738
- }
739
- // Expiration filter
740
- if (!query.includeExpired && entry.expiresAt) {
741
- if (entry.expiresAt < Date.now()) {
742
- return false;
743
- }
744
- }
745
- // Metadata filters
746
- if (query.metadata) {
747
- for (const [key, value] of Object.entries(query.metadata)) {
748
- if (entry.metadata[key] !== value) {
749
- return false;
750
- }
751
- }
752
- }
753
- return true;
754
- });
755
- }
756
- updateAccessStats(entry) {
757
- entry.accessCount++;
758
- entry.lastAccessedAt = Date.now();
759
- }
760
- estimateMemoryUsage() {
761
- let total = 0;
762
- // Estimate entry storage
763
- for (const entry of this.entries.values()) {
764
- total += this.estimateEntrySize(entry);
765
- }
766
- // Add index memory
767
- total += this.index.getStats().memoryUsage;
768
- // Add cache memory
769
- total += this.cache.getStats().memoryUsage;
770
- return total;
771
- }
772
- estimateEntrySize(entry) {
773
- let size = 0;
774
- // Base object overhead
775
- size += 100;
776
- // String fields
777
- size += (entry.id.length + entry.key.length + entry.content.length) * 2;
778
- // Embedding (Float32Array)
779
- if (entry.embedding) {
780
- size += entry.embedding.length * 4;
781
- }
782
- // Tags and references
783
- size += entry.tags.join('').length * 2;
784
- size += entry.references.join('').length * 2;
785
- // Metadata (rough estimate)
786
- size += JSON.stringify(entry.metadata).length * 2;
787
- return size;
788
- }
789
- checkStorageHealth(issues, recommendations) {
790
- const utilizationPercent = (this.entries.size / this.config.maxEntries) * 100;
791
- if (utilizationPercent > 95) {
792
- issues.push('Storage utilization critical (>95%)');
793
- recommendations.push('Increase maxEntries or cleanup old data');
794
- return { status: 'unhealthy', latency: 0, message: 'Storage near capacity' };
795
- }
796
- if (utilizationPercent > 80) {
797
- issues.push('Storage utilization high (>80%)');
798
- recommendations.push('Consider cleanup or capacity increase');
799
- return { status: 'degraded', latency: 0, message: 'Storage utilization high' };
800
- }
801
- return { status: 'healthy', latency: 0 };
802
- }
803
- checkIndexHealth(issues, recommendations) {
804
- const stats = this.index.getStats();
805
- if (stats.avgSearchTime > 10) {
806
- issues.push('Index search time degraded (>10ms)');
807
- recommendations.push('Consider rebuilding index or increasing ef');
808
- return { status: 'degraded', latency: stats.avgSearchTime };
809
- }
810
- return { status: 'healthy', latency: stats.avgSearchTime };
811
- }
812
- checkCacheHealth(issues, recommendations) {
813
- const stats = this.cache.getStats();
814
- if (stats.hitRate < 0.5) {
815
- issues.push('Cache hit rate low (<50%)');
816
- recommendations.push('Consider increasing cache size');
817
- return {
818
- status: 'degraded',
819
- latency: 0,
820
- message: `Hit rate: ${(stats.hitRate * 100).toFixed(1)}%`,
821
- };
822
- }
823
- return { status: 'healthy', latency: 0 };
824
- }
825
- async loadFromDisk() {
826
- // Placeholder for persistence implementation
827
- // Would use SQLite or file-based storage
828
- this.emit('persistence:loaded');
829
- }
830
- async saveToDisk() {
831
- // Placeholder for persistence implementation
832
- this.emit('persistence:saved');
833
- }
834
- }
835
- export default AgentDBAdapter;
836
- //# sourceMappingURL=agentdb-adapter.js.map