ruvector 0.1.58 → 0.1.60

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/bin/cli.js CHANGED
@@ -2096,32 +2096,46 @@ program
2096
2096
  // Full RuVector Stack: VectorDB + SONA + Attention
2097
2097
  // ============================================
2098
2098
 
2099
- // Try to load the full IntelligenceEngine, fallback to simple implementation
2099
+ // LAZY LOADING: IntelligenceEngine is only loaded when first accessed
2100
+ // This reduces CLI startup from ~1000ms to ~70ms for simple operations
2100
2101
  let IntelligenceEngine = null;
2101
- let engineAvailable = false;
2102
+ let engineLoadAttempted = false;
2102
2103
 
2103
- try {
2104
- const core = require('../dist/core/intelligence-engine.js');
2105
- IntelligenceEngine = core.IntelligenceEngine || core.default;
2106
- engineAvailable = true;
2107
- } catch (e) {
2108
- // IntelligenceEngine not available, use fallback
2104
+ function loadIntelligenceEngine() {
2105
+ if (engineLoadAttempted) return IntelligenceEngine;
2106
+ engineLoadAttempted = true;
2107
+ try {
2108
+ const core = require('../dist/core/intelligence-engine.js');
2109
+ IntelligenceEngine = core.IntelligenceEngine || core.default;
2110
+ } catch (e) {
2111
+ // IntelligenceEngine not available, use fallback
2112
+ }
2113
+ return IntelligenceEngine;
2109
2114
  }
2110
2115
 
2111
2116
  class Intelligence {
2112
- constructor() {
2117
+ constructor(options = {}) {
2113
2118
  this.intelPath = this.getIntelPath();
2114
2119
  this.data = this.load();
2115
2120
  this.alpha = 0.1;
2116
2121
  this.lastEditedFile = null;
2117
2122
  this.sessionStartTime = null;
2123
+ this._engine = null;
2124
+ this._engineInitialized = false;
2125
+ // Skip engine init for fast operations (trajectory, coedit, error commands)
2126
+ this._skipEngine = options.skipEngine || false;
2127
+ }
2128
+
2129
+ // Lazy getter for engine - only initializes when first accessed
2130
+ getEngine() {
2131
+ if (this._skipEngine) return null;
2132
+ if (this._engineInitialized) return this._engine;
2133
+ this._engineInitialized = true;
2118
2134
 
2119
- // Initialize full RuVector engine if available
2120
- this.engine = null;
2121
- if (engineAvailable && IntelligenceEngine) {
2135
+ const EngineClass = loadIntelligenceEngine();
2136
+ if (EngineClass) {
2122
2137
  try {
2123
- this.engine = new IntelligenceEngine({
2124
- // Let ONNX auto-select 384d if available, else 256d
2138
+ this._engine = new EngineClass({
2125
2139
  maxMemories: 100000,
2126
2140
  maxEpisodes: 50000,
2127
2141
  enableSona: true,
@@ -2131,13 +2145,29 @@ class Intelligence {
2131
2145
  });
2132
2146
  // Import existing data into engine
2133
2147
  if (this.data) {
2134
- this.engine.import(this.convertLegacyData(this.data), true);
2148
+ this._engine.import(this.convertLegacyData(this.data), true);
2135
2149
  }
2136
2150
  } catch (e) {
2137
- // Engine initialization failed, use fallback
2138
- this.engine = null;
2151
+ this._engine = null;
2139
2152
  }
2140
2153
  }
2154
+ return this._engine;
2155
+ }
2156
+
2157
+ // Property alias for backwards compatibility
2158
+ get engine() {
2159
+ return this.getEngine();
2160
+ }
2161
+
2162
+ // Check if engine is available WITHOUT triggering initialization
2163
+ // Use this for optional engine features that have fallbacks
2164
+ hasEngine() {
2165
+ return this._engineInitialized && this._engine !== null;
2166
+ }
2167
+
2168
+ // Get engine only if already initialized (doesn't trigger lazy load)
2169
+ getEngineIfReady() {
2170
+ return this._engineInitialized ? this._engine : null;
2141
2171
  }
2142
2172
 
2143
2173
  // Convert legacy data format to new engine format
@@ -2221,10 +2251,11 @@ class Intelligence {
2221
2251
  const dir = path.dirname(this.intelPath);
2222
2252
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
2223
2253
 
2224
- // If engine is available, export its data
2225
- if (this.engine) {
2254
+ // If engine is already initialized, export its data (don't trigger lazy load)
2255
+ const eng = this.getEngineIfReady();
2256
+ if (eng) {
2226
2257
  try {
2227
- const engineData = this.engine.export();
2258
+ const engineData = eng.export();
2228
2259
  // Merge engine data with legacy format for compatibility
2229
2260
  this.data.patterns = {};
2230
2261
  for (const [state, actions] of Object.entries(engineData.routingPatterns || {})) {
@@ -2249,9 +2280,11 @@ class Intelligence {
2249
2280
 
2250
2281
  // Use engine embedding if available (256-dim with attention), otherwise fallback (64-dim hash)
2251
2282
  embed(text) {
2252
- if (this.engine) {
2283
+ // Only use engine if already initialized (don't trigger lazy load for embeddings)
2284
+ const eng = this.getEngineIfReady();
2285
+ if (eng) {
2253
2286
  try {
2254
- return this.engine.embed(text);
2287
+ return eng.embed(text);
2255
2288
  } catch {}
2256
2289
  }
2257
2290
  // Fallback: simple 64-dim hash embedding
@@ -2302,9 +2335,10 @@ class Intelligence {
2302
2335
  if (this.data.memories.length > 5000) this.data.memories.splice(0, 1000);
2303
2336
  this.data.stats.total_memories = this.data.memories.length;
2304
2337
 
2305
- // Also store in engine if available
2306
- if (this.engine) {
2307
- this.engine.remember(content, memoryType).catch(() => {});
2338
+ // Also store in engine if already initialized (don't trigger lazy load)
2339
+ const eng = this.getEngineIfReady();
2340
+ if (eng) {
2341
+ eng.remember(content, memoryType).catch(() => {});
2308
2342
  }
2309
2343
 
2310
2344
  return id;
@@ -2351,9 +2385,10 @@ class Intelligence {
2351
2385
  p.last_update = this.now();
2352
2386
  this.data.stats.total_patterns = Object.keys(this.data.patterns).length;
2353
2387
 
2354
- // Record episode in engine if available
2355
- if (this.engine) {
2356
- this.engine.recordEpisode(state, action, reward, state, false).catch(() => {});
2388
+ // Record episode in engine if already initialized (don't trigger lazy load)
2389
+ const eng = this.getEngineIfReady();
2390
+ if (eng) {
2391
+ eng.recordEpisode(state, action, reward, state, false).catch(() => {});
2357
2392
  }
2358
2393
  }
2359
2394
 
@@ -2364,9 +2399,10 @@ class Intelligence {
2364
2399
  if (this.data.trajectories.length > 1000) this.data.trajectories.splice(0, 200);
2365
2400
  this.data.stats.total_trajectories = this.data.trajectories.length;
2366
2401
 
2367
- // End trajectory in engine if available
2368
- if (this.engine) {
2369
- this.engine.endTrajectory(reward > 0.5, reward);
2402
+ // End trajectory in engine if already initialized (don't trigger lazy load)
2403
+ const eng = this.getEngineIfReady();
2404
+ if (eng) {
2405
+ eng.endTrajectory(reward > 0.5, reward);
2370
2406
  }
2371
2407
 
2372
2408
  return id;
@@ -2424,9 +2460,10 @@ class Intelligence {
2424
2460
  const { action, confidence } = this.suggest(state, agents);
2425
2461
  const reason = confidence > 0.5 ? 'learned from past success' : confidence > 0 ? 'based on patterns' : `default for ${fileType} files`;
2426
2462
 
2427
- // Begin trajectory in engine
2428
- if (this.engine) {
2429
- this.engine.beginTrajectory(task || operation, file);
2463
+ // Begin trajectory in engine (only if already initialized)
2464
+ const eng = this.getEngineIfReady();
2465
+ if (eng) {
2466
+ eng.beginTrajectory(task || operation, file);
2430
2467
  }
2431
2468
 
2432
2469
  return { agent: action, confidence, reason };
@@ -2453,17 +2490,19 @@ class Intelligence {
2453
2490
  else this.data.file_sequences.push({ from_file: fromFile, to_file: toFile, count: 1 });
2454
2491
  this.lastEditedFile = toFile;
2455
2492
 
2456
- // Record in engine
2457
- if (this.engine) {
2458
- this.engine.recordCoEdit(fromFile, toFile);
2493
+ // Record in engine (only if already initialized)
2494
+ const eng = this.getEngineIfReady();
2495
+ if (eng) {
2496
+ eng.recordCoEdit(fromFile, toFile);
2459
2497
  }
2460
2498
  }
2461
2499
 
2462
2500
  suggestNext(file, limit = 3) {
2463
- // Try engine first
2464
- if (this.engine) {
2501
+ // Try engine first (only if already initialized)
2502
+ const eng = this.getEngineIfReady();
2503
+ if (eng) {
2465
2504
  try {
2466
- const results = this.engine.getLikelyNextFiles(file, limit);
2505
+ const results = eng.getLikelyNextFiles(file, limit);
2467
2506
  if (results.length > 0) {
2468
2507
  return results.map(r => ({ file: r.file, score: r.count }));
2469
2508
  }
@@ -2486,16 +2525,19 @@ class Intelligence {
2486
2525
  }
2487
2526
  this.data.stats.total_errors = Object.keys(this.data.errors).length;
2488
2527
 
2489
- if (this.engine) {
2490
- this.engine.recordErrorFix(errorPattern, fix);
2528
+ // Record in engine (only if already initialized)
2529
+ const eng = this.getEngineIfReady();
2530
+ if (eng) {
2531
+ eng.recordErrorFix(errorPattern, fix);
2491
2532
  }
2492
2533
  }
2493
2534
 
2494
2535
  getSuggestedFixes(error) {
2495
- // Try engine first (uses embedding similarity)
2496
- if (this.engine) {
2536
+ // Try engine first (only if already initialized)
2537
+ const eng = this.getEngineIfReady();
2538
+ if (eng) {
2497
2539
  try {
2498
- const fixes = this.engine.getSuggestedFixes(error);
2540
+ const fixes = eng.getSuggestedFixes(error);
2499
2541
  if (fixes.length > 0) return fixes;
2500
2542
  } catch {}
2501
2543
  }
@@ -2525,9 +2567,11 @@ class Intelligence {
2525
2567
  stats() {
2526
2568
  const baseStats = this.data.stats;
2527
2569
 
2528
- if (this.engine) {
2570
+ // Only use engine if already initialized (don't trigger lazy load for optional stats)
2571
+ const eng = this.getEngineIfReady();
2572
+ if (eng) {
2529
2573
  try {
2530
- const engineStats = this.engine.getStats();
2574
+ const engineStats = eng.getStats();
2531
2575
  return {
2532
2576
  ...baseStats,
2533
2577
  // Engine stats
@@ -2554,9 +2598,10 @@ class Intelligence {
2554
2598
  this.data.stats.last_session = this.now();
2555
2599
  this.sessionStartTime = this.now();
2556
2600
 
2557
- // Tick engine for background learning
2558
- if (this.engine) {
2559
- this.engine.tick();
2601
+ // Tick engine for background learning (only if already initialized)
2602
+ const eng = this.getEngineIfReady();
2603
+ if (eng) {
2604
+ eng.tick();
2560
2605
  }
2561
2606
  }
2562
2607
 
@@ -2564,9 +2609,10 @@ class Intelligence {
2564
2609
  const duration = this.now() - (this.sessionStartTime || this.data.stats.last_session);
2565
2610
  const actions = this.data.trajectories.filter(t => t.timestamp >= this.data.stats.last_session).length;
2566
2611
 
2567
- // Force learning cycle
2568
- if (this.engine) {
2569
- this.engine.forceLearn();
2612
+ // Force learning cycle (only if engine already initialized)
2613
+ const eng = this.getEngineIfReady();
2614
+ if (eng) {
2615
+ eng.forceLearn();
2570
2616
  }
2571
2617
 
2572
2618
  // Save all data
@@ -3099,7 +3145,7 @@ hooksCmd.command('trajectory-begin')
3099
3145
  .requiredOption('-c, --context <context>', 'Task or operation context')
3100
3146
  .option('-a, --agent <agent>', 'Agent performing the task', 'unknown')
3101
3147
  .action((opts) => {
3102
- const intel = new Intelligence();
3148
+ const intel = new Intelligence({ skipEngine: true }); // Fast mode - no engine needed
3103
3149
  const trajId = `traj_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
3104
3150
  if (!intel.data.activeTrajectories) intel.data.activeTrajectories = {};
3105
3151
  intel.data.activeTrajectories[trajId] = {
@@ -3119,7 +3165,7 @@ hooksCmd.command('trajectory-step')
3119
3165
  .option('-r, --result <result>', 'Result of action')
3120
3166
  .option('--reward <reward>', 'Reward signal (0-1)', '0.5')
3121
3167
  .action((opts) => {
3122
- const intel = new Intelligence();
3168
+ const intel = new Intelligence({ skipEngine: true }); // Fast mode
3123
3169
  const trajectories = intel.data.activeTrajectories || {};
3124
3170
  const trajIds = Object.keys(trajectories);
3125
3171
  if (trajIds.length === 0) {
@@ -3142,7 +3188,7 @@ hooksCmd.command('trajectory-end')
3142
3188
  .option('--success', 'Task succeeded')
3143
3189
  .option('--quality <quality>', 'Quality score (0-1)', '0.5')
3144
3190
  .action((opts) => {
3145
- const intel = new Intelligence();
3191
+ const intel = new Intelligence({ skipEngine: true }); // Fast mode
3146
3192
  const trajectories = intel.data.activeTrajectories || {};
3147
3193
  const trajIds = Object.keys(trajectories);
3148
3194
  if (trajIds.length === 0) {
@@ -3176,7 +3222,7 @@ hooksCmd.command('coedit-record')
3176
3222
  .requiredOption('-p, --primary <file>', 'Primary file being edited')
3177
3223
  .requiredOption('-r, --related <files...>', 'Related files edited together')
3178
3224
  .action((opts) => {
3179
- const intel = new Intelligence();
3225
+ const intel = new Intelligence({ skipEngine: true }); // Fast mode
3180
3226
  if (!intel.data.coEditPatterns) intel.data.coEditPatterns = {};
3181
3227
  if (!intel.data.coEditPatterns[opts.primary]) intel.data.coEditPatterns[opts.primary] = {};
3182
3228
 
@@ -3192,7 +3238,7 @@ hooksCmd.command('coedit-suggest')
3192
3238
  .requiredOption('-f, --file <file>', 'Current file')
3193
3239
  .option('-k, --top-k <n>', 'Number of suggestions', '5')
3194
3240
  .action((opts) => {
3195
- const intel = new Intelligence();
3241
+ const intel = new Intelligence({ skipEngine: true }); // Fast mode
3196
3242
  let suggestions = [];
3197
3243
 
3198
3244
  if (intel.data.coEditPatterns && intel.data.coEditPatterns[opts.file]) {
@@ -3211,7 +3257,7 @@ hooksCmd.command('error-record')
3211
3257
  .requiredOption('-x, --fix <fix>', 'Fix that resolved the error')
3212
3258
  .option('-f, --file <file>', 'File where error occurred')
3213
3259
  .action((opts) => {
3214
- const intel = new Intelligence();
3260
+ const intel = new Intelligence({ skipEngine: true }); // Fast mode
3215
3261
  if (!intel.data.errors) intel.data.errors = {};
3216
3262
  if (!intel.data.errors[opts.error]) intel.data.errors[opts.error] = [];
3217
3263
  intel.data.errors[opts.error].push({ fix: opts.fix, file: opts.file || '', recorded: Date.now() });
@@ -3223,7 +3269,7 @@ hooksCmd.command('error-suggest')
3223
3269
  .description('Get suggested fixes for an error based on learned patterns')
3224
3270
  .requiredOption('-e, --error <error>', 'Error message or code')
3225
3271
  .action((opts) => {
3226
- const intel = new Intelligence();
3272
+ const intel = new Intelligence({ skipEngine: true }); // Fast mode
3227
3273
  let suggestions = [];
3228
3274
 
3229
3275
  if (intel.data.errors) {
@@ -3240,7 +3286,7 @@ hooksCmd.command('error-suggest')
3240
3286
  hooksCmd.command('force-learn')
3241
3287
  .description('Force an immediate learning cycle')
3242
3288
  .action(() => {
3243
- const intel = new Intelligence();
3289
+ const intel = new Intelligence({ skipEngine: true }); // Fast mode
3244
3290
  intel.tick();
3245
3291
  console.log(JSON.stringify({ success: true, result: 'Learning cycle triggered', stats: intel.stats() }));
3246
3292
  });
@@ -11,12 +11,19 @@
11
11
  * - Real semantic understanding (not hash-based)
12
12
  * - Cached model loading (downloads from HuggingFace on first use)
13
13
  * - Batch embedding support
14
+ * - Optional parallel workers for 3.8x batch speedup
14
15
  */
15
16
  export interface OnnxEmbedderConfig {
16
17
  modelId?: string;
17
18
  maxLength?: number;
18
19
  normalize?: boolean;
19
20
  cacheDir?: string;
21
+ /** Enable parallel workers for batch operations (default: auto-detect) */
22
+ enableParallel?: boolean | 'auto';
23
+ /** Number of worker threads (default: CPU cores - 1) */
24
+ numWorkers?: number;
25
+ /** Minimum batch size to use parallel processing (default: 4) */
26
+ parallelThreshold?: number;
20
27
  }
21
28
  export interface EmbeddingResult {
22
29
  embedding: number[];
@@ -41,6 +48,7 @@ export declare function initOnnxEmbedder(config?: OnnxEmbedderConfig): Promise<b
41
48
  export declare function embed(text: string): Promise<EmbeddingResult>;
42
49
  /**
43
50
  * Generate embeddings for multiple texts
51
+ * Uses parallel workers automatically for batches >= parallelThreshold
44
52
  */
45
53
  export declare function embedBatch(texts: string[]): Promise<EmbeddingResult[]>;
46
54
  /**
@@ -60,13 +68,21 @@ export declare function getDimension(): number;
60
68
  */
61
69
  export declare function isReady(): boolean;
62
70
  /**
63
- * Get embedder stats
71
+ * Get embedder stats including SIMD and parallel capabilities
64
72
  */
65
73
  export declare function getStats(): {
66
74
  ready: boolean;
67
75
  dimension: number;
68
76
  model: string;
77
+ simd: boolean;
78
+ parallel: boolean;
79
+ parallelWorkers: number;
80
+ parallelThreshold: number;
69
81
  };
82
+ /**
83
+ * Shutdown parallel workers (call on exit)
84
+ */
85
+ export declare function shutdown(): Promise<void>;
70
86
  export declare class OnnxEmbedder {
71
87
  private config;
72
88
  constructor(config?: OnnxEmbedderConfig);
@@ -1 +1 @@
1
- {"version":3,"file":"onnx-embedder.d.ts","sourceRoot":"","sources":["../../src/core/onnx-embedder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAWH,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAYD;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAOzC;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,kBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC,CAkExF;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAiBlE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAyB5E;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAaxF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAiBjE;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,OAAO,CAEjC;AAED;;GAEG;AACH,wBAAgB,QAAQ,IAAI;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAM/E;AAGD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAqB;gBAEvB,MAAM,GAAE,kBAAuB;IAIrC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAIxB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAKtC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAKhD,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK/D,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;CACF;AAED,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"onnx-embedder.d.ts","sourceRoot":"","sources":["../../src/core/onnx-embedder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAWH,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAClC,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAMD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAeD;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAOzC;AA6DD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,kBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC,CA2ExF;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAiBlE;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAwC5E;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAaxF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAiBjE;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,OAAO,CAEjC;AAED;;GAEG;AACH,wBAAgB,QAAQ,IAAI;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAUA;AAED;;GAEG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAM9C;AAGD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAqB;gBAEvB,MAAM,GAAE,kBAAuB;IAIrC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAIxB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAKtC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAKhD,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK/D,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;CACF;AAED,eAAe,YAAY,CAAC"}
@@ -12,6 +12,7 @@
12
12
  * - Real semantic understanding (not hash-based)
13
13
  * - Cached model loading (downloads from HuggingFace on first use)
14
14
  * - Batch embedding support
15
+ * - Optional parallel workers for 3.8x batch speedup
15
16
  */
16
17
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
18
  if (k2 === undefined) k2 = k;
@@ -57,18 +58,25 @@ exports.cosineSimilarity = cosineSimilarity;
57
58
  exports.getDimension = getDimension;
58
59
  exports.isReady = isReady;
59
60
  exports.getStats = getStats;
61
+ exports.shutdown = shutdown;
60
62
  const path = __importStar(require("path"));
61
63
  const fs = __importStar(require("fs"));
62
64
  const url_1 = require("url");
63
65
  // Force native dynamic import (avoids TypeScript transpiling to require)
64
66
  // eslint-disable-next-line @typescript-eslint/no-implied-eval
65
67
  const dynamicImport = new Function('specifier', 'return import(specifier)');
68
+ // Capability detection
69
+ let simdAvailable = false;
70
+ let parallelAvailable = false;
66
71
  // Lazy-loaded module state
67
72
  let wasmModule = null;
68
73
  let embedder = null;
74
+ let parallelEmbedder = null;
69
75
  let loadError = null;
70
76
  let loadPromise = null;
71
77
  let isInitialized = false;
78
+ let parallelEnabled = false;
79
+ let parallelThreshold = 4;
72
80
  // Default model
73
81
  const DEFAULT_MODEL = 'all-MiniLM-L6-v2';
74
82
  /**
@@ -83,6 +91,63 @@ function isOnnxAvailable() {
83
91
  return false;
84
92
  }
85
93
  }
94
+ /**
95
+ * Check if parallel workers are available (npm package installed)
96
+ */
97
+ async function detectParallelAvailable() {
98
+ try {
99
+ await dynamicImport('ruvector-onnx-embeddings-wasm/parallel');
100
+ parallelAvailable = true;
101
+ return true;
102
+ }
103
+ catch {
104
+ parallelAvailable = false;
105
+ return false;
106
+ }
107
+ }
108
+ /**
109
+ * Check if SIMD is available (from WASM module)
110
+ */
111
+ function detectSimd() {
112
+ try {
113
+ if (wasmModule && typeof wasmModule.simd_available === 'function') {
114
+ simdAvailable = wasmModule.simd_available();
115
+ return simdAvailable;
116
+ }
117
+ }
118
+ catch { }
119
+ return false;
120
+ }
121
+ /**
122
+ * Try to load ParallelEmbedder from npm package (optional)
123
+ */
124
+ async function tryInitParallel(config) {
125
+ // Skip if explicitly disabled
126
+ if (config.enableParallel === false)
127
+ return false;
128
+ // For 'auto' or true, try to initialize
129
+ try {
130
+ const parallelModule = await dynamicImport('ruvector-onnx-embeddings-wasm/parallel');
131
+ const { ParallelEmbedder } = parallelModule;
132
+ parallelEmbedder = new ParallelEmbedder({
133
+ numWorkers: config.numWorkers,
134
+ });
135
+ await parallelEmbedder.init(config.modelId || DEFAULT_MODEL);
136
+ parallelThreshold = config.parallelThreshold || 4;
137
+ parallelEnabled = true;
138
+ parallelAvailable = true;
139
+ console.error(`Parallel embedder ready: ${parallelEmbedder.numWorkers} workers, SIMD: ${simdAvailable}`);
140
+ return true;
141
+ }
142
+ catch (e) {
143
+ parallelAvailable = false;
144
+ if (config.enableParallel === true) {
145
+ // Only warn if explicitly requested
146
+ console.error(`Parallel embedder not available: ${e.message}`);
147
+ }
148
+ return false;
149
+ }
150
+ }
86
151
  /**
87
152
  * Initialize the ONNX embedder (downloads model if needed)
88
153
  */
@@ -132,8 +197,15 @@ async function initOnnxEmbedder(config = {}) {
132
197
  .setNormalize(config.normalize !== false)
133
198
  .setPooling(0); // Mean pooling
134
199
  embedder = wasmModule.WasmEmbedder.withConfig(modelBytes, tokenizerJson, embedderConfig);
135
- console.error(`ONNX embedder ready: ${embedder.dimension()}d`);
200
+ // Detect SIMD capability
201
+ detectSimd();
202
+ console.error(`ONNX embedder ready: ${embedder.dimension()}d, SIMD: ${simdAvailable}`);
136
203
  isInitialized = true;
204
+ // Try parallel by default ('auto') or if explicitly enabled
205
+ const shouldTryParallel = config.enableParallel !== false;
206
+ if (shouldTryParallel) {
207
+ await tryInitParallel(config);
208
+ }
137
209
  }
138
210
  catch (e) {
139
211
  loadError = new Error(`Failed to initialize ONNX embedder: ${e.message}`);
@@ -164,6 +236,7 @@ async function embed(text) {
164
236
  }
165
237
  /**
166
238
  * Generate embeddings for multiple texts
239
+ * Uses parallel workers automatically for batches >= parallelThreshold
167
240
  */
168
241
  async function embedBatch(texts) {
169
242
  if (!isInitialized) {
@@ -173,6 +246,18 @@ async function embedBatch(texts) {
173
246
  throw new Error('ONNX embedder not initialized');
174
247
  }
175
248
  const start = performance.now();
249
+ // Use parallel workers for large batches
250
+ if (parallelEnabled && parallelEmbedder && texts.length >= parallelThreshold) {
251
+ const batchResults = await parallelEmbedder.embedBatch(texts);
252
+ const totalTime = performance.now() - start;
253
+ const dimension = parallelEmbedder.dimension || 384;
254
+ return batchResults.map((emb) => ({
255
+ embedding: Array.from(emb),
256
+ dimension,
257
+ timeMs: totalTime / texts.length,
258
+ }));
259
+ }
260
+ // Sequential fallback
176
261
  const batchEmbeddings = embedder.embedBatch(texts);
177
262
  const totalTime = performance.now() - start;
178
263
  const dimension = embedder.dimension();
@@ -233,15 +318,29 @@ function isReady() {
233
318
  return isInitialized;
234
319
  }
235
320
  /**
236
- * Get embedder stats
321
+ * Get embedder stats including SIMD and parallel capabilities
237
322
  */
238
323
  function getStats() {
239
324
  return {
240
325
  ready: isInitialized,
241
326
  dimension: embedder ? embedder.dimension() : 384,
242
327
  model: DEFAULT_MODEL,
328
+ simd: simdAvailable,
329
+ parallel: parallelEnabled,
330
+ parallelWorkers: parallelEmbedder?.numWorkers || 0,
331
+ parallelThreshold,
243
332
  };
244
333
  }
334
+ /**
335
+ * Shutdown parallel workers (call on exit)
336
+ */
337
+ async function shutdown() {
338
+ if (parallelEmbedder) {
339
+ await parallelEmbedder.shutdown();
340
+ parallelEmbedder = null;
341
+ parallelEnabled = false;
342
+ }
343
+ }
245
344
  // Export class wrapper for compatibility
246
345
  class OnnxEmbedder {
247
346
  constructor(config = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruvector",
3
- "version": "0.1.58",
3
+ "version": "0.1.60",
4
4
  "description": "High-performance vector database for Node.js with automatic native/WASM fallback",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/ruvector.db CHANGED
Binary file