@soulcraft/brainy 3.35.0 → 3.36.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/CHANGELOG.md +57 -0
- package/README.md +32 -12
- package/dist/hnsw/hnswIndex.d.ts +104 -1
- package/dist/hnsw/hnswIndex.js +282 -25
- package/dist/hnsw/hnswIndexOptimized.d.ts +1 -2
- package/dist/hnsw/hnswIndexOptimized.js +3 -5
- package/dist/hnsw/partitionedHNSWIndex.js +1 -1
- package/dist/interfaces/IIndex.d.ts +14 -5
- package/dist/utils/memoryDetection.d.ts +119 -0
- package/dist/utils/memoryDetection.js +321 -0
- package/dist/utils/unifiedCache.d.ts +75 -1
- package/dist/utils/unifiedCache.js +123 -4
- package/package.json +1 -1
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* UnifiedCache - Single cache for both HNSW and MetadataIndex
|
|
3
3
|
* Prevents resource competition with cost-aware eviction
|
|
4
|
+
*
|
|
5
|
+
* Features (v3.36.0+):
|
|
6
|
+
* - Adaptive sizing: Automatically scales from 2GB to 128GB+ based on available memory
|
|
7
|
+
* - Container-aware: Detects Docker/K8s limits (cgroups v1/v2)
|
|
8
|
+
* - Environment detection: Production vs development allocation strategies
|
|
9
|
+
* - Memory pressure monitoring: Warns when approaching limits
|
|
4
10
|
*/
|
|
5
11
|
import { prodLog } from './logger.js';
|
|
12
|
+
import { getRecommendedCacheConfig, formatBytes, checkMemoryPressure } from './memoryDetection.js';
|
|
6
13
|
export class UnifiedCache {
|
|
7
14
|
constructor(config = {}) {
|
|
8
15
|
this.cache = new Map();
|
|
@@ -11,17 +18,51 @@ export class UnifiedCache {
|
|
|
11
18
|
this.typeAccessCounts = { hnsw: 0, metadata: 0, embedding: 0, other: 0 };
|
|
12
19
|
this.totalAccessCount = 0;
|
|
13
20
|
this.currentSize = 0;
|
|
14
|
-
this.
|
|
21
|
+
this.memoryPressureCheckTimer = null;
|
|
22
|
+
this.lastMemoryWarning = 0;
|
|
23
|
+
// Adaptive cache sizing (v3.36.0+)
|
|
24
|
+
const recommendation = getRecommendedCacheConfig({
|
|
25
|
+
manualSize: config.maxSize,
|
|
26
|
+
minSize: config.minSize,
|
|
27
|
+
developmentMode: config.developmentMode
|
|
28
|
+
});
|
|
29
|
+
this.memoryInfo = recommendation.memoryInfo;
|
|
30
|
+
this.allocationStrategy = recommendation.allocation;
|
|
31
|
+
this.maxSize = recommendation.allocation.cacheSize;
|
|
32
|
+
// Log allocation decision (v3.36.0+: includes model memory)
|
|
33
|
+
prodLog.info(`UnifiedCache initialized: ${formatBytes(this.maxSize)} ` +
|
|
34
|
+
`(${this.allocationStrategy.environment} mode, ` +
|
|
35
|
+
`${(this.allocationStrategy.ratio * 100).toFixed(0)}% of ${formatBytes(this.allocationStrategy.availableForCache)} ` +
|
|
36
|
+
`after ${formatBytes(this.allocationStrategy.modelMemory)} ${this.allocationStrategy.modelPrecision.toUpperCase()} model)`);
|
|
37
|
+
// Log memory detection details
|
|
38
|
+
prodLog.debug(`Memory detection: source=${this.memoryInfo.source}, ` +
|
|
39
|
+
`container=${this.memoryInfo.isContainer}, ` +
|
|
40
|
+
`system=${formatBytes(this.memoryInfo.systemTotal)}, ` +
|
|
41
|
+
`free=${formatBytes(this.memoryInfo.free)}, ` +
|
|
42
|
+
`totalAvailable=${formatBytes(this.memoryInfo.available)}, ` +
|
|
43
|
+
`modelReserved=${formatBytes(this.allocationStrategy.modelMemory)}, ` +
|
|
44
|
+
`availableForCache=${formatBytes(this.allocationStrategy.availableForCache)}`);
|
|
45
|
+
// Log warnings if any
|
|
46
|
+
for (const warning of recommendation.warnings) {
|
|
47
|
+
prodLog.warn(`UnifiedCache: ${warning}`);
|
|
48
|
+
}
|
|
49
|
+
// Finalize configuration
|
|
15
50
|
this.config = {
|
|
16
51
|
enableRequestCoalescing: true,
|
|
17
52
|
enableFairnessCheck: true,
|
|
18
53
|
fairnessCheckInterval: 60000, // Check fairness every minute
|
|
19
54
|
persistPatterns: true,
|
|
55
|
+
enableMemoryMonitoring: true,
|
|
56
|
+
memoryCheckInterval: 30000, // Check memory every 30s
|
|
20
57
|
...config
|
|
21
58
|
};
|
|
59
|
+
// Start monitoring
|
|
22
60
|
if (this.config.enableFairnessCheck) {
|
|
23
61
|
this.startFairnessMonitor();
|
|
24
62
|
}
|
|
63
|
+
if (this.config.enableMemoryMonitoring) {
|
|
64
|
+
this.startMemoryPressureMonitor();
|
|
65
|
+
}
|
|
25
66
|
}
|
|
26
67
|
/**
|
|
27
68
|
* Get item from cache with request coalescing
|
|
@@ -62,6 +103,25 @@ export class UnifiedCache {
|
|
|
62
103
|
}
|
|
63
104
|
}
|
|
64
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Synchronous cache lookup (v3.36.0+)
|
|
108
|
+
* Returns cached data immediately or undefined if not cached
|
|
109
|
+
* Use for sync fast path optimization - zero async overhead
|
|
110
|
+
*/
|
|
111
|
+
getSync(key) {
|
|
112
|
+
// Check if in cache
|
|
113
|
+
const item = this.cache.get(key);
|
|
114
|
+
if (item) {
|
|
115
|
+
// Update access tracking synchronously
|
|
116
|
+
this.access.set(key, (this.access.get(key) || 0) + 1);
|
|
117
|
+
this.totalAccessCount++;
|
|
118
|
+
item.lastAccess = Date.now();
|
|
119
|
+
item.accessCount++;
|
|
120
|
+
this.typeAccessCounts[item.type]++;
|
|
121
|
+
return item.data;
|
|
122
|
+
}
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
65
125
|
/**
|
|
66
126
|
* Set item in cache with cost-aware eviction
|
|
67
127
|
*/
|
|
@@ -234,7 +294,45 @@ export class UnifiedCache {
|
|
|
234
294
|
}
|
|
235
295
|
}
|
|
236
296
|
/**
|
|
237
|
-
*
|
|
297
|
+
* Start memory pressure monitoring
|
|
298
|
+
* Periodically checks if we're approaching memory limits
|
|
299
|
+
*/
|
|
300
|
+
startMemoryPressureMonitor() {
|
|
301
|
+
const checkInterval = this.config.memoryCheckInterval || 30000;
|
|
302
|
+
this.memoryPressureCheckTimer = setInterval(() => {
|
|
303
|
+
this.checkMemoryPressure();
|
|
304
|
+
}, checkInterval);
|
|
305
|
+
// Unref so it doesn't keep process alive
|
|
306
|
+
if (this.memoryPressureCheckTimer.unref) {
|
|
307
|
+
this.memoryPressureCheckTimer.unref();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Check current memory pressure and warn if needed
|
|
312
|
+
*/
|
|
313
|
+
checkMemoryPressure() {
|
|
314
|
+
const pressure = checkMemoryPressure(this.currentSize, this.memoryInfo);
|
|
315
|
+
// Only log warnings every 5 minutes to avoid spam
|
|
316
|
+
const now = Date.now();
|
|
317
|
+
const fiveMinutes = 5 * 60 * 1000;
|
|
318
|
+
if (pressure.warnings.length > 0 && now - this.lastMemoryWarning > fiveMinutes) {
|
|
319
|
+
for (const warning of pressure.warnings) {
|
|
320
|
+
prodLog.warn(`UnifiedCache: ${warning}`);
|
|
321
|
+
}
|
|
322
|
+
this.lastMemoryWarning = now;
|
|
323
|
+
}
|
|
324
|
+
// If critical, force aggressive eviction
|
|
325
|
+
if (pressure.pressure === 'critical') {
|
|
326
|
+
const targetSize = Math.floor(this.maxSize * 0.7); // Evict to 70%
|
|
327
|
+
const bytesToFree = this.currentSize - targetSize;
|
|
328
|
+
if (bytesToFree > 0) {
|
|
329
|
+
prodLog.warn(`UnifiedCache: Critical memory pressure - forcing eviction of ${formatBytes(bytesToFree)}`);
|
|
330
|
+
this.evictForSize(bytesToFree);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Get cache statistics with memory information
|
|
238
336
|
*/
|
|
239
337
|
getStats() {
|
|
240
338
|
const typeSizes = { hnsw: 0, metadata: 0, embedding: 0, other: 0 };
|
|
@@ -243,7 +341,10 @@ export class UnifiedCache {
|
|
|
243
341
|
typeSizes[item.type] += item.size;
|
|
244
342
|
typeCounts[item.type]++;
|
|
245
343
|
}
|
|
344
|
+
const hitRate = this.cache.size > 0 ?
|
|
345
|
+
Array.from(this.cache.values()).reduce((sum, item) => sum + item.accessCount, 0) / this.totalAccessCount : 0;
|
|
246
346
|
return {
|
|
347
|
+
// Cache statistics
|
|
247
348
|
totalSize: this.currentSize,
|
|
248
349
|
maxSize: this.maxSize,
|
|
249
350
|
utilization: this.currentSize / this.maxSize,
|
|
@@ -252,8 +353,26 @@ export class UnifiedCache {
|
|
|
252
353
|
typeCounts,
|
|
253
354
|
typeAccessCounts: this.typeAccessCounts,
|
|
254
355
|
totalAccessCount: this.totalAccessCount,
|
|
255
|
-
hitRate
|
|
256
|
-
|
|
356
|
+
hitRate,
|
|
357
|
+
// Memory management (v3.36.0+)
|
|
358
|
+
memory: {
|
|
359
|
+
available: this.memoryInfo.available,
|
|
360
|
+
source: this.memoryInfo.source,
|
|
361
|
+
isContainer: this.memoryInfo.isContainer,
|
|
362
|
+
systemTotal: this.memoryInfo.systemTotal,
|
|
363
|
+
allocationRatio: this.allocationStrategy.ratio,
|
|
364
|
+
environment: this.allocationStrategy.environment
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Get detailed memory information
|
|
370
|
+
*/
|
|
371
|
+
getMemoryInfo() {
|
|
372
|
+
return {
|
|
373
|
+
memoryInfo: { ...this.memoryInfo },
|
|
374
|
+
allocationStrategy: { ...this.allocationStrategy },
|
|
375
|
+
currentPressure: checkMemoryPressure(this.currentSize, this.memoryInfo)
|
|
257
376
|
};
|
|
258
377
|
}
|
|
259
378
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.36.0",
|
|
4
4
|
"description": "Universal Knowledge Protocol™ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. 31 nouns × 40 verbs for infinite expressiveness.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|