@soulcraft/brainy 6.5.0 → 6.6.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.
Files changed (37) hide show
  1. package/assets/models/all-MiniLM-L6-v2-q8/config.json +25 -0
  2. package/assets/models/all-MiniLM-L6-v2-q8/model.onnx +0 -0
  3. package/assets/models/all-MiniLM-L6-v2-q8/tokenizer.json +30686 -0
  4. package/assets/models/all-MiniLM-L6-v2-q8/vocab.json +1 -0
  5. package/dist/brainy.js +0 -6
  6. package/dist/config/index.d.ts +1 -3
  7. package/dist/config/index.js +2 -4
  8. package/dist/config/modelAutoConfig.d.ts +10 -17
  9. package/dist/config/modelAutoConfig.js +15 -88
  10. package/dist/config/sharedConfigManager.d.ts +1 -2
  11. package/dist/config/zeroConfig.d.ts +2 -13
  12. package/dist/config/zeroConfig.js +7 -15
  13. package/dist/critical/model-guardian.d.ts +5 -22
  14. package/dist/critical/model-guardian.js +38 -210
  15. package/dist/embeddings/EmbeddingManager.d.ts +7 -17
  16. package/dist/embeddings/EmbeddingManager.js +28 -136
  17. package/dist/embeddings/wasm/AssetLoader.d.ts +67 -0
  18. package/dist/embeddings/wasm/AssetLoader.js +238 -0
  19. package/dist/embeddings/wasm/EmbeddingPostProcessor.d.ts +60 -0
  20. package/dist/embeddings/wasm/EmbeddingPostProcessor.js +123 -0
  21. package/dist/embeddings/wasm/ONNXInferenceEngine.d.ts +55 -0
  22. package/dist/embeddings/wasm/ONNXInferenceEngine.js +154 -0
  23. package/dist/embeddings/wasm/WASMEmbeddingEngine.d.ts +82 -0
  24. package/dist/embeddings/wasm/WASMEmbeddingEngine.js +231 -0
  25. package/dist/embeddings/wasm/WordPieceTokenizer.d.ts +71 -0
  26. package/dist/embeddings/wasm/WordPieceTokenizer.js +264 -0
  27. package/dist/embeddings/wasm/index.d.ts +13 -0
  28. package/dist/embeddings/wasm/index.js +15 -0
  29. package/dist/embeddings/wasm/types.d.ts +114 -0
  30. package/dist/embeddings/wasm/types.js +25 -0
  31. package/dist/setup.d.ts +11 -11
  32. package/dist/setup.js +17 -31
  33. package/dist/types/brainy.types.d.ts +0 -5
  34. package/dist/utils/embedding.d.ts +45 -62
  35. package/dist/utils/embedding.js +61 -440
  36. package/package.json +10 -3
  37. package/scripts/download-model.cjs +175 -0
@@ -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
  */
@@ -2,39 +2,34 @@
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
- import { pipeline, env } from '@huggingface/transformers';
19
- import { isNode } from '../utils/environment.js';
14
+ import { WASMEmbeddingEngine } from './wasm/index.js';
20
15
  // Global state for true singleton across entire process
21
16
  let globalInstance = null;
22
17
  let globalInitPromise = null;
23
18
  /**
24
19
  * Unified Embedding Manager - Clean, simple, reliable
20
+ *
21
+ * Now powered by direct ONNX WASM for universal compatibility.
25
22
  */
26
23
  export class EmbeddingManager {
27
24
  constructor() {
28
- this.model = null;
29
- this.modelName = 'Xenova/all-MiniLM-L6-v2';
25
+ this.precision = 'q8';
26
+ this.modelName = 'all-MiniLM-L6-v2';
30
27
  this.initialized = false;
31
28
  this.initTime = null;
32
29
  this.embedCount = 0;
33
30
  this.locked = false;
34
- this.modelsPathCache = null;
35
- // Always use Q8 for optimal size/performance (99% accuracy, 75% smaller)
36
- this.precision = 'q8';
37
- console.log(`🎯 EmbeddingManager: Using Q8 precision`);
31
+ this.engine = WASMEmbeddingEngine.getInstance();
32
+ console.log('🎯 EmbeddingManager: Using Q8 precision (WASM)');
38
33
  }
39
34
  /**
40
35
  * Get the singleton instance
@@ -50,9 +45,10 @@ export class EmbeddingManager {
50
45
  */
51
46
  async init() {
52
47
  // In unit test mode, skip real model initialization
53
- const isTestMode = process.env.BRAINY_UNIT_TEST === 'true' || globalThis.__BRAINY_UNIT_TEST__;
48
+ const isTestMode = process.env.BRAINY_UNIT_TEST === 'true' ||
49
+ globalThis.__BRAINY_UNIT_TEST__;
54
50
  if (isTestMode) {
55
- // Production safeguard: Warn if mock mode is active but NODE_ENV is production
51
+ // Production safeguard
56
52
  if (process.env.NODE_ENV === 'production') {
57
53
  throw new Error('CRITICAL: Mock embeddings detected in production environment! ' +
58
54
  'BRAINY_UNIT_TEST or __BRAINY_UNIT_TEST__ is set while NODE_ENV=production. ' +
@@ -66,7 +62,7 @@ export class EmbeddingManager {
66
62
  return;
67
63
  }
68
64
  // Already initialized
69
- if (this.initialized && this.model) {
65
+ if (this.initialized && this.engine.isInitialized()) {
70
66
  return;
71
67
  }
72
68
  // Initialization in progress
@@ -88,62 +84,20 @@ export class EmbeddingManager {
88
84
  */
89
85
  async performInit() {
90
86
  const startTime = Date.now();
91
- console.log(`🚀 Initializing embedding model (${this.precision.toUpperCase()})...`);
92
87
  try {
93
- // Configure transformers.js environment
94
- const modelsPath = this.getModelsPath();
95
- env.cacheDir = modelsPath;
96
- env.allowLocalModels = true;
97
- env.useFSCache = true;
98
- // Check if models exist locally (only in Node.js)
99
- if (isNode()) {
100
- try {
101
- const nodeRequire = typeof require !== 'undefined' ? require : null;
102
- if (nodeRequire) {
103
- const path = nodeRequire('node:path');
104
- const fs = nodeRequire('node:fs');
105
- const modelPath = path.join(modelsPath, ...this.modelName.split('/'));
106
- const hasLocalModels = fs.existsSync(modelPath);
107
- if (hasLocalModels) {
108
- console.log('✅ Using cached models from:', modelPath);
109
- }
110
- }
111
- }
112
- catch {
113
- // Silently continue if require fails
114
- }
115
- }
116
- // Configure pipeline options for the selected precision
117
- const pipelineOptions = {
118
- cache_dir: modelsPath,
119
- local_files_only: false,
120
- // Always use Q8 precision
121
- dtype: 'q8',
122
- quantized: true,
123
- // Memory optimizations
124
- session_options: {
125
- enableCpuMemArena: false,
126
- enableMemPattern: false,
127
- interOpNumThreads: 1,
128
- intraOpNumThreads: 1,
129
- graphOptimizationLevel: 'disabled'
130
- }
131
- };
132
- // Load the model
133
- this.model = await pipeline('feature-extraction', this.modelName, pipelineOptions);
88
+ // Initialize WASM engine (handles all model loading)
89
+ await this.engine.initialize();
134
90
  // Lock precision after successful initialization
135
91
  this.locked = true;
136
92
  this.initialized = true;
137
93
  this.initTime = Date.now() - startTime;
138
94
  // Log success
139
95
  const memoryMB = this.getMemoryUsage();
140
- console.log(`✅ Model loaded in ${this.initTime}ms`);
141
96
  console.log(`📊 Precision: Q8 | Memory: ${memoryMB}MB`);
142
- console.log(`🔒 Configuration locked`);
97
+ console.log('🔒 Configuration locked');
143
98
  }
144
99
  catch (error) {
145
100
  this.initialized = false;
146
- this.model = null;
147
101
  throw new Error(`Failed to initialize embedding model: ${error instanceof Error ? error.message : String(error)}`);
148
102
  }
149
103
  }
@@ -151,10 +105,10 @@ export class EmbeddingManager {
151
105
  * Generate embeddings
152
106
  */
153
107
  async embed(text) {
154
- // Check for unit test environment - use mocks to prevent ONNX conflicts
155
- const isTestMode = process.env.BRAINY_UNIT_TEST === 'true' || globalThis.__BRAINY_UNIT_TEST__;
108
+ // Check for unit test environment
109
+ const isTestMode = process.env.BRAINY_UNIT_TEST === 'true' ||
110
+ globalThis.__BRAINY_UNIT_TEST__;
156
111
  if (isTestMode) {
157
- // Production safeguard
158
112
  if (process.env.NODE_ENV === 'production') {
159
113
  throw new Error('CRITICAL: Mock embeddings in production!');
160
114
  }
@@ -162,38 +116,26 @@ export class EmbeddingManager {
162
116
  }
163
117
  // Ensure initialized
164
118
  await this.init();
165
- if (!this.model) {
166
- throw new Error('Model not initialized');
167
- }
168
- // CRITICAL FIX: Ensure input is always a string
119
+ // Normalize input to string
169
120
  let input;
170
121
  if (Array.isArray(text)) {
171
- // Join array elements, converting each to string first
172
- input = text.map(t => typeof t === 'string' ? t : String(t)).join(' ');
122
+ input = text.map((t) => (typeof t === 'string' ? t : String(t))).join(' ');
173
123
  }
174
124
  else if (typeof text === 'string') {
175
125
  input = text;
176
126
  }
177
127
  else if (typeof text === 'object') {
178
- // Convert object to string representation
179
128
  input = JSON.stringify(text);
180
129
  }
181
130
  else {
182
- // This shouldn't happen but let's be defensive
183
131
  console.warn('EmbeddingManager.embed received unexpected input type:', typeof text);
184
132
  input = String(text);
185
133
  }
186
- // Generate embedding
187
- const output = await this.model(input, {
188
- pooling: 'mean',
189
- normalize: true
190
- });
191
- // Extract embedding vector
192
- const embedding = Array.from(output.data);
134
+ // Generate embedding using WASM engine
135
+ const embedding = await this.engine.embed(input);
193
136
  // Validate dimensions
194
137
  if (embedding.length !== 384) {
195
138
  console.warn(`Unexpected embedding dimension: ${embedding.length}`);
196
- // Pad or truncate
197
139
  if (embedding.length < 384) {
198
140
  return [...embedding, ...new Array(384 - embedding.length).fill(0)];
199
141
  }
@@ -208,7 +150,6 @@ export class EmbeddingManager {
208
150
  * Generate mock embeddings for unit tests
209
151
  */
210
152
  getMockEmbedding(text) {
211
- // Use the same mock logic as setup-unit.ts for consistency
212
153
  const input = Array.isArray(text) ? text.join(' ') : text;
213
154
  const str = typeof input === 'string' ? input : JSON.stringify(input);
214
155
  const vector = new Array(384).fill(0);
@@ -220,7 +161,6 @@ export class EmbeddingManager {
220
161
  for (let i = 0; i < 384; i++) {
221
162
  vector[i] += Math.sin(i * 0.1 + str.length) * 0.1;
222
163
  }
223
- // Track mock embedding count
224
164
  this.embedCount++;
225
165
  return vector;
226
166
  }
@@ -232,55 +172,6 @@ export class EmbeddingManager {
232
172
  return await this.embed(data);
233
173
  };
234
174
  }
235
- /**
236
- * Get models directory path
237
- * Note: In browser environments, returns a simple default path
238
- * In Node.js, checks multiple locations for the models directory
239
- */
240
- getModelsPath() {
241
- // In browser environments, use a default path
242
- if (!isNode()) {
243
- return './models';
244
- }
245
- // Node.js-specific model path resolution
246
- // Cache the result for performance
247
- if (!this.modelsPathCache) {
248
- this.modelsPathCache = this.resolveModelsPathSync();
249
- }
250
- return this.modelsPathCache;
251
- }
252
- resolveModelsPathSync() {
253
- // For Node.js environments, we can safely assume these modules exist
254
- // TypeScript will handle the imports at build time
255
- // At runtime, these will only be called if isNode() is true
256
- // Default fallback path
257
- const defaultPath = './models';
258
- try {
259
- // Create a conditional require function that only works in Node
260
- const nodeRequire = typeof require !== 'undefined' ? require : null;
261
- if (!nodeRequire)
262
- return defaultPath;
263
- const fs = nodeRequire('node:fs');
264
- const path = nodeRequire('node:path');
265
- const paths = [
266
- process.env.BRAINY_MODELS_PATH,
267
- './models',
268
- path.join(process.cwd(), 'models'),
269
- path.join(process.env.HOME || '', '.brainy', 'models')
270
- ];
271
- for (const p of paths) {
272
- if (p && fs.existsSync(p)) {
273
- return p;
274
- }
275
- }
276
- // Default Node.js path
277
- return path.join(process.cwd(), 'models');
278
- }
279
- catch {
280
- // Fallback if require fails
281
- return defaultPath;
282
- }
283
- }
284
175
  /**
285
176
  * Get memory usage in MB
286
177
  */
@@ -295,13 +186,14 @@ export class EmbeddingManager {
295
186
  * Get current statistics
296
187
  */
297
188
  getStats() {
189
+ const engineStats = this.engine.getStats();
298
190
  return {
299
191
  initialized: this.initialized,
300
192
  precision: this.precision,
301
193
  modelName: this.modelName,
302
- embedCount: this.embedCount,
194
+ embedCount: this.embedCount + engineStats.embedCount,
303
195
  initTime: this.initTime,
304
- memoryMB: this.getMemoryUsage()
196
+ memoryMB: this.getMemoryUsage(),
305
197
  };
306
198
  }
307
199
  /**