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 +107 -61
- package/dist/core/onnx-embedder.d.ts +17 -1
- package/dist/core/onnx-embedder.d.ts.map +1 -1
- package/dist/core/onnx-embedder.js +101 -2
- package/package.json +1 -1
- package/ruvector.db +0 -0
package/bin/cli.js
CHANGED
|
@@ -2096,32 +2096,46 @@ program
|
|
|
2096
2096
|
// Full RuVector Stack: VectorDB + SONA + Attention
|
|
2097
2097
|
// ============================================
|
|
2098
2098
|
|
|
2099
|
-
//
|
|
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
|
|
2102
|
+
let engineLoadAttempted = false;
|
|
2102
2103
|
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
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
|
-
|
|
2120
|
-
|
|
2121
|
-
if (engineAvailable && IntelligenceEngine) {
|
|
2135
|
+
const EngineClass = loadIntelligenceEngine();
|
|
2136
|
+
if (EngineClass) {
|
|
2122
2137
|
try {
|
|
2123
|
-
this.
|
|
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.
|
|
2148
|
+
this._engine.import(this.convertLegacyData(this.data), true);
|
|
2135
2149
|
}
|
|
2136
2150
|
} catch (e) {
|
|
2137
|
-
|
|
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
|
|
2225
|
-
|
|
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 =
|
|
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 (
|
|
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
|
|
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
|
|
2306
|
-
|
|
2307
|
-
|
|
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
|
|
2355
|
-
|
|
2356
|
-
|
|
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
|
|
2368
|
-
|
|
2369
|
-
|
|
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
|
-
|
|
2429
|
-
|
|
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
|
-
|
|
2458
|
-
|
|
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
|
-
|
|
2501
|
+
// Try engine first (only if already initialized)
|
|
2502
|
+
const eng = this.getEngineIfReady();
|
|
2503
|
+
if (eng) {
|
|
2465
2504
|
try {
|
|
2466
|
-
const results =
|
|
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
|
|
2490
|
-
|
|
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 (
|
|
2496
|
-
|
|
2536
|
+
// Try engine first (only if already initialized)
|
|
2537
|
+
const eng = this.getEngineIfReady();
|
|
2538
|
+
if (eng) {
|
|
2497
2539
|
try {
|
|
2498
|
-
const fixes =
|
|
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 (
|
|
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 =
|
|
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
|
-
|
|
2559
|
-
|
|
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
|
-
|
|
2569
|
-
|
|
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
|
|
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
|
-
|
|
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
package/ruvector.db
CHANGED
|
Binary file
|