agentic-flow 2.0.1-alpha.17 → 2.0.1-alpha.18

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,105 @@
1
+ /**
2
+ * EmbeddingCache - Persistent SQLite cache for embeddings
3
+ *
4
+ * Makes ONNX embeddings practical by caching across sessions:
5
+ * - First embed: ~400ms (ONNX inference)
6
+ * - Cached embed: ~0.1ms (SQLite lookup)
7
+ *
8
+ * Storage: ~/.agentic-flow/embedding-cache.db
9
+ */
10
+ export interface CacheStats {
11
+ totalEntries: number;
12
+ hits: number;
13
+ misses: number;
14
+ hitRate: number;
15
+ dbSizeBytes: number;
16
+ oldestEntry: number;
17
+ newestEntry: number;
18
+ }
19
+ export interface CacheConfig {
20
+ maxEntries?: number;
21
+ maxAgeDays?: number;
22
+ dbPath?: string;
23
+ dimension?: number;
24
+ }
25
+ export declare class EmbeddingCache {
26
+ private db;
27
+ private config;
28
+ private hits;
29
+ private misses;
30
+ private stmtGet;
31
+ private stmtInsert;
32
+ private stmtUpdateHits;
33
+ private stmtCount;
34
+ private stmtEvictOld;
35
+ private stmtEvictLRU;
36
+ constructor(config?: CacheConfig);
37
+ private initSchema;
38
+ private prepareStatements;
39
+ /**
40
+ * Generate hash key for text + model combination
41
+ */
42
+ private hashKey;
43
+ /**
44
+ * Get embedding from cache
45
+ * Returns null if not found
46
+ */
47
+ get(text: string, model?: string): Float32Array | null;
48
+ /**
49
+ * Store embedding in cache
50
+ */
51
+ set(text: string, embedding: Float32Array, model?: string): void;
52
+ /**
53
+ * Check if text is cached
54
+ */
55
+ has(text: string, model?: string): boolean;
56
+ /**
57
+ * Get multiple embeddings at once
58
+ * Returns map of text -> embedding (only cached ones)
59
+ */
60
+ getMany(texts: string[], model?: string): Map<string, Float32Array>;
61
+ /**
62
+ * Store multiple embeddings at once
63
+ */
64
+ setMany(entries: Array<{
65
+ text: string;
66
+ embedding: Float32Array;
67
+ }>, model?: string): void;
68
+ /**
69
+ * Evict old or LRU entries if over limit
70
+ */
71
+ private maybeEvict;
72
+ /**
73
+ * Cleanup entries older than maxAgeDays
74
+ */
75
+ private cleanupOldEntries;
76
+ /**
77
+ * Get cache statistics
78
+ */
79
+ getStats(): CacheStats;
80
+ /**
81
+ * Clear all cached embeddings
82
+ */
83
+ clear(): void;
84
+ /**
85
+ * Clear embeddings for a specific model
86
+ */
87
+ clearModel(model: string): void;
88
+ /**
89
+ * Vacuum database to reclaim space
90
+ */
91
+ vacuum(): void;
92
+ /**
93
+ * Close database connection
94
+ */
95
+ close(): void;
96
+ }
97
+ /**
98
+ * Get the singleton embedding cache
99
+ */
100
+ export declare function getEmbeddingCache(config?: CacheConfig): EmbeddingCache;
101
+ /**
102
+ * Reset the cache singleton (for testing)
103
+ */
104
+ export declare function resetEmbeddingCache(): void;
105
+ //# sourceMappingURL=EmbeddingCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmbeddingCache.d.ts","sourceRoot":"","sources":["../../src/intelligence/EmbeddingCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD,qBAAa,cAAc;IACzB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,MAAM,CAAa;IAG3B,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,cAAc,CAAsB;IAC5C,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,YAAY,CAAsB;gBAE9B,MAAM,GAAE,WAAgB;IAoBpC,OAAO,CAAC,UAAU;IAmBlB,OAAO,CAAC,iBAAiB;IA2BzB;;OAEG;IACH,OAAO,CAAC,OAAO;IAIf;;;OAGG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,MAAkB,GAAG,YAAY,GAAG,IAAI;IAgBjE;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,GAAE,MAAkB,GAAG,IAAI;IAa3E;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,MAAkB,GAAG,OAAO;IAMrD;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,GAAE,MAAkB,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC;IAa9E;;OAEG;IACH,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,YAAY,CAAA;KAAE,CAAC,EAAE,KAAK,GAAE,MAAkB,GAAG,IAAI;IAcnG;;OAEG;IACH,OAAO,CAAC,UAAU;IAUlB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAKzB;;OAEG;IACH,QAAQ,IAAI,UAAU;IA2BtB;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI/B;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAKD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,cAAc,CAKtE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C"}
@@ -0,0 +1,253 @@
1
+ /**
2
+ * EmbeddingCache - Persistent SQLite cache for embeddings
3
+ *
4
+ * Makes ONNX embeddings practical by caching across sessions:
5
+ * - First embed: ~400ms (ONNX inference)
6
+ * - Cached embed: ~0.1ms (SQLite lookup)
7
+ *
8
+ * Storage: ~/.agentic-flow/embedding-cache.db
9
+ */
10
+ import Database from 'better-sqlite3';
11
+ import { existsSync, mkdirSync } from 'fs';
12
+ import { join } from 'path';
13
+ import { homedir } from 'os';
14
+ import { createHash } from 'crypto';
15
+ // Default config
16
+ const DEFAULT_CONFIG = {
17
+ maxEntries: 10000,
18
+ maxAgeDays: 30,
19
+ dbPath: join(homedir(), '.agentic-flow', 'embedding-cache.db'),
20
+ dimension: 384,
21
+ };
22
+ export class EmbeddingCache {
23
+ db;
24
+ config;
25
+ hits = 0;
26
+ misses = 0;
27
+ // Prepared statements for performance
28
+ stmtGet;
29
+ stmtInsert;
30
+ stmtUpdateHits;
31
+ stmtCount;
32
+ stmtEvictOld;
33
+ stmtEvictLRU;
34
+ constructor(config = {}) {
35
+ this.config = { ...DEFAULT_CONFIG, ...config };
36
+ // Ensure directory exists
37
+ const dir = join(homedir(), '.agentic-flow');
38
+ if (!existsSync(dir)) {
39
+ mkdirSync(dir, { recursive: true });
40
+ }
41
+ // Open database with WAL mode for better concurrency
42
+ this.db = new Database(this.config.dbPath);
43
+ this.db.pragma('journal_mode = WAL');
44
+ this.db.pragma('synchronous = NORMAL');
45
+ this.db.pragma('cache_size = 10000');
46
+ this.initSchema();
47
+ this.prepareStatements();
48
+ this.cleanupOldEntries();
49
+ }
50
+ initSchema() {
51
+ this.db.exec(`
52
+ CREATE TABLE IF NOT EXISTS embeddings (
53
+ hash TEXT PRIMARY KEY,
54
+ text TEXT NOT NULL,
55
+ embedding BLOB NOT NULL,
56
+ dimension INTEGER NOT NULL,
57
+ model TEXT NOT NULL,
58
+ hits INTEGER DEFAULT 1,
59
+ created_at INTEGER NOT NULL,
60
+ last_accessed INTEGER NOT NULL
61
+ );
62
+
63
+ CREATE INDEX IF NOT EXISTS idx_last_accessed ON embeddings(last_accessed);
64
+ CREATE INDEX IF NOT EXISTS idx_created_at ON embeddings(created_at);
65
+ CREATE INDEX IF NOT EXISTS idx_model ON embeddings(model);
66
+ `);
67
+ }
68
+ prepareStatements() {
69
+ this.stmtGet = this.db.prepare(`
70
+ SELECT embedding, dimension FROM embeddings WHERE hash = ?
71
+ `);
72
+ this.stmtInsert = this.db.prepare(`
73
+ INSERT OR REPLACE INTO embeddings (hash, text, embedding, dimension, model, hits, created_at, last_accessed)
74
+ VALUES (?, ?, ?, ?, ?, 1, ?, ?)
75
+ `);
76
+ this.stmtUpdateHits = this.db.prepare(`
77
+ UPDATE embeddings SET hits = hits + 1, last_accessed = ? WHERE hash = ?
78
+ `);
79
+ this.stmtCount = this.db.prepare(`SELECT COUNT(*) as count FROM embeddings`);
80
+ this.stmtEvictOld = this.db.prepare(`
81
+ DELETE FROM embeddings WHERE created_at < ?
82
+ `);
83
+ this.stmtEvictLRU = this.db.prepare(`
84
+ DELETE FROM embeddings WHERE hash IN (
85
+ SELECT hash FROM embeddings ORDER BY last_accessed ASC LIMIT ?
86
+ )
87
+ `);
88
+ }
89
+ /**
90
+ * Generate hash key for text + model combination
91
+ */
92
+ hashKey(text, model = 'default') {
93
+ return createHash('sha256').update(`${model}:${text}`).digest('hex').slice(0, 32);
94
+ }
95
+ /**
96
+ * Get embedding from cache
97
+ * Returns null if not found
98
+ */
99
+ get(text, model = 'default') {
100
+ const hash = this.hashKey(text, model);
101
+ const row = this.stmtGet.get(hash);
102
+ if (row) {
103
+ this.hits++;
104
+ // Update access time and hit count
105
+ this.stmtUpdateHits.run(Date.now(), hash);
106
+ // Convert Buffer back to Float32Array
107
+ return new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.dimension);
108
+ }
109
+ this.misses++;
110
+ return null;
111
+ }
112
+ /**
113
+ * Store embedding in cache
114
+ */
115
+ set(text, embedding, model = 'default') {
116
+ const hash = this.hashKey(text, model);
117
+ const now = Date.now();
118
+ // Convert Float32Array to Buffer
119
+ const buffer = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
120
+ this.stmtInsert.run(hash, text, buffer, embedding.length, model, now, now);
121
+ // Check if we need to evict
122
+ this.maybeEvict();
123
+ }
124
+ /**
125
+ * Check if text is cached
126
+ */
127
+ has(text, model = 'default') {
128
+ const hash = this.hashKey(text, model);
129
+ const row = this.stmtGet.get(hash);
130
+ return row !== undefined;
131
+ }
132
+ /**
133
+ * Get multiple embeddings at once
134
+ * Returns map of text -> embedding (only cached ones)
135
+ */
136
+ getMany(texts, model = 'default') {
137
+ const result = new Map();
138
+ for (const text of texts) {
139
+ const embedding = this.get(text, model);
140
+ if (embedding) {
141
+ result.set(text, embedding);
142
+ }
143
+ }
144
+ return result;
145
+ }
146
+ /**
147
+ * Store multiple embeddings at once
148
+ */
149
+ setMany(entries, model = 'default') {
150
+ const insertMany = this.db.transaction((items) => {
151
+ const now = Date.now();
152
+ for (const { text, embedding } of items) {
153
+ const hash = this.hashKey(text, model);
154
+ const buffer = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
155
+ this.stmtInsert.run(hash, text, buffer, embedding.length, model, now, now);
156
+ }
157
+ });
158
+ insertMany(entries);
159
+ this.maybeEvict();
160
+ }
161
+ /**
162
+ * Evict old or LRU entries if over limit
163
+ */
164
+ maybeEvict() {
165
+ const count = this.stmtCount.get().count;
166
+ if (count > this.config.maxEntries) {
167
+ // Evict 10% of entries (LRU)
168
+ const toEvict = Math.ceil(this.config.maxEntries * 0.1);
169
+ this.stmtEvictLRU.run(toEvict);
170
+ }
171
+ }
172
+ /**
173
+ * Cleanup entries older than maxAgeDays
174
+ */
175
+ cleanupOldEntries() {
176
+ const cutoff = Date.now() - (this.config.maxAgeDays * 24 * 60 * 60 * 1000);
177
+ this.stmtEvictOld.run(cutoff);
178
+ }
179
+ /**
180
+ * Get cache statistics
181
+ */
182
+ getStats() {
183
+ const count = this.stmtCount.get().count;
184
+ const oldest = this.db.prepare(`SELECT MIN(created_at) as oldest FROM embeddings`).get();
185
+ const newest = this.db.prepare(`SELECT MAX(created_at) as newest FROM embeddings`).get();
186
+ // Get database file size
187
+ let dbSizeBytes = 0;
188
+ try {
189
+ const fs = require('fs');
190
+ const stats = fs.statSync(this.config.dbPath);
191
+ dbSizeBytes = stats.size;
192
+ }
193
+ catch {
194
+ // Ignore
195
+ }
196
+ return {
197
+ totalEntries: count,
198
+ hits: this.hits,
199
+ misses: this.misses,
200
+ hitRate: this.hits + this.misses > 0 ? this.hits / (this.hits + this.misses) : 0,
201
+ dbSizeBytes,
202
+ oldestEntry: oldest.oldest || 0,
203
+ newestEntry: newest.newest || 0,
204
+ };
205
+ }
206
+ /**
207
+ * Clear all cached embeddings
208
+ */
209
+ clear() {
210
+ this.db.exec('DELETE FROM embeddings');
211
+ this.hits = 0;
212
+ this.misses = 0;
213
+ }
214
+ /**
215
+ * Clear embeddings for a specific model
216
+ */
217
+ clearModel(model) {
218
+ this.db.prepare('DELETE FROM embeddings WHERE model = ?').run(model);
219
+ }
220
+ /**
221
+ * Vacuum database to reclaim space
222
+ */
223
+ vacuum() {
224
+ this.db.exec('VACUUM');
225
+ }
226
+ /**
227
+ * Close database connection
228
+ */
229
+ close() {
230
+ this.db.close();
231
+ }
232
+ }
233
+ // Singleton instance
234
+ let cacheInstance = null;
235
+ /**
236
+ * Get the singleton embedding cache
237
+ */
238
+ export function getEmbeddingCache(config) {
239
+ if (!cacheInstance) {
240
+ cacheInstance = new EmbeddingCache(config);
241
+ }
242
+ return cacheInstance;
243
+ }
244
+ /**
245
+ * Reset the cache singleton (for testing)
246
+ */
247
+ export function resetEmbeddingCache() {
248
+ if (cacheInstance) {
249
+ cacheInstance.close();
250
+ cacheInstance = null;
251
+ }
252
+ }
253
+ //# sourceMappingURL=EmbeddingCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmbeddingCache.js","sourceRoot":"","sources":["../../src/intelligence/EmbeddingCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAmBpC,iBAAiB;AACjB,MAAM,cAAc,GAA0B;IAC5C,UAAU,EAAE,KAAK;IACjB,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,oBAAoB,CAAC;IAC9D,SAAS,EAAE,GAAG;CACf,CAAC;AAEF,MAAM,OAAO,cAAc;IACjB,EAAE,CAAoB;IACtB,MAAM,CAAwB;IAC9B,IAAI,GAAW,CAAC,CAAC;IACjB,MAAM,GAAW,CAAC,CAAC;IAE3B,sCAAsC;IAC9B,OAAO,CAAsB;IAC7B,UAAU,CAAsB;IAChC,cAAc,CAAsB;IACpC,SAAS,CAAsB;IAC/B,YAAY,CAAsB;IAClC,YAAY,CAAsB;IAE1C,YAAY,SAAsB,EAAE;QAClC,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;QAE/C,0BAA0B;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC,CAAC;QAC7C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;QAED,qDAAqD;QACrD,IAAI,CAAC,EAAE,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACrC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACvC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAErC,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAEO,UAAU;QAChB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;KAeZ,CAAC,CAAC;IACL,CAAC;IAEO,iBAAiB;QACvB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;KAE9B,CAAC,CAAC;QAEH,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;KAGjC,CAAC,CAAC;QAEH,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;KAErC,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC;QAE7E,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;KAEnC,CAAC,CAAC;QAEH,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;;KAInC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,OAAO,CAAC,IAAY,EAAE,QAAgB,SAAS;QACrD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpF,CAAC;IAED;;;OAGG;IACH,GAAG,CAAC,IAAY,EAAE,QAAgB,SAAS;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAyD,CAAC;QAE3F,IAAI,GAAG,EAAE,CAAC;YACR,IAAI,CAAC,IAAI,EAAE,CAAC;YACZ,mCAAmC;YACnC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;YAC1C,sCAAsC;YACtC,OAAO,IAAI,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC,UAAU,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;QACzF,CAAC;QAED,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,IAAY,EAAE,SAAuB,EAAE,QAAgB,SAAS;QAClE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,iCAAiC;QACjC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,UAAU,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;QAEzF,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QAE3E,4BAA4B;QAC5B,IAAI,CAAC,UAAU,EAAE,CAAC;IACpB,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,IAAY,EAAE,QAAgB,SAAS;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,GAAG,KAAK,SAAS,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,KAAe,EAAE,QAAgB,SAAS;QAChD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAwB,CAAC;QAE/C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACxC,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,OAAyD,EAAE,QAAgB,SAAS;QAC1F,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,KAAqB,EAAE,EAAE;YAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,KAAK,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,KAAK,EAAE,CAAC;gBACxC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBACvC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,UAAU,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;gBACzF,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YAC7E,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,UAAU,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,CAAC,UAAU,EAAE,CAAC;IACpB,CAAC;IAED;;OAEG;IACK,UAAU;QAChB,MAAM,KAAK,GAAI,IAAI,CAAC,SAAS,CAAC,GAAG,EAAwB,CAAC,KAAK,CAAC;QAEhE,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YACnC,6BAA6B;YAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC;YACxD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED;;OAEG;IACK,iBAAiB;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC3E,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,MAAM,KAAK,GAAI,IAAI,CAAC,SAAS,CAAC,GAAG,EAAwB,CAAC,KAAK,CAAC;QAEhE,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,kDAAkD,CAAC,CAAC,GAAG,EAA+B,CAAC;QACtH,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,kDAAkD,CAAC,CAAC,GAAG,EAA+B,CAAC;QAEtH,yBAAyB;QACzB,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YACzB,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9C,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,OAAO;YACL,YAAY,EAAE,KAAK;YACnB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,OAAO,EAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAChF,WAAW;YACX,WAAW,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC;YAC/B,WAAW,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC;SAChC,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACd,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAClB,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,KAAa;QACtB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,wCAAwC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvE,CAAC;IAED;;OAEG;IACH,MAAM;QACJ,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CACF;AAED,qBAAqB;AACrB,IAAI,aAAa,GAA0B,IAAI,CAAC;AAEhD;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,aAAa,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,IAAI,aAAa,EAAE,CAAC;QAClB,aAAa,CAAC,KAAK,EAAE,CAAC;QACtB,aAAa,GAAG,IAAI,CAAC;IACvB,CAAC;AACH,CAAC","sourcesContent":["/**\n * EmbeddingCache - Persistent SQLite cache for embeddings\n *\n * Makes ONNX embeddings practical by caching across sessions:\n * - First embed: ~400ms (ONNX inference)\n * - Cached embed: ~0.1ms (SQLite lookup)\n *\n * Storage: ~/.agentic-flow/embedding-cache.db\n */\n\nimport Database from 'better-sqlite3';\nimport { existsSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { createHash } from 'crypto';\n\nexport interface CacheStats {\n totalEntries: number;\n hits: number;\n misses: number;\n hitRate: number;\n dbSizeBytes: number;\n oldestEntry: number;\n newestEntry: number;\n}\n\nexport interface CacheConfig {\n maxEntries?: number; // Max cache entries (default: 10000)\n maxAgeDays?: number; // Max age before eviction (default: 30)\n dbPath?: string; // Custom database path\n dimension?: number; // Embedding dimension (default: 384)\n}\n\n// Default config\nconst DEFAULT_CONFIG: Required<CacheConfig> = {\n maxEntries: 10000,\n maxAgeDays: 30,\n dbPath: join(homedir(), '.agentic-flow', 'embedding-cache.db'),\n dimension: 384,\n};\n\nexport class EmbeddingCache {\n private db: Database.Database;\n private config: Required<CacheConfig>;\n private hits: number = 0;\n private misses: number = 0;\n\n // Prepared statements for performance\n private stmtGet!: Database.Statement;\n private stmtInsert!: Database.Statement;\n private stmtUpdateHits!: Database.Statement;\n private stmtCount!: Database.Statement;\n private stmtEvictOld!: Database.Statement;\n private stmtEvictLRU!: Database.Statement;\n\n constructor(config: CacheConfig = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n\n // Ensure directory exists\n const dir = join(homedir(), '.agentic-flow');\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Open database with WAL mode for better concurrency\n this.db = new Database(this.config.dbPath);\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('synchronous = NORMAL');\n this.db.pragma('cache_size = 10000');\n\n this.initSchema();\n this.prepareStatements();\n this.cleanupOldEntries();\n }\n\n private initSchema(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS embeddings (\n hash TEXT PRIMARY KEY,\n text TEXT NOT NULL,\n embedding BLOB NOT NULL,\n dimension INTEGER NOT NULL,\n model TEXT NOT NULL,\n hits INTEGER DEFAULT 1,\n created_at INTEGER NOT NULL,\n last_accessed INTEGER NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_last_accessed ON embeddings(last_accessed);\n CREATE INDEX IF NOT EXISTS idx_created_at ON embeddings(created_at);\n CREATE INDEX IF NOT EXISTS idx_model ON embeddings(model);\n `);\n }\n\n private prepareStatements(): void {\n this.stmtGet = this.db.prepare(`\n SELECT embedding, dimension FROM embeddings WHERE hash = ?\n `);\n\n this.stmtInsert = this.db.prepare(`\n INSERT OR REPLACE INTO embeddings (hash, text, embedding, dimension, model, hits, created_at, last_accessed)\n VALUES (?, ?, ?, ?, ?, 1, ?, ?)\n `);\n\n this.stmtUpdateHits = this.db.prepare(`\n UPDATE embeddings SET hits = hits + 1, last_accessed = ? WHERE hash = ?\n `);\n\n this.stmtCount = this.db.prepare(`SELECT COUNT(*) as count FROM embeddings`);\n\n this.stmtEvictOld = this.db.prepare(`\n DELETE FROM embeddings WHERE created_at < ?\n `);\n\n this.stmtEvictLRU = this.db.prepare(`\n DELETE FROM embeddings WHERE hash IN (\n SELECT hash FROM embeddings ORDER BY last_accessed ASC LIMIT ?\n )\n `);\n }\n\n /**\n * Generate hash key for text + model combination\n */\n private hashKey(text: string, model: string = 'default'): string {\n return createHash('sha256').update(`${model}:${text}`).digest('hex').slice(0, 32);\n }\n\n /**\n * Get embedding from cache\n * Returns null if not found\n */\n get(text: string, model: string = 'default'): Float32Array | null {\n const hash = this.hashKey(text, model);\n const row = this.stmtGet.get(hash) as { embedding: Buffer; dimension: number } | undefined;\n\n if (row) {\n this.hits++;\n // Update access time and hit count\n this.stmtUpdateHits.run(Date.now(), hash);\n // Convert Buffer back to Float32Array\n return new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.dimension);\n }\n\n this.misses++;\n return null;\n }\n\n /**\n * Store embedding in cache\n */\n set(text: string, embedding: Float32Array, model: string = 'default'): void {\n const hash = this.hashKey(text, model);\n const now = Date.now();\n\n // Convert Float32Array to Buffer\n const buffer = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);\n\n this.stmtInsert.run(hash, text, buffer, embedding.length, model, now, now);\n\n // Check if we need to evict\n this.maybeEvict();\n }\n\n /**\n * Check if text is cached\n */\n has(text: string, model: string = 'default'): boolean {\n const hash = this.hashKey(text, model);\n const row = this.stmtGet.get(hash);\n return row !== undefined;\n }\n\n /**\n * Get multiple embeddings at once\n * Returns map of text -> embedding (only cached ones)\n */\n getMany(texts: string[], model: string = 'default'): Map<string, Float32Array> {\n const result = new Map<string, Float32Array>();\n\n for (const text of texts) {\n const embedding = this.get(text, model);\n if (embedding) {\n result.set(text, embedding);\n }\n }\n\n return result;\n }\n\n /**\n * Store multiple embeddings at once\n */\n setMany(entries: Array<{ text: string; embedding: Float32Array }>, model: string = 'default'): void {\n const insertMany = this.db.transaction((items: typeof entries) => {\n const now = Date.now();\n for (const { text, embedding } of items) {\n const hash = this.hashKey(text, model);\n const buffer = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);\n this.stmtInsert.run(hash, text, buffer, embedding.length, model, now, now);\n }\n });\n\n insertMany(entries);\n this.maybeEvict();\n }\n\n /**\n * Evict old or LRU entries if over limit\n */\n private maybeEvict(): void {\n const count = (this.stmtCount.get() as { count: number }).count;\n\n if (count > this.config.maxEntries) {\n // Evict 10% of entries (LRU)\n const toEvict = Math.ceil(this.config.maxEntries * 0.1);\n this.stmtEvictLRU.run(toEvict);\n }\n }\n\n /**\n * Cleanup entries older than maxAgeDays\n */\n private cleanupOldEntries(): void {\n const cutoff = Date.now() - (this.config.maxAgeDays * 24 * 60 * 60 * 1000);\n this.stmtEvictOld.run(cutoff);\n }\n\n /**\n * Get cache statistics\n */\n getStats(): CacheStats {\n const count = (this.stmtCount.get() as { count: number }).count;\n\n const oldest = this.db.prepare(`SELECT MIN(created_at) as oldest FROM embeddings`).get() as { oldest: number | null };\n const newest = this.db.prepare(`SELECT MAX(created_at) as newest FROM embeddings`).get() as { newest: number | null };\n\n // Get database file size\n let dbSizeBytes = 0;\n try {\n const fs = require('fs');\n const stats = fs.statSync(this.config.dbPath);\n dbSizeBytes = stats.size;\n } catch {\n // Ignore\n }\n\n return {\n totalEntries: count,\n hits: this.hits,\n misses: this.misses,\n hitRate: this.hits + this.misses > 0 ? this.hits / (this.hits + this.misses) : 0,\n dbSizeBytes,\n oldestEntry: oldest.oldest || 0,\n newestEntry: newest.newest || 0,\n };\n }\n\n /**\n * Clear all cached embeddings\n */\n clear(): void {\n this.db.exec('DELETE FROM embeddings');\n this.hits = 0;\n this.misses = 0;\n }\n\n /**\n * Clear embeddings for a specific model\n */\n clearModel(model: string): void {\n this.db.prepare('DELETE FROM embeddings WHERE model = ?').run(model);\n }\n\n /**\n * Vacuum database to reclaim space\n */\n vacuum(): void {\n this.db.exec('VACUUM');\n }\n\n /**\n * Close database connection\n */\n close(): void {\n this.db.close();\n }\n}\n\n// Singleton instance\nlet cacheInstance: EmbeddingCache | null = null;\n\n/**\n * Get the singleton embedding cache\n */\nexport function getEmbeddingCache(config?: CacheConfig): EmbeddingCache {\n if (!cacheInstance) {\n cacheInstance = new EmbeddingCache(config);\n }\n return cacheInstance;\n}\n\n/**\n * Reset the cache singleton (for testing)\n */\nexport function resetEmbeddingCache(): void {\n if (cacheInstance) {\n cacheInstance.close();\n cacheInstance = null;\n }\n}\n"]}
@@ -5,10 +5,13 @@
5
5
  * - SIMD128 acceleration (6x faster)
6
6
  * - Parallel worker threads (7 workers)
7
7
  * - all-MiniLM-L6-v2 model (384 dimensions)
8
+ * - Persistent SQLite cache (0.1ms vs 400ms)
8
9
  *
9
10
  * Configure via:
10
11
  * - AGENTIC_FLOW_EMBEDDINGS=simple|onnx|auto (default: auto)
11
12
  * - AGENTIC_FLOW_EMBEDDING_MODEL=all-MiniLM-L6-v2 (default)
13
+ * - AGENTIC_FLOW_EMBEDDING_CACHE=true|false (default: true)
14
+ * - AGENTIC_FLOW_PERSISTENT_CACHE=true|false (default: true)
12
15
  */
13
16
  export type EmbeddingBackend = 'simple' | 'onnx' | 'auto';
14
17
  export interface EmbeddingStats {
@@ -23,6 +26,14 @@ export interface EmbeddingStats {
23
26
  modelName?: string;
24
27
  simdAvailable?: boolean;
25
28
  parallelWorkers?: number;
29
+ persistentCache?: {
30
+ enabled: boolean;
31
+ entries: number;
32
+ hits: number;
33
+ misses: number;
34
+ hitRate: number;
35
+ dbSizeKB: number;
36
+ };
26
37
  }
27
38
  export interface SimilarityResult {
28
39
  similarity: number;
@@ -51,6 +62,8 @@ export declare class EmbeddingService {
51
62
  private cacheHits;
52
63
  private cache;
53
64
  private cacheEnabled;
65
+ private persistentCache;
66
+ private persistentCacheEnabled;
54
67
  private corpus;
55
68
  private constructor();
56
69
  static getInstance(): EmbeddingService;
@@ -137,9 +150,26 @@ export declare class EmbeddingService {
137
150
  */
138
151
  getStats(): EmbeddingStats;
139
152
  /**
140
- * Clear cache
153
+ * Clear in-memory cache
141
154
  */
142
155
  clearCache(): void;
156
+ /**
157
+ * Clear persistent cache (SQLite)
158
+ */
159
+ clearPersistentCache(): void;
160
+ /**
161
+ * Clear all caches (memory + persistent)
162
+ */
163
+ clearAllCaches(): void;
164
+ /**
165
+ * Get persistent cache stats
166
+ */
167
+ getPersistentCacheStats(): {
168
+ entries: number;
169
+ hits: number;
170
+ misses: number;
171
+ hitRate: number;
172
+ } | null;
143
173
  /**
144
174
  * Clear corpus
145
175
  */
@@ -1 +1 @@
1
- {"version":3,"file":"EmbeddingService.d.ts","sourceRoot":"","sources":["../../src/intelligence/EmbeddingService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EmbeddingService.d.ts","sourceRoot":"","sources":["../../src/intelligence/EmbeddingService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,eAAe,CAAC,EAAE;QAChB,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAyFD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiC;IAExD,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,gBAAgB,CAAiC;IACzD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;IAG1B,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,cAAc,CAA8B;IAGpD,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,SAAS,CAAa;IAG9B,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,YAAY,CAAU;IAG9B,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,sBAAsB,CAAU;IAGxC,OAAO,CAAC,MAAM,CAAkF;IAEhG,OAAO;IAoBP,MAAM,CAAC,WAAW,IAAI,gBAAgB;IAOtC;;OAEG;YACW,cAAc;IAsB5B;;OAEG;IACH,UAAU,IAAI,gBAAgB;IAI9B;;OAEG;IACH,mBAAmB,IAAI,gBAAgB;IAIvC;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;;OAGG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IA0DhD;;;OAGG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAyC1D;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAa/D;;;OAGG;IACG,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAiB5D;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjD;;;OAGG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAsB9E;;;OAGG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,GAAE,MAAY,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAoCzF;;;OAGG;IACG,YAAY,CAChB,KAAK,EAAE,MAAM,EAAE,EACf,CAAC,GAAE,MAAU,EACb,aAAa,GAAE,MAAY,GAC1B,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;IAyE7D;;;OAGG;IACI,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,GAAE,MAAW,GAAG,cAAc,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,YAAY,CAAA;KAAE,CAAC;IAerI;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAY,GAAG,YAAY;IAwB1D;;OAEG;IACH,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM;IAiB1D;;OAEG;IACH,QAAQ,IAAI,cAAc;IAkC1B;;OAEG;IACH,UAAU,IAAI,IAAI;IAIlB;;OAEG;IACH,oBAAoB,IAAI,IAAI;IAM5B;;OAEG;IACH,cAAc,IAAI,IAAI;IAOtB;;OAEG;IACH,uBAAuB,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAWpG;;OAEG;IACH,WAAW,IAAI,IAAI;IAInB;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/B;;OAEG;WACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQpC;AAGD,wBAAgB,mBAAmB,IAAI,gBAAgB,CAEtD;AAGD,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAE/D;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAEzE;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAElF;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAY,GAAG,YAAY,CAEzE;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAE3E;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAE7F;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,GAAE,MAAY,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAExG;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,GAAE,MAAU,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC,CAE7H"}
@@ -5,11 +5,15 @@
5
5
  * - SIMD128 acceleration (6x faster)
6
6
  * - Parallel worker threads (7 workers)
7
7
  * - all-MiniLM-L6-v2 model (384 dimensions)
8
+ * - Persistent SQLite cache (0.1ms vs 400ms)
8
9
  *
9
10
  * Configure via:
10
11
  * - AGENTIC_FLOW_EMBEDDINGS=simple|onnx|auto (default: auto)
11
12
  * - AGENTIC_FLOW_EMBEDDING_MODEL=all-MiniLM-L6-v2 (default)
13
+ * - AGENTIC_FLOW_EMBEDDING_CACHE=true|false (default: true)
14
+ * - AGENTIC_FLOW_PERSISTENT_CACHE=true|false (default: true)
12
15
  */
16
+ import { getEmbeddingCache } from './EmbeddingCache.js';
13
17
  // ONNX availability cache
14
18
  let onnxAvailable = null;
15
19
  let ruvectorModule = null;
@@ -32,8 +36,8 @@ async function detectOnnx() {
32
36
  return false;
33
37
  }
34
38
  }
35
- // Simple LRU cache for embeddings
36
- class EmbeddingCache {
39
+ // Simple LRU cache for embeddings (in-memory, fast)
40
+ class LRUCache {
37
41
  cache = new Map();
38
42
  maxSize;
39
43
  constructor(maxSize = 1000) {
@@ -78,9 +82,12 @@ export class EmbeddingService {
78
82
  totalEmbeddings = 0;
79
83
  totalLatencyMs = 0;
80
84
  cacheHits = 0;
81
- // Cache
85
+ // Cache (in-memory LRU)
82
86
  cache;
83
87
  cacheEnabled;
88
+ // Persistent cache (SQLite)
89
+ persistentCache = null;
90
+ persistentCacheEnabled;
84
91
  // Corpus for search operations
85
92
  corpus = { texts: [], embeddings: [] };
86
93
  constructor() {
@@ -89,7 +96,18 @@ export class EmbeddingService {
89
96
  this.modelName = process.env.AGENTIC_FLOW_EMBEDDING_MODEL || 'all-MiniLM-L6-v2';
90
97
  this.dimension = 256; // Will be updated when ONNX loads (384)
91
98
  this.cacheEnabled = process.env.AGENTIC_FLOW_EMBEDDING_CACHE !== 'false';
92
- this.cache = new EmbeddingCache(1000);
99
+ this.persistentCacheEnabled = process.env.AGENTIC_FLOW_PERSISTENT_CACHE !== 'false';
100
+ this.cache = new LRUCache(1000);
101
+ // Initialize persistent cache
102
+ if (this.persistentCacheEnabled) {
103
+ try {
104
+ this.persistentCache = getEmbeddingCache({ dimension: 384 });
105
+ }
106
+ catch (error) {
107
+ console.warn('[EmbeddingService] Persistent cache unavailable:', error);
108
+ this.persistentCacheEnabled = false;
109
+ }
110
+ }
93
111
  }
94
112
  static getInstance() {
95
113
  if (!EmbeddingService.instance) {
@@ -150,7 +168,7 @@ export class EmbeddingService {
150
168
  */
151
169
  async embed(text) {
152
170
  const startTime = performance.now();
153
- // Check cache
171
+ // Check in-memory cache first (fastest)
154
172
  if (this.cacheEnabled) {
155
173
  const cached = this.cache.get(text);
156
174
  if (cached) {
@@ -158,6 +176,18 @@ export class EmbeddingService {
158
176
  return cached;
159
177
  }
160
178
  }
179
+ // Check persistent cache (SQLite, ~0.1ms)
180
+ if (this.persistentCache) {
181
+ const cached = this.persistentCache.get(text, this.modelName);
182
+ if (cached) {
183
+ this.cacheHits++;
184
+ // Also store in memory cache for faster subsequent access
185
+ if (this.cacheEnabled) {
186
+ this.cache.set(text, cached);
187
+ }
188
+ return cached;
189
+ }
190
+ }
161
191
  // Resolve backend (handles 'auto' mode)
162
192
  const effectiveBackend = await this.resolveBackend();
163
193
  let embedding;
@@ -177,10 +207,14 @@ export class EmbeddingService {
177
207
  // Update stats
178
208
  this.totalEmbeddings++;
179
209
  this.totalLatencyMs += performance.now() - startTime;
180
- // Cache result
210
+ // Cache result in memory
181
211
  if (this.cacheEnabled) {
182
212
  this.cache.set(text, embedding);
183
213
  }
214
+ // Cache result persistently (for cross-session)
215
+ if (this.persistentCache && effectiveBackend === 'onnx') {
216
+ this.persistentCache.set(text, embedding, this.modelName);
217
+ }
184
218
  return embedding;
185
219
  }
186
220
  /**
@@ -448,6 +482,19 @@ export class EmbeddingService {
448
482
  getStats() {
449
483
  const effective = this.effectiveBackend || this.backend;
450
484
  const ruvectorStats = ruvectorModule?.getStats?.() || {};
485
+ // Get persistent cache stats
486
+ let persistentCacheStats;
487
+ if (this.persistentCache) {
488
+ const cacheStats = this.persistentCache.getStats();
489
+ persistentCacheStats = {
490
+ enabled: true,
491
+ entries: cacheStats.totalEntries,
492
+ hits: cacheStats.hits,
493
+ misses: cacheStats.misses,
494
+ hitRate: cacheStats.hitRate,
495
+ dbSizeKB: Math.round(cacheStats.dbSizeBytes / 1024),
496
+ };
497
+ }
451
498
  return {
452
499
  backend: this.backend,
453
500
  effectiveBackend: effective,
@@ -460,14 +507,46 @@ export class EmbeddingService {
460
507
  modelName: effective === 'onnx' ? this.modelName : undefined,
461
508
  simdAvailable: ruvectorStats.simdAvailable ?? onnxAvailable,
462
509
  parallelWorkers: ruvectorStats.workerCount ?? undefined,
510
+ persistentCache: persistentCacheStats,
463
511
  };
464
512
  }
465
513
  /**
466
- * Clear cache
514
+ * Clear in-memory cache
467
515
  */
468
516
  clearCache() {
469
517
  this.cache.clear();
470
518
  }
519
+ /**
520
+ * Clear persistent cache (SQLite)
521
+ */
522
+ clearPersistentCache() {
523
+ if (this.persistentCache) {
524
+ this.persistentCache.clear();
525
+ }
526
+ }
527
+ /**
528
+ * Clear all caches (memory + persistent)
529
+ */
530
+ clearAllCaches() {
531
+ this.cache.clear();
532
+ if (this.persistentCache) {
533
+ this.persistentCache.clear();
534
+ }
535
+ }
536
+ /**
537
+ * Get persistent cache stats
538
+ */
539
+ getPersistentCacheStats() {
540
+ if (!this.persistentCache)
541
+ return null;
542
+ const stats = this.persistentCache.getStats();
543
+ return {
544
+ entries: stats.totalEntries,
545
+ hits: stats.hits,
546
+ misses: stats.misses,
547
+ hitRate: stats.hitRate,
548
+ };
549
+ }
471
550
  /**
472
551
  * Clear corpus
473
552
  */