@danielsimonjr/memory-mcp 9.9.0 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/EntityManager.d.ts +2 -0
- package/dist/core/EntityManager.d.ts.map +1 -1
- package/dist/core/EntityManager.js +14 -9
- package/dist/core/RelationManager.d.ts.map +1 -1
- package/dist/core/RelationManager.js +5 -4
- package/dist/features/CompressionManager.d.ts +3 -1
- package/dist/features/CompressionManager.d.ts.map +1 -1
- package/dist/features/CompressionManager.js +14 -5
- package/dist/search/BM25Search.d.ts +148 -0
- package/dist/search/BM25Search.d.ts.map +1 -0
- package/dist/search/BM25Search.js +339 -0
- package/dist/search/EarlyTerminationManager.d.ts +140 -0
- package/dist/search/EarlyTerminationManager.d.ts.map +1 -0
- package/dist/search/EarlyTerminationManager.js +279 -0
- package/dist/search/EmbeddingCache.d.ts +175 -0
- package/dist/search/EmbeddingCache.d.ts.map +1 -0
- package/dist/search/EmbeddingCache.js +246 -0
- package/dist/search/EmbeddingService.d.ts +108 -9
- package/dist/search/EmbeddingService.d.ts.map +1 -1
- package/dist/search/EmbeddingService.js +187 -15
- package/dist/search/HybridScorer.d.ts +181 -0
- package/dist/search/HybridScorer.d.ts.map +1 -0
- package/dist/search/HybridScorer.js +257 -0
- package/dist/search/IncrementalIndexer.d.ts +201 -0
- package/dist/search/IncrementalIndexer.d.ts.map +1 -0
- package/dist/search/IncrementalIndexer.js +342 -0
- package/dist/search/OptimizedInvertedIndex.d.ts +163 -0
- package/dist/search/OptimizedInvertedIndex.d.ts.map +1 -0
- package/dist/search/OptimizedInvertedIndex.js +358 -0
- package/dist/search/ParallelSearchExecutor.d.ts +172 -0
- package/dist/search/ParallelSearchExecutor.d.ts.map +1 -0
- package/dist/search/ParallelSearchExecutor.js +309 -0
- package/dist/search/QuantizedVectorStore.d.ts +171 -0
- package/dist/search/QuantizedVectorStore.d.ts.map +1 -0
- package/dist/search/QuantizedVectorStore.js +307 -0
- package/dist/search/QueryCostEstimator.d.ts +135 -2
- package/dist/search/QueryCostEstimator.d.ts.map +1 -1
- package/dist/search/QueryCostEstimator.js +298 -1
- package/dist/search/QueryPlanCache.d.ts +220 -0
- package/dist/search/QueryPlanCache.d.ts.map +1 -0
- package/dist/search/QueryPlanCache.js +379 -0
- package/dist/search/ReflectionManager.d.ts +49 -0
- package/dist/search/ReflectionManager.d.ts.map +1 -1
- package/dist/search/ReflectionManager.js +113 -6
- package/dist/search/index.d.ts +12 -3
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +20 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types.d.ts +41 -2
- package/dist/types/types.d.ts.map +1 -1
- package/dist/utils/BatchProcessor.d.ts +271 -0
- package/dist/utils/BatchProcessor.d.ts.map +1 -0
- package/dist/utils/BatchProcessor.js +376 -0
- package/dist/utils/MemoryMonitor.d.ts +176 -0
- package/dist/utils/MemoryMonitor.d.ts.map +1 -0
- package/dist/utils/MemoryMonitor.js +305 -0
- package/dist/utils/WorkerPoolManager.d.ts +233 -0
- package/dist/utils/WorkerPoolManager.d.ts.map +1 -0
- package/dist/utils/WorkerPoolManager.js +420 -0
- package/dist/utils/compressedCache.d.ts +29 -0
- package/dist/utils/compressedCache.d.ts.map +1 -1
- package/dist/utils/compressedCache.js +39 -0
- package/dist/utils/entityUtils.d.ts +25 -0
- package/dist/utils/entityUtils.d.ts.map +1 -1
- package/dist/utils/entityUtils.js +33 -0
- package/dist/utils/index.d.ts +4 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Plan Cache
|
|
3
|
+
*
|
|
4
|
+
* Phase 12 Sprint 4: Caches query analysis and planning results
|
|
5
|
+
* with LRU eviction for improved performance.
|
|
6
|
+
*
|
|
7
|
+
* @module search/QueryPlanCache
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Default cache options.
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_OPTIONS = {
|
|
13
|
+
maxSize: 1000,
|
|
14
|
+
ttlMs: 5 * 60 * 1000, // 5 minutes
|
|
15
|
+
normalizeQueries: true,
|
|
16
|
+
enableStats: true,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Query Plan Cache with LRU eviction.
|
|
20
|
+
*
|
|
21
|
+
* Caches query analysis and planning results to avoid redundant computation.
|
|
22
|
+
* Uses LRU (Least Recently Used) eviction when cache is full.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const cache = new QueryPlanCache({ maxSize: 500 });
|
|
27
|
+
*
|
|
28
|
+
* // Cache an analysis
|
|
29
|
+
* cache.setAnalysis('Find Alice', analysis);
|
|
30
|
+
*
|
|
31
|
+
* // Retrieve cached analysis
|
|
32
|
+
* const cached = cache.getAnalysis('Find Alice');
|
|
33
|
+
* if (cached) {
|
|
34
|
+
* console.log('Cache hit!', cached);
|
|
35
|
+
* }
|
|
36
|
+
*
|
|
37
|
+
* // Get statistics
|
|
38
|
+
* const stats = cache.getStats();
|
|
39
|
+
* console.log(`Hit rate: ${(stats.hitRate * 100).toFixed(1)}%`);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export class QueryPlanCache {
|
|
43
|
+
cache;
|
|
44
|
+
options;
|
|
45
|
+
// Statistics
|
|
46
|
+
hits = 0;
|
|
47
|
+
misses = 0;
|
|
48
|
+
evictions = 0;
|
|
49
|
+
constructor(options) {
|
|
50
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
51
|
+
this.cache = new Map();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get cached analysis for a query.
|
|
55
|
+
*
|
|
56
|
+
* @param query - The search query
|
|
57
|
+
* @returns Cached analysis or undefined if not found
|
|
58
|
+
*/
|
|
59
|
+
getAnalysis(query) {
|
|
60
|
+
const entry = this.getEntry(query);
|
|
61
|
+
return entry?.analysis;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get cached plan for a query.
|
|
65
|
+
*
|
|
66
|
+
* @param query - The search query
|
|
67
|
+
* @returns Cached plan or undefined if not found
|
|
68
|
+
*/
|
|
69
|
+
getPlan(query) {
|
|
70
|
+
const entry = this.getEntry(query);
|
|
71
|
+
return entry?.plan;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get full cached entry for a query.
|
|
75
|
+
*
|
|
76
|
+
* @param query - The search query
|
|
77
|
+
* @returns Cached entry or undefined if not found
|
|
78
|
+
*/
|
|
79
|
+
getEntry(query) {
|
|
80
|
+
const key = this.normalizeQuery(query);
|
|
81
|
+
const entry = this.cache.get(key);
|
|
82
|
+
if (!entry) {
|
|
83
|
+
if (this.options.enableStats)
|
|
84
|
+
this.misses++;
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
// Check TTL
|
|
88
|
+
if (this.isExpired(entry)) {
|
|
89
|
+
this.cache.delete(key);
|
|
90
|
+
if (this.options.enableStats)
|
|
91
|
+
this.misses++;
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
// Update access time and count
|
|
95
|
+
entry.lastAccessed = Date.now();
|
|
96
|
+
entry.hitCount++;
|
|
97
|
+
if (this.options.enableStats)
|
|
98
|
+
this.hits++;
|
|
99
|
+
return entry;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Cache analysis results for a query.
|
|
103
|
+
*
|
|
104
|
+
* @param query - The search query
|
|
105
|
+
* @param analysis - The analysis result to cache
|
|
106
|
+
*/
|
|
107
|
+
setAnalysis(query, analysis) {
|
|
108
|
+
const key = this.normalizeQuery(query);
|
|
109
|
+
const now = Date.now();
|
|
110
|
+
const existing = this.cache.get(key);
|
|
111
|
+
if (existing) {
|
|
112
|
+
// Update existing entry
|
|
113
|
+
existing.analysis = analysis;
|
|
114
|
+
existing.lastAccessed = now;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// Evict if necessary
|
|
118
|
+
this.evictIfNeeded();
|
|
119
|
+
// Create new entry
|
|
120
|
+
this.cache.set(key, {
|
|
121
|
+
normalizedQuery: key,
|
|
122
|
+
originalQuery: query,
|
|
123
|
+
analysis,
|
|
124
|
+
createdAt: now,
|
|
125
|
+
lastAccessed: now,
|
|
126
|
+
hitCount: 0,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Cache plan results for a query.
|
|
132
|
+
*
|
|
133
|
+
* @param query - The search query
|
|
134
|
+
* @param analysis - The analysis result to cache
|
|
135
|
+
* @param plan - The plan result to cache
|
|
136
|
+
*/
|
|
137
|
+
setPlan(query, analysis, plan) {
|
|
138
|
+
const key = this.normalizeQuery(query);
|
|
139
|
+
const now = Date.now();
|
|
140
|
+
const existing = this.cache.get(key);
|
|
141
|
+
if (existing) {
|
|
142
|
+
// Update existing entry
|
|
143
|
+
existing.analysis = analysis;
|
|
144
|
+
existing.plan = plan;
|
|
145
|
+
existing.lastAccessed = now;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// Evict if necessary
|
|
149
|
+
this.evictIfNeeded();
|
|
150
|
+
// Create new entry
|
|
151
|
+
this.cache.set(key, {
|
|
152
|
+
normalizedQuery: key,
|
|
153
|
+
originalQuery: query,
|
|
154
|
+
analysis,
|
|
155
|
+
plan,
|
|
156
|
+
createdAt: now,
|
|
157
|
+
lastAccessed: now,
|
|
158
|
+
hitCount: 0,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Check if a query is cached.
|
|
164
|
+
*
|
|
165
|
+
* @param query - The search query
|
|
166
|
+
* @returns True if cached (and not expired)
|
|
167
|
+
*/
|
|
168
|
+
has(query) {
|
|
169
|
+
const key = this.normalizeQuery(query);
|
|
170
|
+
const entry = this.cache.get(key);
|
|
171
|
+
if (!entry)
|
|
172
|
+
return false;
|
|
173
|
+
if (this.isExpired(entry)) {
|
|
174
|
+
this.cache.delete(key);
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Invalidate a specific query from cache.
|
|
181
|
+
*
|
|
182
|
+
* @param query - The search query to invalidate
|
|
183
|
+
* @returns True if entry was found and removed
|
|
184
|
+
*/
|
|
185
|
+
invalidate(query) {
|
|
186
|
+
const key = this.normalizeQuery(query);
|
|
187
|
+
return this.cache.delete(key);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Invalidate all entries matching a pattern.
|
|
191
|
+
*
|
|
192
|
+
* @param pattern - Regex pattern to match against queries
|
|
193
|
+
* @returns Number of entries invalidated
|
|
194
|
+
*/
|
|
195
|
+
invalidatePattern(pattern) {
|
|
196
|
+
let count = 0;
|
|
197
|
+
for (const [key, entry] of this.cache) {
|
|
198
|
+
if (pattern.test(entry.originalQuery) || pattern.test(key)) {
|
|
199
|
+
this.cache.delete(key);
|
|
200
|
+
count++;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return count;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Clear all cache entries.
|
|
207
|
+
*/
|
|
208
|
+
clear() {
|
|
209
|
+
this.cache.clear();
|
|
210
|
+
this.hits = 0;
|
|
211
|
+
this.misses = 0;
|
|
212
|
+
this.evictions = 0;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Get cache statistics.
|
|
216
|
+
*
|
|
217
|
+
* @returns Cache statistics
|
|
218
|
+
*/
|
|
219
|
+
getStats() {
|
|
220
|
+
const now = Date.now();
|
|
221
|
+
let totalAge = 0;
|
|
222
|
+
for (const entry of this.cache.values()) {
|
|
223
|
+
totalAge += now - entry.createdAt;
|
|
224
|
+
}
|
|
225
|
+
const total = this.hits + this.misses;
|
|
226
|
+
return {
|
|
227
|
+
size: this.cache.size,
|
|
228
|
+
maxSize: this.options.maxSize,
|
|
229
|
+
hits: this.hits,
|
|
230
|
+
misses: this.misses,
|
|
231
|
+
hitRate: total > 0 ? this.hits / total : 0,
|
|
232
|
+
evictions: this.evictions,
|
|
233
|
+
averageEntryAgeMs: this.cache.size > 0 ? totalAge / this.cache.size : 0,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get the current cache size.
|
|
238
|
+
*/
|
|
239
|
+
get size() {
|
|
240
|
+
return this.cache.size;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Normalize a query for cache lookup.
|
|
244
|
+
*
|
|
245
|
+
* Normalization helps improve cache hit rate by treating
|
|
246
|
+
* similar queries as equivalent.
|
|
247
|
+
*
|
|
248
|
+
* @param query - The query to normalize
|
|
249
|
+
* @returns Normalized query string
|
|
250
|
+
*/
|
|
251
|
+
normalizeQuery(query) {
|
|
252
|
+
if (!this.options.normalizeQueries) {
|
|
253
|
+
return query;
|
|
254
|
+
}
|
|
255
|
+
return query
|
|
256
|
+
.toLowerCase()
|
|
257
|
+
.trim()
|
|
258
|
+
// Normalize whitespace
|
|
259
|
+
.replace(/\s+/g, ' ')
|
|
260
|
+
// Remove punctuation that doesn't affect meaning
|
|
261
|
+
.replace(/[.,!?;:]+$/g, '')
|
|
262
|
+
// Sort operators for consistent matching
|
|
263
|
+
.replace(/\b(AND|OR|NOT)\b/gi, match => match.toUpperCase());
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Check if an entry has expired.
|
|
267
|
+
* @private
|
|
268
|
+
*/
|
|
269
|
+
isExpired(entry) {
|
|
270
|
+
return Date.now() - entry.createdAt > this.options.ttlMs;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Evict entries if cache is at capacity.
|
|
274
|
+
* Uses LRU (Least Recently Used) eviction.
|
|
275
|
+
* @private
|
|
276
|
+
*/
|
|
277
|
+
evictIfNeeded() {
|
|
278
|
+
if (this.cache.size < this.options.maxSize) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
// Find the least recently used entry
|
|
282
|
+
let oldestKey = null;
|
|
283
|
+
let oldestTime = Infinity;
|
|
284
|
+
for (const [key, entry] of this.cache) {
|
|
285
|
+
// Also check for expired entries during eviction
|
|
286
|
+
if (this.isExpired(entry)) {
|
|
287
|
+
this.cache.delete(key);
|
|
288
|
+
if (this.options.enableStats)
|
|
289
|
+
this.evictions++;
|
|
290
|
+
if (this.cache.size < this.options.maxSize)
|
|
291
|
+
return;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (entry.lastAccessed < oldestTime) {
|
|
295
|
+
oldestTime = entry.lastAccessed;
|
|
296
|
+
oldestKey = key;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Evict the oldest entry
|
|
300
|
+
if (oldestKey) {
|
|
301
|
+
this.cache.delete(oldestKey);
|
|
302
|
+
if (this.options.enableStats)
|
|
303
|
+
this.evictions++;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Clean up expired entries.
|
|
308
|
+
*
|
|
309
|
+
* Call this periodically to remove stale entries.
|
|
310
|
+
*
|
|
311
|
+
* @returns Number of entries removed
|
|
312
|
+
*/
|
|
313
|
+
cleanup() {
|
|
314
|
+
let count = 0;
|
|
315
|
+
for (const [key, entry] of this.cache) {
|
|
316
|
+
if (this.isExpired(entry)) {
|
|
317
|
+
this.cache.delete(key);
|
|
318
|
+
count++;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return count;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Get all cache keys (for debugging).
|
|
325
|
+
*/
|
|
326
|
+
keys() {
|
|
327
|
+
return Array.from(this.cache.keys());
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Preload cache with common queries.
|
|
331
|
+
*
|
|
332
|
+
* @param queries - Array of query-analysis pairs to preload
|
|
333
|
+
*/
|
|
334
|
+
preload(queries) {
|
|
335
|
+
for (const { query, analysis, plan } of queries) {
|
|
336
|
+
if (plan) {
|
|
337
|
+
this.setPlan(query, analysis, plan);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
this.setAnalysis(query, analysis);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Export cache entries for persistence.
|
|
346
|
+
*
|
|
347
|
+
* @returns Array of cache entries
|
|
348
|
+
*/
|
|
349
|
+
export() {
|
|
350
|
+
const entries = [];
|
|
351
|
+
for (const entry of this.cache.values()) {
|
|
352
|
+
if (!this.isExpired(entry)) {
|
|
353
|
+
entries.push({ ...entry });
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return entries;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Import cache entries from persistence.
|
|
360
|
+
*
|
|
361
|
+
* @param entries - Array of cache entries to import
|
|
362
|
+
* @param preserveTimestamps - Whether to preserve original timestamps
|
|
363
|
+
*/
|
|
364
|
+
import(entries, preserveTimestamps = false) {
|
|
365
|
+
const now = Date.now();
|
|
366
|
+
for (const entry of entries) {
|
|
367
|
+
if (!preserveTimestamps) {
|
|
368
|
+
entry.createdAt = now;
|
|
369
|
+
entry.lastAccessed = now;
|
|
370
|
+
}
|
|
371
|
+
// Skip if expired
|
|
372
|
+
if (this.isExpired(entry))
|
|
373
|
+
continue;
|
|
374
|
+
// Evict if needed
|
|
375
|
+
this.evictIfNeeded();
|
|
376
|
+
this.cache.set(entry.normalizedQuery, entry);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
@@ -21,6 +21,31 @@ export interface ReflectionOptions {
|
|
|
21
21
|
minResults?: number;
|
|
22
22
|
/** Hybrid search options */
|
|
23
23
|
searchOptions?: Partial<HybridSearchOptions>;
|
|
24
|
+
/** Phase 12 Sprint 4: Progressive limit increase factor (default: 1.5) */
|
|
25
|
+
limitIncreaseFactor?: number;
|
|
26
|
+
/** Phase 12 Sprint 4: Initial search limit (default: 10) */
|
|
27
|
+
initialLimit?: number;
|
|
28
|
+
/** Phase 12 Sprint 4: Focus on specific missing info types */
|
|
29
|
+
focusMissingTypes?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Phase 12 Sprint 4: Refinement history entry.
|
|
33
|
+
*/
|
|
34
|
+
export interface RefinementHistoryEntry {
|
|
35
|
+
/** Iteration number (1-based) */
|
|
36
|
+
iteration: number;
|
|
37
|
+
/** Query used in this iteration */
|
|
38
|
+
query: string;
|
|
39
|
+
/** Search limit used */
|
|
40
|
+
limit: number;
|
|
41
|
+
/** Results found in this iteration */
|
|
42
|
+
resultsFound: number;
|
|
43
|
+
/** Adequacy score after this iteration */
|
|
44
|
+
adequacyScore: number;
|
|
45
|
+
/** Reason for refinement (if not final) */
|
|
46
|
+
refinementReason?: string;
|
|
47
|
+
/** Missing info types that triggered refinement */
|
|
48
|
+
missingInfoTypes?: string[];
|
|
24
49
|
}
|
|
25
50
|
/**
|
|
26
51
|
* Result of reflection-based retrieval.
|
|
@@ -31,6 +56,10 @@ export interface ReflectionResult {
|
|
|
31
56
|
adequate: boolean;
|
|
32
57
|
refinements: string[];
|
|
33
58
|
adequacyScore: number;
|
|
59
|
+
/** Phase 12 Sprint 4: Detailed refinement history */
|
|
60
|
+
refinementHistory: RefinementHistoryEntry[];
|
|
61
|
+
/** Phase 12 Sprint 4: Final search limit used */
|
|
62
|
+
finalLimit: number;
|
|
34
63
|
}
|
|
35
64
|
/**
|
|
36
65
|
* Reflection Manager for iterative retrieval refinement.
|
|
@@ -57,8 +86,28 @@ export declare class ReflectionManager {
|
|
|
57
86
|
constructor(hybridSearch: HybridSearchManager, analyzer: QueryAnalyzer);
|
|
58
87
|
/**
|
|
59
88
|
* Perform retrieval with reflection-based refinement.
|
|
89
|
+
*
|
|
90
|
+
* Phase 12 Sprint 4 enhancements:
|
|
91
|
+
* - Progressive limit increase per round
|
|
92
|
+
* - Focused query refinement based on missing information
|
|
93
|
+
* - Detailed refinement history tracking
|
|
60
94
|
*/
|
|
61
95
|
retrieveWithReflection(graph: ReadonlyKnowledgeGraph, query: string, options?: ReflectionOptions): Promise<ReflectionResult>;
|
|
96
|
+
/**
|
|
97
|
+
* Find missing information types based on analysis requirements.
|
|
98
|
+
* @private
|
|
99
|
+
*/
|
|
100
|
+
private findMissingInfoTypes;
|
|
101
|
+
/**
|
|
102
|
+
* Refine query with focus on missing information types.
|
|
103
|
+
* @private
|
|
104
|
+
*/
|
|
105
|
+
private refineQueryFocused;
|
|
106
|
+
/**
|
|
107
|
+
* Get human-readable reason for refinement.
|
|
108
|
+
* @private
|
|
109
|
+
*/
|
|
110
|
+
private getRefinementReason;
|
|
62
111
|
/**
|
|
63
112
|
* Calculate result adequacy score.
|
|
64
113
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReflectionManager.d.ts","sourceRoot":"","sources":["../../src/search/ReflectionManager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,sBAAsB,EAEtB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"ReflectionManager.d.ts","sourceRoot":"","sources":["../../src/search/ReflectionManager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,sBAAsB,EAEtB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC7C,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,iBAAiB,EAAE,sBAAsB,EAAE,CAAC;IAC5C,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,QAAQ;gBADR,YAAY,EAAE,mBAAmB,EACjC,QAAQ,EAAE,aAAa;IAGjC;;;;;;;OAOG;IACG,sBAAsB,CAC1B,KAAK,EAAE,sBAAsB,EAC7B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC;IAwG5B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IA4B5B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAqC1B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAoB3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA6BzB;;OAEG;IACH,OAAO,CAAC,WAAW;CA6BpB"}
|
|
@@ -34,18 +34,31 @@ export class ReflectionManager {
|
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* Perform retrieval with reflection-based refinement.
|
|
37
|
+
*
|
|
38
|
+
* Phase 12 Sprint 4 enhancements:
|
|
39
|
+
* - Progressive limit increase per round
|
|
40
|
+
* - Focused query refinement based on missing information
|
|
41
|
+
* - Detailed refinement history tracking
|
|
37
42
|
*/
|
|
38
43
|
async retrieveWithReflection(graph, query, options = {}) {
|
|
39
|
-
const { maxIterations = 3, adequacyThreshold = 0.7, minResults = 3, searchOptions = {}, } = options;
|
|
44
|
+
const { maxIterations = 3, adequacyThreshold = 0.7, minResults = 3, searchOptions = {}, limitIncreaseFactor = 1.5, initialLimit = 10, focusMissingTypes = true, } = options;
|
|
40
45
|
const allResults = [];
|
|
41
46
|
const refinements = [];
|
|
47
|
+
const refinementHistory = [];
|
|
42
48
|
let currentQuery = query;
|
|
43
49
|
let iteration = 0;
|
|
44
50
|
let adequacyScore = 0;
|
|
51
|
+
let currentLimit = initialLimit;
|
|
52
|
+
// Get initial analysis for tracking
|
|
53
|
+
const analysis = this.analyzer.analyze(query);
|
|
45
54
|
while (iteration < maxIterations) {
|
|
46
55
|
iteration++;
|
|
47
|
-
//
|
|
48
|
-
|
|
56
|
+
// Calculate current limit with progressive increase
|
|
57
|
+
currentLimit = Math.round(initialLimit * Math.pow(limitIncreaseFactor, iteration - 1));
|
|
58
|
+
// Execute search with current limit
|
|
59
|
+
const results = await this.hybridSearch.searchWithEntities(graph, currentQuery, { ...searchOptions, limit: currentLimit });
|
|
60
|
+
// Track results found this iteration (before deduplication)
|
|
61
|
+
const newResultsCount = results.filter(r => !allResults.some(existing => existing.entity.name === r.entity.name)).length;
|
|
49
62
|
// Add new results (deduplicated)
|
|
50
63
|
for (const result of results) {
|
|
51
64
|
if (!allResults.some(r => r.entity.name === result.entity.name)) {
|
|
@@ -53,17 +66,36 @@ export class ReflectionManager {
|
|
|
53
66
|
}
|
|
54
67
|
}
|
|
55
68
|
// Check adequacy
|
|
56
|
-
const
|
|
57
|
-
adequacyScore = this.calculateAdequacy(allResults,
|
|
69
|
+
const iterAnalysis = this.analyzer.analyze(currentQuery);
|
|
70
|
+
adequacyScore = this.calculateAdequacy(allResults, iterAnalysis, minResults);
|
|
71
|
+
// Determine missing info types
|
|
72
|
+
const missingInfoTypes = this.findMissingInfoTypes(allResults, analysis);
|
|
73
|
+
// Record history entry
|
|
74
|
+
const historyEntry = {
|
|
75
|
+
iteration,
|
|
76
|
+
query: currentQuery,
|
|
77
|
+
limit: currentLimit,
|
|
78
|
+
resultsFound: newResultsCount,
|
|
79
|
+
adequacyScore,
|
|
80
|
+
};
|
|
58
81
|
if (adequacyScore >= adequacyThreshold) {
|
|
82
|
+
refinementHistory.push(historyEntry);
|
|
59
83
|
break;
|
|
60
84
|
}
|
|
61
85
|
// Refine query if not adequate
|
|
62
|
-
const refinedQuery =
|
|
86
|
+
const refinedQuery = focusMissingTypes && missingInfoTypes.length > 0
|
|
87
|
+
? this.refineQueryFocused(currentQuery, allResults, analysis, missingInfoTypes)
|
|
88
|
+
: this.refineQuery(currentQuery, allResults, analysis);
|
|
63
89
|
if (refinedQuery === currentQuery) {
|
|
64
90
|
// No refinement possible
|
|
91
|
+
historyEntry.refinementReason = 'No further refinement possible';
|
|
92
|
+
refinementHistory.push(historyEntry);
|
|
65
93
|
break;
|
|
66
94
|
}
|
|
95
|
+
// Record refinement details in history
|
|
96
|
+
historyEntry.refinementReason = this.getRefinementReason(currentQuery, refinedQuery, missingInfoTypes);
|
|
97
|
+
historyEntry.missingInfoTypes = missingInfoTypes;
|
|
98
|
+
refinementHistory.push(historyEntry);
|
|
67
99
|
refinements.push(refinedQuery);
|
|
68
100
|
currentQuery = refinedQuery;
|
|
69
101
|
}
|
|
@@ -73,8 +105,83 @@ export class ReflectionManager {
|
|
|
73
105
|
adequate: adequacyScore >= adequacyThreshold,
|
|
74
106
|
refinements,
|
|
75
107
|
adequacyScore,
|
|
108
|
+
refinementHistory,
|
|
109
|
+
finalLimit: currentLimit,
|
|
76
110
|
};
|
|
77
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Find missing information types based on analysis requirements.
|
|
114
|
+
* @private
|
|
115
|
+
*/
|
|
116
|
+
findMissingInfoTypes(results, analysis) {
|
|
117
|
+
const coveredTypes = new Set();
|
|
118
|
+
for (const result of results) {
|
|
119
|
+
const entityType = result.entity.entityType.toLowerCase();
|
|
120
|
+
coveredTypes.add(entityType);
|
|
121
|
+
// Map entity types to info types
|
|
122
|
+
if (['person', 'people', 'user', 'employee'].includes(entityType)) {
|
|
123
|
+
coveredTypes.add('person');
|
|
124
|
+
}
|
|
125
|
+
if (['location', 'place', 'city', 'country', 'address'].includes(entityType)) {
|
|
126
|
+
coveredTypes.add('location');
|
|
127
|
+
}
|
|
128
|
+
if (result.entity.createdAt || result.entity.lastModified) {
|
|
129
|
+
coveredTypes.add('temporal');
|
|
130
|
+
}
|
|
131
|
+
// Entity type covers 'entity' requirement
|
|
132
|
+
coveredTypes.add('entity');
|
|
133
|
+
}
|
|
134
|
+
// Find which required types are missing
|
|
135
|
+
return analysis.requiredInfoTypes.filter(type => !coveredTypes.has(type));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Refine query with focus on missing information types.
|
|
139
|
+
* @private
|
|
140
|
+
*/
|
|
141
|
+
refineQueryFocused(query, results, analysis, missingInfoTypes) {
|
|
142
|
+
const additions = [];
|
|
143
|
+
for (const missingType of missingInfoTypes) {
|
|
144
|
+
switch (missingType) {
|
|
145
|
+
case 'person':
|
|
146
|
+
additions.push('person people who');
|
|
147
|
+
break;
|
|
148
|
+
case 'location':
|
|
149
|
+
additions.push('location place where');
|
|
150
|
+
break;
|
|
151
|
+
case 'temporal':
|
|
152
|
+
additions.push('when date time');
|
|
153
|
+
break;
|
|
154
|
+
case 'quantity':
|
|
155
|
+
additions.push('count number how many');
|
|
156
|
+
break;
|
|
157
|
+
case 'reason':
|
|
158
|
+
additions.push('reason why because');
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (additions.length > 0) {
|
|
163
|
+
// Add relevant keywords to query
|
|
164
|
+
return `${query} ${additions.slice(0, 2).join(' ')}`;
|
|
165
|
+
}
|
|
166
|
+
// Fallback to standard refinement
|
|
167
|
+
return this.refineQuery(query, results, analysis);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get human-readable reason for refinement.
|
|
171
|
+
* @private
|
|
172
|
+
*/
|
|
173
|
+
getRefinementReason(_originalQuery, refinedQuery, missingInfoTypes) {
|
|
174
|
+
if (missingInfoTypes.length > 0) {
|
|
175
|
+
return `Added keywords for missing info types: ${missingInfoTypes.join(', ')}`;
|
|
176
|
+
}
|
|
177
|
+
if (refinedQuery.includes('person people')) {
|
|
178
|
+
return 'Expanded query to find person-related entities';
|
|
179
|
+
}
|
|
180
|
+
if (refinedQuery.includes('recent history timeline')) {
|
|
181
|
+
return 'Added temporal context to query';
|
|
182
|
+
}
|
|
183
|
+
return 'Broadened query by removing constraints';
|
|
184
|
+
}
|
|
78
185
|
/**
|
|
79
186
|
* Calculate result adequacy score.
|
|
80
187
|
*/
|
package/dist/search/index.d.ts
CHANGED
|
@@ -12,15 +12,24 @@ export { SearchSuggestions } from './SearchSuggestions.js';
|
|
|
12
12
|
export { SavedSearchManager } from './SavedSearchManager.js';
|
|
13
13
|
export { SearchManager } from './SearchManager.js';
|
|
14
14
|
export { SearchFilterChain, type SearchFilters, type ValidatedPagination } from './SearchFilterChain.js';
|
|
15
|
-
export { OpenAIEmbeddingService, LocalEmbeddingService, MockEmbeddingService, createEmbeddingService, } from './EmbeddingService.js';
|
|
15
|
+
export { OpenAIEmbeddingService, LocalEmbeddingService, MockEmbeddingService, createEmbeddingService, l2Normalize, QUERY_PREFIX, DOCUMENT_PREFIX, type EmbeddingProgressCallback, } from './EmbeddingService.js';
|
|
16
|
+
export { EmbeddingCache, DEFAULT_EMBEDDING_CACHE_OPTIONS, type EmbeddingCacheStats, type EmbeddingCacheOptions, } from './EmbeddingCache.js';
|
|
17
|
+
export { IncrementalIndexer, DEFAULT_INDEXER_OPTIONS, type IndexOperationType, type IndexOperation, type IncrementalIndexerOptions, type FlushResult, } from './IncrementalIndexer.js';
|
|
16
18
|
export { InMemoryVectorStore, SQLiteVectorStore, createVectorStore, cosineSimilarity, type SQLiteStorageWithEmbeddings, } from './VectorStore.js';
|
|
17
19
|
export { SemanticSearch, entityToText, } from './SemanticSearch.js';
|
|
18
20
|
export { TFIDFIndexManager } from './TFIDFIndexManager.js';
|
|
19
21
|
export { TFIDFEventSync } from './TFIDFEventSync.js';
|
|
20
|
-
export { QueryCostEstimator } from './QueryCostEstimator.js';
|
|
22
|
+
export { QueryCostEstimator, type SearchLayer, type ExtendedQueryCostEstimate, type LayerRecommendationOptions, type TokenEstimationOptions, type AdaptiveDepthConfig, } from './QueryCostEstimator.js';
|
|
21
23
|
export { SymbolicSearch, type SymbolicResult } from './SymbolicSearch.js';
|
|
22
24
|
export { HybridSearchManager, DEFAULT_HYBRID_WEIGHTS } from './HybridSearchManager.js';
|
|
23
25
|
export { QueryAnalyzer } from './QueryAnalyzer.js';
|
|
24
26
|
export { QueryPlanner } from './QueryPlanner.js';
|
|
25
|
-
export { ReflectionManager, type ReflectionOptions, type ReflectionResult, } from './ReflectionManager.js';
|
|
27
|
+
export { ReflectionManager, type ReflectionOptions, type ReflectionResult, type RefinementHistoryEntry, } from './ReflectionManager.js';
|
|
28
|
+
export { BM25Search, STOPWORDS, DEFAULT_BM25_CONFIG, type BM25DocumentEntry, type BM25Index, type BM25Config, } from './BM25Search.js';
|
|
29
|
+
export { OptimizedInvertedIndex, type IndexMemoryUsage, type PostingListResult, } from './OptimizedInvertedIndex.js';
|
|
30
|
+
export { HybridScorer, DEFAULT_SCORER_WEIGHTS, type SemanticSearchResult, type LexicalSearchResult, type SymbolicSearchResult, type ScoredResult, type HybridWeights, type HybridScorerOptions, } from './HybridScorer.js';
|
|
31
|
+
export { ParallelSearchExecutor, type LayerTiming, type ParallelSearchResult, type ParallelSearchOptions, } from './ParallelSearchExecutor.js';
|
|
32
|
+
export { EarlyTerminationManager, type AdequacyCheck, type EarlyTerminationOptions, type EarlyTerminationResult, } from './EarlyTerminationManager.js';
|
|
33
|
+
export { QueryPlanCache, type CachedQueryEntry, type QueryPlanCacheStats, type QueryPlanCacheOptions, } from './QueryPlanCache.js';
|
|
34
|
+
export { QuantizedVectorStore, type QuantizationParams, type QuantizedVectorStoreStats, type QuantizedSearchResult, type QuantizedVectorStoreOptions, } from './QuantizedVectorStore.js';
|
|
26
35
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EAAE,iBAAiB,EAAE,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":"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"}
|