@aitytech/agentkits-memory 1.0.1 → 2.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.
- package/README.md +54 -5
- package/dist/better-sqlite3-backend.d.ts +192 -0
- package/dist/better-sqlite3-backend.d.ts.map +1 -0
- package/dist/better-sqlite3-backend.js +801 -0
- package/dist/better-sqlite3-backend.js.map +1 -0
- package/dist/cli/save.js +0 -0
- package/dist/cli/setup.d.ts +6 -2
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +289 -42
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/viewer.js +25 -56
- package/dist/cli/viewer.js.map +1 -1
- package/dist/cli/web-viewer.d.ts +2 -1
- package/dist/cli/web-viewer.d.ts.map +1 -1
- package/dist/cli/web-viewer.js +791 -141
- package/dist/cli/web-viewer.js.map +1 -1
- package/dist/embeddings/embedding-cache.d.ts +131 -0
- package/dist/embeddings/embedding-cache.d.ts.map +1 -0
- package/dist/embeddings/embedding-cache.js +217 -0
- package/dist/embeddings/embedding-cache.js.map +1 -0
- package/dist/embeddings/index.d.ts +11 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +11 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/local-embeddings.d.ts +140 -0
- package/dist/embeddings/local-embeddings.d.ts.map +1 -0
- package/dist/embeddings/local-embeddings.js +293 -0
- package/dist/embeddings/local-embeddings.js.map +1 -0
- package/dist/hooks/context.d.ts +6 -1
- package/dist/hooks/context.d.ts.map +1 -1
- package/dist/hooks/context.js +12 -2
- package/dist/hooks/context.js.map +1 -1
- package/dist/hooks/observation.d.ts +6 -1
- package/dist/hooks/observation.d.ts.map +1 -1
- package/dist/hooks/observation.js +12 -2
- package/dist/hooks/observation.js.map +1 -1
- package/dist/hooks/service.d.ts +1 -6
- package/dist/hooks/service.d.ts.map +1 -1
- package/dist/hooks/service.js +33 -85
- package/dist/hooks/service.js.map +1 -1
- package/dist/hooks/session-init.d.ts +6 -1
- package/dist/hooks/session-init.d.ts.map +1 -1
- package/dist/hooks/session-init.js +12 -2
- package/dist/hooks/session-init.js.map +1 -1
- package/dist/hooks/summarize.d.ts +6 -1
- package/dist/hooks/summarize.d.ts.map +1 -1
- package/dist/hooks/summarize.js +12 -2
- package/dist/hooks/summarize.js.map +1 -1
- package/dist/index.d.ts +10 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +172 -94
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +17 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/migration.js +3 -3
- package/dist/migration.js.map +1 -1
- package/dist/search/hybrid-search.d.ts +262 -0
- package/dist/search/hybrid-search.d.ts.map +1 -0
- package/dist/search/hybrid-search.js +688 -0
- package/dist/search/hybrid-search.js.map +1 -0
- package/dist/search/index.d.ts +13 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +13 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/token-economics.d.ts +161 -0
- package/dist/search/token-economics.d.ts.map +1 -0
- package/dist/search/token-economics.js +239 -0
- package/dist/search/token-economics.js.map +1 -0
- package/dist/types.d.ts +0 -68
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +6 -4
- package/src/__tests__/better-sqlite3-backend.test.ts +1466 -0
- package/src/__tests__/cache-manager.test.ts +499 -0
- package/src/__tests__/embedding-integration.test.ts +481 -0
- package/src/__tests__/hnsw-index.test.ts +727 -0
- package/src/__tests__/index.test.ts +432 -0
- package/src/better-sqlite3-backend.ts +1000 -0
- package/src/cli/setup.ts +358 -47
- package/src/cli/viewer.ts +28 -63
- package/src/cli/web-viewer.ts +936 -182
- package/src/embeddings/__tests__/embedding-cache.test.ts +269 -0
- package/src/embeddings/__tests__/local-embeddings.test.ts +495 -0
- package/src/embeddings/embedding-cache.ts +318 -0
- package/src/embeddings/index.ts +20 -0
- package/src/embeddings/local-embeddings.ts +419 -0
- package/src/hooks/__tests__/handlers.test.ts +58 -17
- package/src/hooks/__tests__/integration.test.ts +77 -26
- package/src/hooks/context.ts +13 -2
- package/src/hooks/observation.ts +13 -2
- package/src/hooks/service.ts +39 -100
- package/src/hooks/session-init.ts +13 -2
- package/src/hooks/summarize.ts +13 -2
- package/src/index.ts +210 -116
- package/src/mcp/server.ts +20 -3
- package/src/search/__tests__/hybrid-search.test.ts +669 -0
- package/src/search/__tests__/token-economics.test.ts +276 -0
- package/src/search/hybrid-search.ts +968 -0
- package/src/search/index.ts +29 -0
- package/src/search/token-economics.ts +367 -0
- package/src/types.ts +0 -96
- package/src/__tests__/sqljs-backend.test.ts +0 -410
- package/src/migration.ts +0 -574
- package/src/sql.js.d.ts +0 -70
- package/src/sqljs-backend.ts +0 -789
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
import type { Database as BetterDatabase } from 'better-sqlite3';
|
|
4
|
+
import {
|
|
5
|
+
PersistentEmbeddingCache,
|
|
6
|
+
createPersistentEmbeddingCache,
|
|
7
|
+
} from '../embedding-cache.js';
|
|
8
|
+
|
|
9
|
+
describe('PersistentEmbeddingCache', () => {
|
|
10
|
+
let db: BetterDatabase;
|
|
11
|
+
let cache: PersistentEmbeddingCache;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
db = new Database(':memory:');
|
|
15
|
+
cache = new PersistentEmbeddingCache(db);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
db.close();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('basic operations', () => {
|
|
23
|
+
it('should store and retrieve embeddings', () => {
|
|
24
|
+
const embedding = new Float32Array([0.1, 0.2, 0.3, 0.4, 0.5]);
|
|
25
|
+
cache.set('Test content', embedding);
|
|
26
|
+
|
|
27
|
+
const retrieved = cache.get('Test content');
|
|
28
|
+
|
|
29
|
+
expect(retrieved).not.toBeNull();
|
|
30
|
+
expect(retrieved?.length).toBe(5);
|
|
31
|
+
for (let i = 0; i < embedding.length; i++) {
|
|
32
|
+
expect(retrieved![i]).toBeCloseTo(embedding[i], 5);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should return null for missing content', () => {
|
|
37
|
+
const retrieved = cache.get('Non-existent content');
|
|
38
|
+
expect(retrieved).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should check if content exists', () => {
|
|
42
|
+
const embedding = new Float32Array([0.1, 0.2, 0.3]);
|
|
43
|
+
cache.set('Existing content', embedding);
|
|
44
|
+
|
|
45
|
+
expect(cache.has('Existing content')).toBe(true);
|
|
46
|
+
expect(cache.has('Missing content')).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should overwrite existing embeddings', () => {
|
|
50
|
+
const embedding1 = new Float32Array([0.1, 0.2, 0.3]);
|
|
51
|
+
const embedding2 = new Float32Array([0.4, 0.5, 0.6]);
|
|
52
|
+
|
|
53
|
+
cache.set('Content', embedding1);
|
|
54
|
+
cache.set('Content', embedding2);
|
|
55
|
+
|
|
56
|
+
const retrieved = cache.get('Content');
|
|
57
|
+
for (let i = 0; i < embedding2.length; i++) {
|
|
58
|
+
expect(retrieved![i]).toBeCloseTo(embedding2[i], 5);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should clear all entries', () => {
|
|
63
|
+
cache.set('Content 1', new Float32Array([0.1]));
|
|
64
|
+
cache.set('Content 2', new Float32Array([0.2]));
|
|
65
|
+
|
|
66
|
+
cache.clear();
|
|
67
|
+
|
|
68
|
+
expect(cache.get('Content 1')).toBeNull();
|
|
69
|
+
expect(cache.get('Content 2')).toBeNull();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('expiration', () => {
|
|
74
|
+
it('should expire entries after TTL', async () => {
|
|
75
|
+
const shortTtlCache = new PersistentEmbeddingCache(db, { ttlMs: 100 });
|
|
76
|
+
shortTtlCache.set('Expiring content', new Float32Array([0.1, 0.2]));
|
|
77
|
+
|
|
78
|
+
// Should exist immediately
|
|
79
|
+
expect(shortTtlCache.get('Expiring content')).not.toBeNull();
|
|
80
|
+
|
|
81
|
+
// Wait for expiration
|
|
82
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
83
|
+
|
|
84
|
+
// Should be expired
|
|
85
|
+
expect(shortTtlCache.get('Expiring content')).toBeNull();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should evict expired entries', async () => {
|
|
89
|
+
const shortTtlCache = new PersistentEmbeddingCache(db, { ttlMs: 50 });
|
|
90
|
+
shortTtlCache.set('Expiring', new Float32Array([0.1]));
|
|
91
|
+
|
|
92
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
93
|
+
|
|
94
|
+
const evicted = shortTtlCache.evictExpired();
|
|
95
|
+
expect(evicted).toBe(1);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('capacity management', () => {
|
|
100
|
+
it('should evict entries when over capacity', () => {
|
|
101
|
+
const smallCache = new PersistentEmbeddingCache(db, { maxSize: 5 });
|
|
102
|
+
|
|
103
|
+
// Add 5 entries (at capacity)
|
|
104
|
+
for (let i = 1; i <= 5; i++) {
|
|
105
|
+
smallCache.set(`Entry ${i}`, new Float32Array([i * 0.1]));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Verify all 5 exist
|
|
109
|
+
const stats1 = smallCache.getStats();
|
|
110
|
+
expect(stats1.size).toBe(5);
|
|
111
|
+
|
|
112
|
+
// Add 2 more entries, should trigger eviction
|
|
113
|
+
smallCache.set('Entry 6', new Float32Array([0.6]));
|
|
114
|
+
smallCache.set('Entry 7', new Float32Array([0.7]));
|
|
115
|
+
|
|
116
|
+
// New entries should exist
|
|
117
|
+
expect(smallCache.get('Entry 6')).not.toBeNull();
|
|
118
|
+
expect(smallCache.get('Entry 7')).not.toBeNull();
|
|
119
|
+
|
|
120
|
+
// Size should not exceed maxSize
|
|
121
|
+
const stats2 = smallCache.getStats();
|
|
122
|
+
expect(stats2.size).toBeLessThanOrEqual(5);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('statistics', () => {
|
|
127
|
+
it('should track hits and misses', () => {
|
|
128
|
+
cache.set('Content', new Float32Array([0.1, 0.2, 0.3]));
|
|
129
|
+
|
|
130
|
+
cache.get('Content'); // Hit
|
|
131
|
+
cache.get('Content'); // Hit
|
|
132
|
+
cache.get('Missing'); // Miss
|
|
133
|
+
|
|
134
|
+
const stats = cache.getStats();
|
|
135
|
+
|
|
136
|
+
expect(stats.hits).toBe(2);
|
|
137
|
+
expect(stats.misses).toBe(1);
|
|
138
|
+
expect(stats.hitRate).toBeCloseTo(2 / 3);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should track size and bytes', () => {
|
|
142
|
+
cache.set('Content 1', new Float32Array([0.1, 0.2, 0.3])); // 12 bytes
|
|
143
|
+
cache.set('Content 2', new Float32Array([0.4, 0.5])); // 8 bytes
|
|
144
|
+
|
|
145
|
+
const stats = cache.getStats();
|
|
146
|
+
|
|
147
|
+
expect(stats.size).toBe(2);
|
|
148
|
+
expect(stats.bytesUsed).toBeGreaterThan(0);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should reset stats on clear', () => {
|
|
152
|
+
cache.set('Content', new Float32Array([0.1]));
|
|
153
|
+
cache.get('Content');
|
|
154
|
+
|
|
155
|
+
cache.clear();
|
|
156
|
+
|
|
157
|
+
const stats = cache.getStats();
|
|
158
|
+
expect(stats.hits).toBe(0);
|
|
159
|
+
expect(stats.misses).toBe(0);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('getAllEmbeddings', () => {
|
|
164
|
+
it('should return all non-expired embeddings', () => {
|
|
165
|
+
cache.set('Content 1', new Float32Array([0.1, 0.2]));
|
|
166
|
+
cache.set('Content 2', new Float32Array([0.3, 0.4]));
|
|
167
|
+
cache.set('Content 3', new Float32Array([0.5, 0.6]));
|
|
168
|
+
|
|
169
|
+
const all = cache.getAllEmbeddings();
|
|
170
|
+
|
|
171
|
+
expect(all.length).toBe(3);
|
|
172
|
+
all.forEach((item) => {
|
|
173
|
+
expect(item.hash).toBeDefined();
|
|
174
|
+
expect(item.embedding).toBeInstanceOf(Float32Array);
|
|
175
|
+
expect(item.embedding.length).toBe(2);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should not include expired embeddings', async () => {
|
|
180
|
+
const shortTtlCache = new PersistentEmbeddingCache(db, { ttlMs: 50 });
|
|
181
|
+
shortTtlCache.set('Expiring', new Float32Array([0.1]));
|
|
182
|
+
shortTtlCache.set('Not expiring', new Float32Array([0.2]));
|
|
183
|
+
|
|
184
|
+
// Set longer TTL for second entry
|
|
185
|
+
const longTtlCache = new PersistentEmbeddingCache(db, { ttlMs: 10000 });
|
|
186
|
+
longTtlCache.set('Long TTL', new Float32Array([0.3]));
|
|
187
|
+
|
|
188
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
189
|
+
|
|
190
|
+
const all = longTtlCache.getAllEmbeddings();
|
|
191
|
+
|
|
192
|
+
// Only non-expired entries should be returned
|
|
193
|
+
expect(all.length).toBe(1);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('createPersistentEmbeddingCache factory', () => {
|
|
198
|
+
it('should create cache with default config', () => {
|
|
199
|
+
const cache = createPersistentEmbeddingCache(db);
|
|
200
|
+
expect(cache).toBeInstanceOf(PersistentEmbeddingCache);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should accept custom config', () => {
|
|
204
|
+
const cache = createPersistentEmbeddingCache(db, {
|
|
205
|
+
maxSize: 500,
|
|
206
|
+
ttlMs: 1000,
|
|
207
|
+
dimensions: 768,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
expect(cache).toBeInstanceOf(PersistentEmbeddingCache);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('content hashing', () => {
|
|
215
|
+
it('should use consistent hashing', () => {
|
|
216
|
+
const embedding = new Float32Array([0.1, 0.2, 0.3]);
|
|
217
|
+
|
|
218
|
+
cache.set('Same content', embedding);
|
|
219
|
+
|
|
220
|
+
// Different cache instance, same DB
|
|
221
|
+
const cache2 = new PersistentEmbeddingCache(db);
|
|
222
|
+
const retrieved = cache2.get('Same content');
|
|
223
|
+
|
|
224
|
+
expect(retrieved).not.toBeNull();
|
|
225
|
+
for (let i = 0; i < embedding.length; i++) {
|
|
226
|
+
expect(retrieved![i]).toBeCloseTo(embedding[i], 5);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should differentiate similar content', () => {
|
|
231
|
+
const embeddingA = new Float32Array([0.1]);
|
|
232
|
+
const embeddingB = new Float32Array([0.2]);
|
|
233
|
+
cache.set('Content A', embeddingA);
|
|
234
|
+
cache.set('Content B', embeddingB);
|
|
235
|
+
|
|
236
|
+
expect(cache.get('Content A')![0]).toBeCloseTo(0.1, 5);
|
|
237
|
+
expect(cache.get('Content B')![0]).toBeCloseTo(0.2, 5);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe('large embeddings', () => {
|
|
242
|
+
it('should handle 384-dimension embeddings', () => {
|
|
243
|
+
const embedding = new Float32Array(384);
|
|
244
|
+
for (let i = 0; i < 384; i++) {
|
|
245
|
+
embedding[i] = Math.random();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
cache.set('Large embedding', embedding);
|
|
249
|
+
const retrieved = cache.get('Large embedding');
|
|
250
|
+
|
|
251
|
+
expect(retrieved?.length).toBe(384);
|
|
252
|
+
for (let i = 0; i < 384; i++) {
|
|
253
|
+
expect(retrieved![i]).toBeCloseTo(embedding[i], 5);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('should handle 768-dimension embeddings', () => {
|
|
258
|
+
const embedding = new Float32Array(768);
|
|
259
|
+
for (let i = 0; i < 768; i++) {
|
|
260
|
+
embedding[i] = Math.random();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
cache.set('Larger embedding', embedding);
|
|
264
|
+
const retrieved = cache.get('Larger embedding');
|
|
265
|
+
|
|
266
|
+
expect(retrieved?.length).toBe(768);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
});
|