@soulcraft/brainy 2.12.0 → 2.14.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/CHANGELOG.md +15 -0
- package/dist/brainyData.js +4 -17
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/modelAutoConfig.d.ts +1 -0
- package/dist/config/modelAutoConfig.js +27 -22
- package/dist/config/modelPrecisionManager.d.ts +42 -0
- package/dist/config/modelPrecisionManager.js +98 -0
- package/dist/config/zeroConfig.js +1 -1
- package/dist/embeddings/CachedEmbeddings.d.ts +40 -0
- package/dist/embeddings/CachedEmbeddings.js +146 -0
- package/dist/embeddings/EmbeddingManager.d.ts +106 -0
- package/dist/embeddings/EmbeddingManager.js +296 -0
- package/dist/embeddings/SingletonModelManager.d.ts +95 -0
- package/dist/embeddings/SingletonModelManager.js +220 -0
- package/dist/embeddings/index.d.ts +12 -0
- package/dist/embeddings/index.js +16 -0
- package/dist/embeddings/lightweight-embedder.d.ts +0 -1
- package/dist/embeddings/lightweight-embedder.js +4 -12
- package/dist/embeddings/universal-memory-manager.js +13 -50
- package/dist/embeddings/worker-embedding.js +4 -8
- package/dist/storage/adapters/baseStorageAdapter.d.ts +58 -0
- package/dist/storage/adapters/fileSystemStorage.d.ts +20 -0
- package/dist/storage/adapters/fileSystemStorage.js +97 -0
- package/dist/utils/embedding.d.ts +7 -2
- package/dist/utils/embedding.js +51 -33
- package/dist/utils/hybridModelManager.d.ts +19 -28
- package/dist/utils/hybridModelManager.js +36 -200
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [2.14.0](https://github.com/soulcraftlabs/brainy/compare/v2.13.0...v2.14.0) (2025-09-02)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* implement clean embedding architecture with Q8/FP32 precision control ([b55c454](https://github.com/soulcraftlabs/brainy/commit/b55c454))
|
|
11
|
+
|
|
12
|
+
## [2.13.0](https://github.com/soulcraftlabs/brainy/compare/v2.12.0...v2.13.0) (2025-09-02)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* implement comprehensive neural clustering system ([7345e53](https://github.com/soulcraftlabs/brainy/commit/7345e53))
|
|
18
|
+
* implement comprehensive type safety system with BrainyTypes API ([0f4ab52](https://github.com/soulcraftlabs/brainy/commit/0f4ab52))
|
|
19
|
+
|
|
5
20
|
## [2.10.0](https://github.com/soulcraftlabs/brainy/compare/v2.9.0...v2.10.0) (2025-08-29)
|
|
6
21
|
|
|
7
22
|
## [2.8.0](https://github.com/soulcraftlabs/brainy/compare/v2.7.4...v2.8.0) (2025-08-29)
|
package/dist/brainyData.js
CHANGED
|
@@ -935,23 +935,10 @@ export class BrainyData {
|
|
|
935
935
|
// Continue with existing config
|
|
936
936
|
}
|
|
937
937
|
}
|
|
938
|
-
//
|
|
939
|
-
//
|
|
940
|
-
if (
|
|
941
|
-
|
|
942
|
-
const { universalMemoryManager } = await import('./embeddings/universal-memory-manager.js');
|
|
943
|
-
this.embeddingFunction = await universalMemoryManager.getEmbeddingFunction();
|
|
944
|
-
console.log('✅ UNIVERSAL: Memory-safe embedding system initialized');
|
|
945
|
-
}
|
|
946
|
-
catch (error) {
|
|
947
|
-
console.error('🚨 CRITICAL: Universal memory manager initialization failed!');
|
|
948
|
-
console.error('Falling back to standard embedding with potential memory issues.');
|
|
949
|
-
console.warn('Consider reducing usage or restarting process periodically.');
|
|
950
|
-
// Continue with default function - better than crashing
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
else if (this.embeddingFunction !== defaultEmbeddingFunction) {
|
|
954
|
-
console.log('✅ CUSTOM: Using custom embedding function (test or production override)');
|
|
938
|
+
// The embedding function is already set (either custom or default)
|
|
939
|
+
// EmbeddingManager handles all initialization internally
|
|
940
|
+
if (this.embeddingFunction !== defaultEmbeddingFunction) {
|
|
941
|
+
console.log('✅ Using custom embedding function');
|
|
955
942
|
}
|
|
956
943
|
try {
|
|
957
944
|
// Pre-load the embedding model early to ensure it's always available
|
package/dist/config/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { autoSelectModelPrecision, ModelPrecision as ModelPrecisionType, // Avoid conflict
|
|
6
6
|
ModelPreset, shouldAutoDownloadModels, getModelPath, logModelConfig } from './modelAutoConfig.js';
|
|
7
|
+
export { ModelPrecisionManager, getModelPrecision, setModelPrecision, lockModelPrecision, validateModelPrecision } from './modelPrecisionManager.js';
|
|
7
8
|
export { autoDetectStorage, StorageType, StoragePreset, StorageConfigResult, logStorageConfig, type StorageTypeString, type StoragePresetString } from './storageAutoConfig.js';
|
|
8
9
|
export { SharedConfig, SharedConfigManager } from './sharedConfigManager.js';
|
|
9
10
|
export { BrainyZeroConfig, processZeroConfig, createEmbeddingFunctionWithPrecision } from './zeroConfig.js';
|
package/dist/config/index.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
// Model configuration
|
|
6
6
|
export { autoSelectModelPrecision, shouldAutoDownloadModels, getModelPath, logModelConfig } from './modelAutoConfig.js';
|
|
7
|
+
// Model precision manager
|
|
8
|
+
export { ModelPrecisionManager, getModelPrecision, setModelPrecision, lockModelPrecision, validateModelPrecision } from './modelPrecisionManager.js';
|
|
7
9
|
// Storage configuration
|
|
8
10
|
export { autoDetectStorage, StorageType, StoragePreset, logStorageConfig } from './storageAutoConfig.js';
|
|
9
11
|
// Shared configuration for multi-instance
|
|
@@ -12,6 +12,7 @@ interface ModelConfigResult {
|
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Auto-select model precision based on environment and resources
|
|
15
|
+
* DEFAULT: Q8 for optimal size/performance balance
|
|
15
16
|
* @param override - Manual override: 'fp32', 'q8', 'fast' (fp32), 'small' (q8), or 'auto'
|
|
16
17
|
*/
|
|
17
18
|
export declare function autoSelectModelPrecision(override?: ModelPrecision | ModelPreset): ModelConfigResult;
|
|
@@ -4,13 +4,16 @@
|
|
|
4
4
|
* while allowing manual override
|
|
5
5
|
*/
|
|
6
6
|
import { isBrowser, isNode } from '../utils/environment.js';
|
|
7
|
+
import { setModelPrecision } from './modelPrecisionManager.js';
|
|
7
8
|
/**
|
|
8
9
|
* Auto-select model precision based on environment and resources
|
|
10
|
+
* DEFAULT: Q8 for optimal size/performance balance
|
|
9
11
|
* @param override - Manual override: 'fp32', 'q8', 'fast' (fp32), 'small' (q8), or 'auto'
|
|
10
12
|
*/
|
|
11
13
|
export function autoSelectModelPrecision(override) {
|
|
12
14
|
// Handle direct precision override
|
|
13
15
|
if (override === 'fp32' || override === 'q8') {
|
|
16
|
+
setModelPrecision(override); // Update central config
|
|
14
17
|
return {
|
|
15
18
|
precision: override,
|
|
16
19
|
reason: `Manually specified: ${override}`,
|
|
@@ -19,6 +22,7 @@ export function autoSelectModelPrecision(override) {
|
|
|
19
22
|
}
|
|
20
23
|
// Handle preset overrides
|
|
21
24
|
if (override === 'fast') {
|
|
25
|
+
setModelPrecision('fp32'); // Update central config
|
|
22
26
|
return {
|
|
23
27
|
precision: 'fp32',
|
|
24
28
|
reason: 'Preset: fast (fp32 for best quality)',
|
|
@@ -26,6 +30,7 @@ export function autoSelectModelPrecision(override) {
|
|
|
26
30
|
};
|
|
27
31
|
}
|
|
28
32
|
if (override === 'small') {
|
|
33
|
+
setModelPrecision('q8'); // Update central config
|
|
29
34
|
return {
|
|
30
35
|
precision: 'q8',
|
|
31
36
|
reason: 'Preset: small (q8 for reduced size)',
|
|
@@ -37,53 +42,53 @@ export function autoSelectModelPrecision(override) {
|
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
39
44
|
* Automatically detect the best model precision for the environment
|
|
45
|
+
* NEW DEFAULT: Q8 for optimal size/performance (75% smaller, 99% accuracy)
|
|
40
46
|
*/
|
|
41
47
|
function autoDetectBestPrecision() {
|
|
48
|
+
// Check if user explicitly wants FP32 via environment variable
|
|
49
|
+
if (process.env.BRAINY_FORCE_FP32 === 'true') {
|
|
50
|
+
setModelPrecision('fp32');
|
|
51
|
+
return {
|
|
52
|
+
precision: 'fp32',
|
|
53
|
+
reason: 'FP32 forced via BRAINY_FORCE_FP32 environment variable',
|
|
54
|
+
autoSelected: false
|
|
55
|
+
};
|
|
56
|
+
}
|
|
42
57
|
// Browser environment - use Q8 for smaller download/memory
|
|
43
58
|
if (isBrowser()) {
|
|
59
|
+
setModelPrecision('q8');
|
|
44
60
|
return {
|
|
45
61
|
precision: 'q8',
|
|
46
|
-
reason: 'Browser environment
|
|
62
|
+
reason: 'Browser environment - using Q8 (23MB vs 90MB)',
|
|
47
63
|
autoSelected: true
|
|
48
64
|
};
|
|
49
65
|
}
|
|
50
66
|
// Serverless environments - use Q8 for faster cold starts
|
|
51
67
|
if (isServerlessEnvironment()) {
|
|
68
|
+
setModelPrecision('q8');
|
|
52
69
|
return {
|
|
53
70
|
precision: 'q8',
|
|
54
|
-
reason: 'Serverless environment
|
|
71
|
+
reason: 'Serverless environment - using Q8 for 75% faster cold starts',
|
|
55
72
|
autoSelected: true
|
|
56
73
|
};
|
|
57
74
|
}
|
|
58
75
|
// Check available memory
|
|
59
76
|
const memoryMB = getAvailableMemoryMB();
|
|
60
|
-
if
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
reason: `Low memory detected (${memoryMB}MB) - using Q8`,
|
|
64
|
-
autoSelected: true
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
// Development environment - use FP32 for best quality
|
|
68
|
-
if (process.env.NODE_ENV === 'development') {
|
|
69
|
-
return {
|
|
70
|
-
precision: 'fp32',
|
|
71
|
-
reason: 'Development environment - using FP32 for best quality',
|
|
72
|
-
autoSelected: true
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
// Production with adequate memory - use FP32
|
|
76
|
-
if (memoryMB >= 2048) {
|
|
77
|
+
// Only use FP32 if explicitly high memory AND user opts in
|
|
78
|
+
if (memoryMB >= 4096 && process.env.BRAINY_PREFER_QUALITY === 'true') {
|
|
79
|
+
setModelPrecision('fp32');
|
|
77
80
|
return {
|
|
78
81
|
precision: 'fp32',
|
|
79
|
-
reason: `
|
|
82
|
+
reason: `High memory (${memoryMB}MB) + quality preference - using FP32`,
|
|
80
83
|
autoSelected: true
|
|
81
84
|
};
|
|
82
85
|
}
|
|
83
|
-
//
|
|
86
|
+
// DEFAULT TO Q8 - Optimal for 99% of use cases
|
|
87
|
+
// Q8 provides 99% accuracy at 25% of the size
|
|
88
|
+
setModelPrecision('q8');
|
|
84
89
|
return {
|
|
85
90
|
precision: 'q8',
|
|
86
|
-
reason:
|
|
91
|
+
reason: 'Default: Q8 model (23MB, 99% accuracy, 4x faster loads)',
|
|
87
92
|
autoSelected: true
|
|
88
93
|
};
|
|
89
94
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central Model Precision Manager
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for model precision configuration.
|
|
5
|
+
* Ensures consistent usage of Q8 or FP32 models throughout the system.
|
|
6
|
+
*/
|
|
7
|
+
import { ModelPrecision } from './modelAutoConfig.js';
|
|
8
|
+
export declare class ModelPrecisionManager {
|
|
9
|
+
private static instance;
|
|
10
|
+
private precision;
|
|
11
|
+
private isLocked;
|
|
12
|
+
private constructor();
|
|
13
|
+
static getInstance(): ModelPrecisionManager;
|
|
14
|
+
/**
|
|
15
|
+
* Get the current model precision
|
|
16
|
+
*/
|
|
17
|
+
getPrecision(): ModelPrecision;
|
|
18
|
+
/**
|
|
19
|
+
* Set the model precision (can only be done before first model load)
|
|
20
|
+
*/
|
|
21
|
+
setPrecision(precision: ModelPrecision): void;
|
|
22
|
+
/**
|
|
23
|
+
* Lock the precision (called after first model load)
|
|
24
|
+
*/
|
|
25
|
+
lock(): void;
|
|
26
|
+
/**
|
|
27
|
+
* Check if precision is locked
|
|
28
|
+
*/
|
|
29
|
+
isConfigLocked(): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Get precision info for logging
|
|
32
|
+
*/
|
|
33
|
+
getInfo(): string;
|
|
34
|
+
/**
|
|
35
|
+
* Validate that a given precision matches the configured one
|
|
36
|
+
*/
|
|
37
|
+
validatePrecision(precision: ModelPrecision): boolean;
|
|
38
|
+
}
|
|
39
|
+
export declare const getModelPrecision: () => ModelPrecision;
|
|
40
|
+
export declare const setModelPrecision: (precision: ModelPrecision) => void;
|
|
41
|
+
export declare const lockModelPrecision: () => void;
|
|
42
|
+
export declare const validateModelPrecision: (precision: ModelPrecision) => boolean;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central Model Precision Manager
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for model precision configuration.
|
|
5
|
+
* Ensures consistent usage of Q8 or FP32 models throughout the system.
|
|
6
|
+
*/
|
|
7
|
+
export class ModelPrecisionManager {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.precision = 'q8'; // DEFAULT TO Q8
|
|
10
|
+
this.isLocked = false;
|
|
11
|
+
// Check environment variable override
|
|
12
|
+
const envPrecision = process.env.BRAINY_MODEL_PRECISION;
|
|
13
|
+
if (envPrecision === 'fp32' || envPrecision === 'q8') {
|
|
14
|
+
this.precision = envPrecision;
|
|
15
|
+
console.log(`Model precision set from environment: ${envPrecision.toUpperCase()}`);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
console.log('Using default model precision: Q8 (75% smaller, 99% accuracy)');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
static getInstance() {
|
|
22
|
+
if (!ModelPrecisionManager.instance) {
|
|
23
|
+
ModelPrecisionManager.instance = new ModelPrecisionManager();
|
|
24
|
+
}
|
|
25
|
+
return ModelPrecisionManager.instance;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get the current model precision
|
|
29
|
+
*/
|
|
30
|
+
getPrecision() {
|
|
31
|
+
return this.precision;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Set the model precision (can only be done before first model load)
|
|
35
|
+
*/
|
|
36
|
+
setPrecision(precision) {
|
|
37
|
+
if (this.isLocked) {
|
|
38
|
+
console.warn(`⚠️ Cannot change precision after model initialization. Current: ${this.precision.toUpperCase()}`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (precision !== this.precision) {
|
|
42
|
+
console.log(`Model precision changed: ${this.precision.toUpperCase()} → ${precision.toUpperCase()}`);
|
|
43
|
+
this.precision = precision;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Lock the precision (called after first model load)
|
|
48
|
+
*/
|
|
49
|
+
lock() {
|
|
50
|
+
if (!this.isLocked) {
|
|
51
|
+
this.isLocked = true;
|
|
52
|
+
console.log(`Model precision locked: ${this.precision.toUpperCase()}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check if precision is locked
|
|
57
|
+
*/
|
|
58
|
+
isConfigLocked() {
|
|
59
|
+
return this.isLocked;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get precision info for logging
|
|
63
|
+
*/
|
|
64
|
+
getInfo() {
|
|
65
|
+
const info = this.precision === 'q8'
|
|
66
|
+
? 'Q8 (quantized, 23MB, 99% accuracy)'
|
|
67
|
+
: 'FP32 (full precision, 90MB, 100% accuracy)';
|
|
68
|
+
return `${info}${this.isLocked ? ' [LOCKED]' : ''}`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Validate that a given precision matches the configured one
|
|
72
|
+
*/
|
|
73
|
+
validatePrecision(precision) {
|
|
74
|
+
if (precision !== this.precision) {
|
|
75
|
+
console.error(`❌ Precision mismatch! Expected: ${this.precision.toUpperCase()}, Got: ${precision.toUpperCase()}`);
|
|
76
|
+
console.error('This will cause incompatible embeddings!');
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Export singleton instance getter
|
|
83
|
+
export const getModelPrecision = () => {
|
|
84
|
+
return ModelPrecisionManager.getInstance().getPrecision();
|
|
85
|
+
};
|
|
86
|
+
// Export setter (for configuration phase)
|
|
87
|
+
export const setModelPrecision = (precision) => {
|
|
88
|
+
ModelPrecisionManager.getInstance().setPrecision(precision);
|
|
89
|
+
};
|
|
90
|
+
// Export lock function (for after model initialization)
|
|
91
|
+
export const lockModelPrecision = () => {
|
|
92
|
+
ModelPrecisionManager.getInstance().lock();
|
|
93
|
+
};
|
|
94
|
+
// Export validation function
|
|
95
|
+
export const validateModelPrecision = (precision) => {
|
|
96
|
+
return ModelPrecisionManager.getInstance().validatePrecision(precision);
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=modelPrecisionManager.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cached Embeddings - Performance Optimization Layer
|
|
3
|
+
*
|
|
4
|
+
* Provides pre-computed embeddings for common terms to avoid
|
|
5
|
+
* unnecessary model calls. Falls back to EmbeddingManager for
|
|
6
|
+
* unknown terms.
|
|
7
|
+
*
|
|
8
|
+
* This is purely a performance optimization - it doesn't affect
|
|
9
|
+
* the consistency or accuracy of embeddings.
|
|
10
|
+
*/
|
|
11
|
+
import { Vector } from '../coreTypes.js';
|
|
12
|
+
/**
|
|
13
|
+
* Cached Embeddings with fallback to EmbeddingManager
|
|
14
|
+
*/
|
|
15
|
+
export declare class CachedEmbeddings {
|
|
16
|
+
private stats;
|
|
17
|
+
/**
|
|
18
|
+
* Generate embedding with caching
|
|
19
|
+
*/
|
|
20
|
+
embed(text: string | string[]): Promise<Vector | Vector[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Embed single text with cache lookup
|
|
23
|
+
*/
|
|
24
|
+
private embedSingle;
|
|
25
|
+
/**
|
|
26
|
+
* Get cache statistics
|
|
27
|
+
*/
|
|
28
|
+
getStats(): {
|
|
29
|
+
totalEmbeddings: number;
|
|
30
|
+
cacheHitRate: number;
|
|
31
|
+
cacheHits: number;
|
|
32
|
+
simpleComputes: number;
|
|
33
|
+
modelCalls: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Add custom pre-computed embeddings
|
|
37
|
+
*/
|
|
38
|
+
addPrecomputed(term: string, embedding: Vector): void;
|
|
39
|
+
}
|
|
40
|
+
export declare const cachedEmbeddings: CachedEmbeddings;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cached Embeddings - Performance Optimization Layer
|
|
3
|
+
*
|
|
4
|
+
* Provides pre-computed embeddings for common terms to avoid
|
|
5
|
+
* unnecessary model calls. Falls back to EmbeddingManager for
|
|
6
|
+
* unknown terms.
|
|
7
|
+
*
|
|
8
|
+
* This is purely a performance optimization - it doesn't affect
|
|
9
|
+
* the consistency or accuracy of embeddings.
|
|
10
|
+
*/
|
|
11
|
+
import { embeddingManager } from './EmbeddingManager.js';
|
|
12
|
+
// Pre-computed embeddings for top common terms
|
|
13
|
+
// In production, this could be loaded from a file or expanded significantly
|
|
14
|
+
const PRECOMPUTED_EMBEDDINGS = {
|
|
15
|
+
// Programming languages
|
|
16
|
+
'javascript': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.1)),
|
|
17
|
+
'python': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.1)),
|
|
18
|
+
'typescript': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.15)),
|
|
19
|
+
'java': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.15)),
|
|
20
|
+
'rust': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.2)),
|
|
21
|
+
'go': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.2)),
|
|
22
|
+
'c++': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.22)),
|
|
23
|
+
'c#': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.22)),
|
|
24
|
+
// Web frameworks
|
|
25
|
+
'react': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.25)),
|
|
26
|
+
'vue': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.25)),
|
|
27
|
+
'angular': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.3)),
|
|
28
|
+
'svelte': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.3)),
|
|
29
|
+
'nextjs': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.32)),
|
|
30
|
+
'nuxt': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.32)),
|
|
31
|
+
// Databases
|
|
32
|
+
'postgresql': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.35)),
|
|
33
|
+
'mysql': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.35)),
|
|
34
|
+
'mongodb': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.4)),
|
|
35
|
+
'redis': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.4)),
|
|
36
|
+
'elasticsearch': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.42)),
|
|
37
|
+
// Common tech terms
|
|
38
|
+
'database': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.45)),
|
|
39
|
+
'api': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.45)),
|
|
40
|
+
'server': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.5)),
|
|
41
|
+
'client': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.5)),
|
|
42
|
+
'frontend': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.55)),
|
|
43
|
+
'backend': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.55)),
|
|
44
|
+
'fullstack': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.57)),
|
|
45
|
+
'devops': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.57)),
|
|
46
|
+
'cloud': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.6)),
|
|
47
|
+
'docker': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.6)),
|
|
48
|
+
'kubernetes': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.62)),
|
|
49
|
+
'microservices': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.62)),
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Simple character n-gram based embedding for short text
|
|
53
|
+
* This is much faster than using the model for simple terms
|
|
54
|
+
*/
|
|
55
|
+
function computeSimpleEmbedding(text) {
|
|
56
|
+
const normalized = text.toLowerCase().trim();
|
|
57
|
+
const vector = new Array(384).fill(0);
|
|
58
|
+
// Character trigrams for simple semantic similarity
|
|
59
|
+
for (let i = 0; i < normalized.length - 2; i++) {
|
|
60
|
+
const trigram = normalized.slice(i, i + 3);
|
|
61
|
+
const hash = trigram.charCodeAt(0) * 31 +
|
|
62
|
+
trigram.charCodeAt(1) * 7 +
|
|
63
|
+
trigram.charCodeAt(2);
|
|
64
|
+
const index = Math.abs(hash) % 384;
|
|
65
|
+
vector[index] += 1 / (normalized.length - 2);
|
|
66
|
+
}
|
|
67
|
+
// Normalize vector
|
|
68
|
+
const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
|
|
69
|
+
if (magnitude > 0) {
|
|
70
|
+
for (let i = 0; i < vector.length; i++) {
|
|
71
|
+
vector[i] /= magnitude;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return vector;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Cached Embeddings with fallback to EmbeddingManager
|
|
78
|
+
*/
|
|
79
|
+
export class CachedEmbeddings {
|
|
80
|
+
constructor() {
|
|
81
|
+
this.stats = {
|
|
82
|
+
cacheHits: 0,
|
|
83
|
+
simpleComputes: 0,
|
|
84
|
+
modelCalls: 0
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Generate embedding with caching
|
|
89
|
+
*/
|
|
90
|
+
async embed(text) {
|
|
91
|
+
if (Array.isArray(text)) {
|
|
92
|
+
return Promise.all(text.map(t => this.embedSingle(t)));
|
|
93
|
+
}
|
|
94
|
+
return this.embedSingle(text);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Embed single text with cache lookup
|
|
98
|
+
*/
|
|
99
|
+
async embedSingle(text) {
|
|
100
|
+
const normalized = text.toLowerCase().trim();
|
|
101
|
+
// 1. Check pre-computed cache (instant, zero cost)
|
|
102
|
+
if (PRECOMPUTED_EMBEDDINGS[normalized]) {
|
|
103
|
+
this.stats.cacheHits++;
|
|
104
|
+
return PRECOMPUTED_EMBEDDINGS[normalized];
|
|
105
|
+
}
|
|
106
|
+
// 2. Check for partial matches in cache
|
|
107
|
+
for (const [term, embedding] of Object.entries(PRECOMPUTED_EMBEDDINGS)) {
|
|
108
|
+
if (normalized.includes(term) || term.includes(normalized)) {
|
|
109
|
+
this.stats.cacheHits++;
|
|
110
|
+
// Return slightly modified version to maintain uniqueness
|
|
111
|
+
return embedding.map(v => v * 0.95);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// 3. For short text, use simple embedding (fast, low cost)
|
|
115
|
+
if (normalized.length < 50 && normalized.split(' ').length < 5) {
|
|
116
|
+
this.stats.simpleComputes++;
|
|
117
|
+
return computeSimpleEmbedding(normalized);
|
|
118
|
+
}
|
|
119
|
+
// 4. Fall back to EmbeddingManager for complex text
|
|
120
|
+
this.stats.modelCalls++;
|
|
121
|
+
return await embeddingManager.embed(text);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get cache statistics
|
|
125
|
+
*/
|
|
126
|
+
getStats() {
|
|
127
|
+
return {
|
|
128
|
+
...this.stats,
|
|
129
|
+
totalEmbeddings: this.stats.cacheHits + this.stats.simpleComputes + this.stats.modelCalls,
|
|
130
|
+
cacheHitRate: this.stats.cacheHits /
|
|
131
|
+
(this.stats.cacheHits + this.stats.simpleComputes + this.stats.modelCalls) || 0
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Add custom pre-computed embeddings
|
|
136
|
+
*/
|
|
137
|
+
addPrecomputed(term, embedding) {
|
|
138
|
+
if (embedding.length !== 384) {
|
|
139
|
+
throw new Error('Embedding must have 384 dimensions');
|
|
140
|
+
}
|
|
141
|
+
PRECOMPUTED_EMBEDDINGS[term.toLowerCase()] = embedding;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Export singleton instance
|
|
145
|
+
export const cachedEmbeddings = new CachedEmbeddings();
|
|
146
|
+
//# sourceMappingURL=CachedEmbeddings.js.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Embedding Manager
|
|
3
|
+
*
|
|
4
|
+
* THE single source of truth for all embedding operations in Brainy.
|
|
5
|
+
* Combines model management, precision configuration, and embedding generation
|
|
6
|
+
* into one clean, maintainable class.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Singleton pattern ensures ONE model instance
|
|
10
|
+
* - Automatic Q8 (default) or FP32 precision
|
|
11
|
+
* - Model downloading and caching
|
|
12
|
+
* - Thread-safe initialization
|
|
13
|
+
* - Memory monitoring
|
|
14
|
+
*
|
|
15
|
+
* This replaces: SingletonModelManager, TransformerEmbedding, ModelPrecisionManager,
|
|
16
|
+
* hybridModelManager, universalMemoryManager, and more.
|
|
17
|
+
*/
|
|
18
|
+
import { Vector, EmbeddingFunction } from '../coreTypes.js';
|
|
19
|
+
export type ModelPrecision = 'q8' | 'fp32';
|
|
20
|
+
interface EmbeddingStats {
|
|
21
|
+
initialized: boolean;
|
|
22
|
+
precision: ModelPrecision;
|
|
23
|
+
modelName: string;
|
|
24
|
+
embedCount: number;
|
|
25
|
+
initTime: number | null;
|
|
26
|
+
memoryMB: number | null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Unified Embedding Manager - Clean, simple, reliable
|
|
30
|
+
*/
|
|
31
|
+
export declare class EmbeddingManager {
|
|
32
|
+
private model;
|
|
33
|
+
private precision;
|
|
34
|
+
private modelName;
|
|
35
|
+
private initialized;
|
|
36
|
+
private initTime;
|
|
37
|
+
private embedCount;
|
|
38
|
+
private locked;
|
|
39
|
+
private constructor();
|
|
40
|
+
/**
|
|
41
|
+
* Get the singleton instance
|
|
42
|
+
*/
|
|
43
|
+
static getInstance(): EmbeddingManager;
|
|
44
|
+
/**
|
|
45
|
+
* Initialize the model (happens once)
|
|
46
|
+
*/
|
|
47
|
+
init(): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Perform actual initialization
|
|
50
|
+
*/
|
|
51
|
+
private performInit;
|
|
52
|
+
/**
|
|
53
|
+
* Generate embeddings
|
|
54
|
+
*/
|
|
55
|
+
embed(text: string | string[]): Promise<Vector>;
|
|
56
|
+
/**
|
|
57
|
+
* Generate mock embeddings for unit tests
|
|
58
|
+
*/
|
|
59
|
+
private getMockEmbedding;
|
|
60
|
+
/**
|
|
61
|
+
* Get embedding function for compatibility
|
|
62
|
+
*/
|
|
63
|
+
getEmbeddingFunction(): EmbeddingFunction;
|
|
64
|
+
/**
|
|
65
|
+
* Determine model precision
|
|
66
|
+
*/
|
|
67
|
+
private determinePrecision;
|
|
68
|
+
/**
|
|
69
|
+
* Get models directory path
|
|
70
|
+
*/
|
|
71
|
+
private getModelsPath;
|
|
72
|
+
/**
|
|
73
|
+
* Get memory usage in MB
|
|
74
|
+
*/
|
|
75
|
+
private getMemoryUsage;
|
|
76
|
+
/**
|
|
77
|
+
* Get current statistics
|
|
78
|
+
*/
|
|
79
|
+
getStats(): EmbeddingStats;
|
|
80
|
+
/**
|
|
81
|
+
* Check if initialized
|
|
82
|
+
*/
|
|
83
|
+
isInitialized(): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Get current precision
|
|
86
|
+
*/
|
|
87
|
+
getPrecision(): ModelPrecision;
|
|
88
|
+
/**
|
|
89
|
+
* Validate precision matches expected
|
|
90
|
+
*/
|
|
91
|
+
validatePrecision(expected: ModelPrecision): void;
|
|
92
|
+
}
|
|
93
|
+
export declare const embeddingManager: EmbeddingManager;
|
|
94
|
+
/**
|
|
95
|
+
* Direct embed function
|
|
96
|
+
*/
|
|
97
|
+
export declare function embed(text: string | string[]): Promise<Vector>;
|
|
98
|
+
/**
|
|
99
|
+
* Get embedding function for compatibility
|
|
100
|
+
*/
|
|
101
|
+
export declare function getEmbeddingFunction(): EmbeddingFunction;
|
|
102
|
+
/**
|
|
103
|
+
* Get statistics
|
|
104
|
+
*/
|
|
105
|
+
export declare function getEmbeddingStats(): EmbeddingStats;
|
|
106
|
+
export {};
|