@soulcraft/brainy 6.5.0 → 6.6.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.
@@ -5,15 +5,14 @@
5
5
  * Without the exact model, users CANNOT access their data
6
6
  *
7
7
  * Requirements:
8
- * 1. Model MUST be Xenova/all-MiniLM-L6-v2 (never changes)
9
- * 2. Model MUST be available at runtime
8
+ * 1. Model MUST be all-MiniLM-L6-v2-q8 (bundled in package)
9
+ * 2. Model MUST be available at runtime (embedded in npm package)
10
10
  * 3. Model MUST produce consistent 384-dim embeddings
11
11
  * 4. System MUST fail fast if model unavailable in production
12
12
  */
13
13
  export declare class ModelGuardian {
14
14
  private static instance;
15
15
  private isVerified;
16
- private modelPath;
17
16
  private lastVerification;
18
17
  private constructor();
19
18
  static getInstance(): ModelGuardian;
@@ -23,34 +22,18 @@ export declare class ModelGuardian {
23
22
  */
24
23
  ensureCriticalModel(): Promise<void>;
25
24
  /**
26
- * Verify the local model files exist and are correct
25
+ * Verify the bundled WASM model works correctly
27
26
  */
28
- private verifyLocalModel;
29
- /**
30
- * Compute SHA256 hash of a file
31
- */
32
- private computeFileHash;
33
- /**
34
- * Download model from a fallback source
35
- */
36
- private downloadFromSource;
37
- /**
38
- * Configure transformers.js to use verified local model
39
- */
40
- private configureTransformers;
41
- /**
42
- * Detect where models should be stored
43
- */
44
- private detectModelPath;
27
+ private verifyBundledModel;
45
28
  /**
46
29
  * Get model status for diagnostics
47
30
  */
48
31
  getStatus(): Promise<{
49
32
  verified: boolean;
50
- path: string;
51
33
  lastVerification: Date | null;
52
34
  modelName: string;
53
35
  dimensions: number;
36
+ bundled: boolean;
54
37
  }>;
55
38
  /**
56
39
  * Force re-verification (for testing)
@@ -5,51 +5,24 @@
5
5
  * Without the exact model, users CANNOT access their data
6
6
  *
7
7
  * Requirements:
8
- * 1. Model MUST be Xenova/all-MiniLM-L6-v2 (never changes)
9
- * 2. Model MUST be available at runtime
8
+ * 1. Model MUST be all-MiniLM-L6-v2-q8 (bundled in package)
9
+ * 2. Model MUST be available at runtime (embedded in npm package)
10
10
  * 3. Model MUST produce consistent 384-dim embeddings
11
11
  * 4. System MUST fail fast if model unavailable in production
12
12
  */
13
- import { env } from '@huggingface/transformers';
13
+ import { WASMEmbeddingEngine } from '../embeddings/wasm/index.js';
14
14
  // CRITICAL: These values MUST NEVER CHANGE
15
15
  const CRITICAL_MODEL_CONFIG = {
16
- modelName: 'Xenova/all-MiniLM-L6-v2',
17
- modelHash: {
18
- // SHA256 of model.onnx - computed from actual model
19
- 'onnx/model.onnx': 'add_actual_hash_here',
20
- 'tokenizer.json': 'add_actual_hash_here'
21
- },
22
- modelSize: {
23
- 'onnx/model.onnx': 90387606, // Exact size in bytes (updated to match actual file)
24
- 'tokenizer.json': 711661
25
- },
16
+ modelName: 'all-MiniLM-L6-v2-q8',
26
17
  embeddingDimensions: 384,
27
- fallbackSources: [
28
- // Primary: Our Google Cloud Storage CDN (we control this, fastest)
29
- {
30
- name: 'Soulcraft CDN (Primary)',
31
- url: 'https://models.soulcraft.com/models/all-MiniLM-L6-v2.tar.gz',
32
- type: 'tarball'
33
- },
34
- // Secondary: GitHub releases backup
35
- {
36
- name: 'GitHub Backup',
37
- url: 'https://github.com/soulcraftlabs/brainy-models/releases/download/v1.0.0/all-MiniLM-L6-v2.tar.gz',
38
- type: 'tarball'
39
- },
40
- // Tertiary: Hugging Face (original source)
41
- {
42
- name: 'Hugging Face',
43
- url: 'huggingface',
44
- type: 'transformers'
45
- }
46
- ]
18
+ // Model is bundled in package - no external downloads needed
19
+ bundled: true
47
20
  };
48
21
  export class ModelGuardian {
49
22
  constructor() {
50
23
  this.isVerified = false;
51
24
  this.lastVerification = null;
52
- this.modelPath = this.detectModelPath();
25
+ // Model is bundled - no path detection needed
53
26
  }
54
27
  static getInstance() {
55
28
  if (!ModelGuardian.instance) {
@@ -62,200 +35,55 @@ export class ModelGuardian {
62
35
  * This MUST be called before any embedding operations
63
36
  */
64
37
  async ensureCriticalModel() {
65
- console.log('DEBUG: ensureCriticalModel called');
66
- console.log('🛡️ MODEL GUARDIAN: Verifying critical model availability...');
67
- console.log(`🚀 Debug: Model path: ${this.modelPath}`);
68
- console.log(`🚀 Debug: Already verified: ${this.isVerified}`);
69
38
  // Check if already verified in this session
70
39
  if (this.isVerified && this.lastVerification) {
71
40
  const hoursSinceVerification = (Date.now() - this.lastVerification.getTime()) / (1000 * 60 * 60);
72
41
  if (hoursSinceVerification < 24) {
73
- console.log('✅ Model previously verified in this session');
74
42
  return;
75
43
  }
76
44
  }
77
- // Step 1: Check if model exists locally
78
- console.log('🔍 Debug: Calling verifyLocalModel()');
79
- const modelExists = await this.verifyLocalModel();
80
- if (modelExists) {
81
- console.log('✅ Critical model verified locally');
45
+ // Verify the bundled WASM model works
46
+ const modelWorks = await this.verifyBundledModel();
47
+ if (modelWorks) {
82
48
  this.isVerified = true;
83
49
  this.lastVerification = new Date();
84
- this.configureTransformers();
85
50
  return;
86
51
  }
87
- // Step 2: In production, FAIL FAST (Node.js only)
88
- if (typeof window === 'undefined' && process.env.NODE_ENV === 'production' && !process.env.BRAINY_ALLOW_RUNTIME_DOWNLOAD) {
89
- throw new Error('🚨 CRITICAL FAILURE: Transformer model not found in production!\n' +
90
- 'The model is REQUIRED for Brainy to function.\n' +
91
- 'Users CANNOT access their data without it.\n' +
92
- 'Solution: Run "npm run download-models" during build stage.');
93
- }
94
- // Step 3: Attempt to download from fallback sources
95
- console.warn('⚠️ Model not found locally, attempting download...');
96
- for (const source of CRITICAL_MODEL_CONFIG.fallbackSources) {
97
- try {
98
- console.log(`📥 Trying ${source.name}...`);
99
- await this.downloadFromSource(source);
100
- // Verify the download
101
- if (await this.verifyLocalModel()) {
102
- console.log(`✅ Successfully downloaded from ${source.name}`);
103
- this.isVerified = true;
104
- this.lastVerification = new Date();
105
- this.configureTransformers();
106
- return;
107
- }
108
- }
109
- catch (error) {
110
- console.warn(`❌ ${source.name} failed:`, error.message);
111
- }
112
- }
113
- // Step 4: CRITICAL FAILURE
114
- throw new Error('🚨 CRITICAL FAILURE: Cannot obtain transformer model!\n' +
115
- 'Tried all fallback sources.\n' +
116
- 'Brainy CANNOT function without the model.\n' +
117
- 'Users CANNOT access their data.\n' +
118
- 'Please check network connectivity or pre-download models.');
52
+ // CRITICAL FAILURE
53
+ throw new Error('🚨 CRITICAL FAILURE: Bundled transformer model not working!\n' +
54
+ 'The model is REQUIRED for Brainy to function.\n' +
55
+ 'Users CANNOT access their data without it.\n' +
56
+ 'This indicates a package installation issue.');
119
57
  }
120
58
  /**
121
- * Verify the local model files exist and are correct
59
+ * Verify the bundled WASM model works correctly
122
60
  */
123
- async verifyLocalModel() {
124
- // Browser doesn't have local file access
125
- if (typeof window !== 'undefined') {
126
- console.log('⚠️ Model verification skipped in browser environment');
127
- return false;
128
- }
129
- // Dynamically import Node.js modules
130
- const fs = await import('node:fs');
131
- const fsPromises = await import('node:fs/promises');
132
- const path = await import('node:path');
133
- const modelBasePath = path.join(this.modelPath, ...CRITICAL_MODEL_CONFIG.modelName.split('/'));
134
- console.log(`🔍 Debug: Checking model at path: ${modelBasePath}`);
135
- console.log(`🔍 Debug: Model path components: ${this.modelPath} + ${CRITICAL_MODEL_CONFIG.modelName.split('/')}`);
136
- // Check critical files
137
- const criticalFiles = [
138
- 'onnx/model.onnx',
139
- 'tokenizer.json',
140
- 'config.json'
141
- ];
142
- for (const file of criticalFiles) {
143
- const filePath = path.join(modelBasePath, file);
144
- console.log(`🔍 Debug: Checking file: ${filePath}`);
145
- if (!fs.existsSync(filePath)) {
146
- console.log(`❌ Missing critical file: ${file} at ${filePath}`);
61
+ async verifyBundledModel() {
62
+ try {
63
+ const engine = WASMEmbeddingEngine.getInstance();
64
+ // Initialize the engine (loads bundled model)
65
+ await engine.initialize();
66
+ // Test embedding generation
67
+ const testEmbedding = await engine.embed('test verification');
68
+ // Verify dimensions
69
+ if (testEmbedding.length !== CRITICAL_MODEL_CONFIG.embeddingDimensions) {
70
+ console.error(`❌ CRITICAL: Model dimension mismatch!\n` +
71
+ `Expected: ${CRITICAL_MODEL_CONFIG.embeddingDimensions}\n` +
72
+ `Got: ${testEmbedding.length}`);
147
73
  return false;
148
74
  }
149
- // Verify size for critical files
150
- if (CRITICAL_MODEL_CONFIG.modelSize[file]) {
151
- const stats = await fsPromises.stat(filePath);
152
- const expectedSize = CRITICAL_MODEL_CONFIG.modelSize[file];
153
- if (Math.abs(stats.size - expectedSize) > 1000) { // Allow 1KB variance
154
- console.error(`❌ CRITICAL: Model file size mismatch!\n` +
155
- `File: ${file}\n` +
156
- `Expected: ${expectedSize} bytes\n` +
157
- `Actual: ${stats.size} bytes\n` +
158
- `This indicates model corruption or version mismatch!`);
159
- return false;
160
- }
161
- }
162
- // SHA256 verification for ultimate security
163
- if (CRITICAL_MODEL_CONFIG.modelHash && CRITICAL_MODEL_CONFIG.modelHash[file]) {
164
- const hash = await this.computeFileHash(filePath);
165
- if (hash !== CRITICAL_MODEL_CONFIG.modelHash[file]) {
166
- console.error(`❌ CRITICAL: Model hash mismatch for ${file}!\n` +
167
- `Expected: ${CRITICAL_MODEL_CONFIG.modelHash[file]}\n` +
168
- `Got: ${hash}\n` +
169
- `This indicates model tampering or corruption!`);
170
- return false;
171
- }
75
+ // Verify normalization (should be unit length)
76
+ const norm = Math.sqrt(testEmbedding.reduce((sum, v) => sum + v * v, 0));
77
+ if (Math.abs(norm - 1.0) > 0.01) {
78
+ console.error(`❌ CRITICAL: Embeddings not normalized! Norm: ${norm}`);
79
+ return false;
172
80
  }
173
- }
174
- return true;
175
- }
176
- /**
177
- * Compute SHA256 hash of a file
178
- */
179
- async computeFileHash(filePath) {
180
- try {
181
- const { readFile } = await import('node:fs/promises');
182
- const { createHash } = await import('node:crypto');
183
- const fileBuffer = await readFile(filePath);
184
- const hash = createHash('sha256').update(fileBuffer).digest('hex');
185
- return hash;
81
+ return true;
186
82
  }
187
83
  catch (error) {
188
- console.error(`Failed to compute hash for ${filePath}:`, error);
189
- return '';
190
- }
191
- }
192
- /**
193
- * Download model from a fallback source
194
- */
195
- async downloadFromSource(source) {
196
- if (source.type === 'transformers') {
197
- // Use transformers.js native download
198
- const { pipeline } = await import('@huggingface/transformers');
199
- env.cacheDir = this.modelPath;
200
- env.allowRemoteModels = true;
201
- const extractor = await pipeline('feature-extraction', CRITICAL_MODEL_CONFIG.modelName);
202
- // Test the model
203
- const test = await extractor('test', { pooling: 'mean', normalize: true });
204
- if (test.data.length !== CRITICAL_MODEL_CONFIG.embeddingDimensions) {
205
- throw new Error(`CRITICAL: Model dimension mismatch! ` +
206
- `Expected ${CRITICAL_MODEL_CONFIG.embeddingDimensions}, ` +
207
- `got ${test.data.length}`);
208
- }
209
- }
210
- else if (source.type === 'tarball') {
211
- // Tarball extraction would require additional dependencies
212
- // Skip this source and try next fallback
213
- console.warn(`⚠️ Tarball extraction not available for ${source.name}. Trying next source...`);
214
- return; // Will continue to next source in the loop
215
- }
216
- }
217
- /**
218
- * Configure transformers.js to use verified local model
219
- */
220
- configureTransformers() {
221
- env.localModelPath = this.modelPath;
222
- env.allowRemoteModels = false; // Force local only after verification
223
- console.log('🔒 Transformers configured to use verified local model');
224
- }
225
- /**
226
- * Detect where models should be stored
227
- */
228
- detectModelPath() {
229
- // Browser always uses default path
230
- if (typeof window !== 'undefined') {
231
- return './models';
232
- }
233
- // Use require for synchronous access in Node.js
234
- try {
235
- const fs = require('node:fs');
236
- const path = require('node:path');
237
- const candidates = [
238
- process.env.BRAINY_MODELS_PATH,
239
- './models',
240
- path.join(process.cwd(), 'models'),
241
- path.join(process.env.HOME || '', '.brainy', 'models'),
242
- '/opt/models', // Lambda/container path
243
- env.cacheDir
244
- ];
245
- for (const candidatePath of candidates) {
246
- if (candidatePath && fs.existsSync(candidatePath)) {
247
- const modelPath = path.join(candidatePath, ...CRITICAL_MODEL_CONFIG.modelName.split('/'));
248
- if (fs.existsSync(path.join(modelPath, 'onnx', 'model.onnx'))) {
249
- return candidatePath; // Return the models directory, not its parent
250
- }
251
- }
252
- }
253
- }
254
- catch (e) {
255
- // If Node.js modules not available, return default
84
+ console.error('❌ Model verification failed:', error);
85
+ return false;
256
86
  }
257
- // Default
258
- return './models';
259
87
  }
260
88
  /**
261
89
  * Get model status for diagnostics
@@ -263,10 +91,10 @@ export class ModelGuardian {
263
91
  async getStatus() {
264
92
  return {
265
93
  verified: this.isVerified,
266
- path: this.modelPath,
267
94
  lastVerification: this.lastVerification,
268
95
  modelName: CRITICAL_MODEL_CONFIG.modelName,
269
- dimensions: CRITICAL_MODEL_CONFIG.embeddingDimensions
96
+ dimensions: CRITICAL_MODEL_CONFIG.embeddingDimensions,
97
+ bundled: CRITICAL_MODEL_CONFIG.bundled
270
98
  };
271
99
  }
272
100
  /**
@@ -2,18 +2,14 @@
2
2
  * Unified Embedding Manager
3
3
  *
4
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.
5
+ * Uses direct ONNX WASM inference for universal compatibility.
7
6
  *
8
7
  * Features:
9
8
  * - Singleton pattern ensures ONE model instance
10
- * - Automatic Q8 (default) or FP32 precision
11
- * - Model downloading and caching
12
- * - Thread-safe initialization
9
+ * - Direct ONNX WASM (no transformers.js dependency)
10
+ * - Bundled model (no runtime downloads)
11
+ * - Works everywhere: Node.js, Bun, Bun --compile, browsers
13
12
  * - Memory monitoring
14
- *
15
- * This replaces: SingletonModelManager, TransformerEmbedding, ModelPrecisionManager,
16
- * hybridModelManager, universalMemoryManager, and more.
17
13
  */
18
14
  import { Vector, EmbeddingFunction } from '../coreTypes.js';
19
15
  export type ModelPrecision = 'q8' | 'fp32';
@@ -27,9 +23,11 @@ interface EmbeddingStats {
27
23
  }
28
24
  /**
29
25
  * Unified Embedding Manager - Clean, simple, reliable
26
+ *
27
+ * Now powered by direct ONNX WASM for universal compatibility.
30
28
  */
31
29
  export declare class EmbeddingManager {
32
- private model;
30
+ private engine;
33
31
  private precision;
34
32
  private modelName;
35
33
  private initialized;
@@ -61,14 +59,6 @@ export declare class EmbeddingManager {
61
59
  * Get embedding function for compatibility
62
60
  */
63
61
  getEmbeddingFunction(): EmbeddingFunction;
64
- /**
65
- * Get models directory path
66
- * Note: In browser environments, returns a simple default path
67
- * In Node.js, checks multiple locations for the models directory
68
- */
69
- private getModelsPath;
70
- private modelsPathCache;
71
- private resolveModelsPathSync;
72
62
  /**
73
63
  * Get memory usage in MB
74
64
  */