@ruvector/ruvllm 2.5.5 → 2.5.6

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 (63) hide show
  1. package/README.md +282 -282
  2. package/bin/cli.js +1020 -1020
  3. package/dist/cjs/benchmarks/embedding-benchmark.js +1 -1
  4. package/dist/cjs/benchmarks/index.js +1 -1
  5. package/dist/cjs/benchmarks/model-comparison.js +1 -1
  6. package/dist/cjs/benchmarks/routing-benchmark.js +1 -1
  7. package/dist/cjs/contrastive.js +70 -70
  8. package/dist/cjs/engine.js +13 -13
  9. package/dist/cjs/export.js +31 -31
  10. package/dist/cjs/federated.js +1 -1
  11. package/dist/cjs/index.js +1 -1
  12. package/dist/cjs/intelligence.js +1 -1
  13. package/dist/cjs/lora.js +1 -1
  14. package/dist/cjs/models.js +1 -1
  15. package/dist/cjs/native.js +1 -1
  16. package/dist/cjs/session.js +1 -1
  17. package/dist/cjs/simd.js +1 -1
  18. package/dist/cjs/sona.d.ts +36 -0
  19. package/dist/cjs/sona.d.ts.map +1 -1
  20. package/dist/cjs/sona.js +66 -4
  21. package/dist/cjs/streaming.js +1 -1
  22. package/dist/cjs/training.js +1 -1
  23. package/dist/cjs/types.js +1 -1
  24. package/dist/esm/benchmarks/embedding-benchmark.js +1 -1
  25. package/dist/esm/benchmarks/index.js +1 -1
  26. package/dist/esm/benchmarks/model-comparison.js +1 -1
  27. package/dist/esm/benchmarks/routing-benchmark.js +1 -1
  28. package/dist/esm/contrastive.js +70 -70
  29. package/dist/esm/engine.js +13 -13
  30. package/dist/esm/export.js +31 -31
  31. package/dist/esm/federated.js +1 -1
  32. package/dist/esm/index.js +1 -1
  33. package/dist/esm/intelligence.js +1 -1
  34. package/dist/esm/lora.js +1 -1
  35. package/dist/esm/models.js +1 -1
  36. package/dist/esm/native.js +1 -1
  37. package/dist/esm/session.js +1 -1
  38. package/dist/esm/simd.js +1 -1
  39. package/dist/esm/sona.d.ts +36 -0
  40. package/dist/esm/sona.d.ts.map +1 -1
  41. package/dist/esm/sona.js +66 -4
  42. package/dist/esm/streaming.js +1 -1
  43. package/dist/esm/training.js +1 -1
  44. package/dist/esm/types.js +1 -1
  45. package/package.json +133 -133
  46. package/scripts/ensemble-model-compare.js +287 -287
  47. package/scripts/huggingface/README.md +537 -537
  48. package/scripts/huggingface/publish.sh +112 -112
  49. package/scripts/hybrid-model-compare.js +373 -373
  50. package/scripts/improved-model-compare.js +288 -288
  51. package/scripts/optimized-model-compare.js +364 -364
  52. package/scripts/real-model-compare.js +280 -280
  53. package/scripts/training/agentic-flow-capabilities.json +329 -329
  54. package/scripts/training/claude-code-synth.js +505 -505
  55. package/scripts/training/claude-flow-capabilities.json +1322 -1322
  56. package/scripts/training/claude-hard-negatives.js +222 -222
  57. package/scripts/training/contrastive-finetune.js +517 -517
  58. package/scripts/training/ecosystem-triplets.jsonl +1466 -1466
  59. package/scripts/training/generate-ecosystem-triplets.js +1324 -1324
  60. package/scripts/training/routing-dataset.js +634 -634
  61. package/scripts/training/ruvector-capabilities.json +798 -798
  62. package/scripts/training/validate-ecosystem.js +381 -381
  63. package/scripts/training/validation-results.json +22 -22
package/bin/cli.js CHANGED
@@ -1,1020 +1,1020 @@
1
- #!/usr/bin/env node
2
- /**
3
- * RuvLLM CLI - Self-learning LLM orchestration
4
- *
5
- * Usage:
6
- * ruvllm query "What is machine learning?"
7
- * ruvllm generate "Write a haiku about AI"
8
- * ruvllm memory add "Important context"
9
- * ruvllm memory search "context"
10
- * ruvllm models list
11
- * ruvllm models download claude-code
12
- * ruvllm stats
13
- * ruvllm benchmark
14
- */
15
-
16
- const { RuvLLM, SimdOps, version, hasSimdSupport, ModelDownloader, listModels, getModelInfo, RUVLTRA_MODELS, getDefaultModelsDir, runRoutingBenchmark, formatRoutingResults, baselineKeywordRouter, runEmbeddingBenchmark, formatEmbeddingResults, runFullBenchmark, formatFullResults, ROUTING_TEST_CASES, runFullComparison, formatComparisonResults, ContrastiveTrainer, AGENT_TRAINING_DATA, generateTrainingDataset, generateContrastivePairs, getDatasetStats, tripletLoss, cosineSimilarity } = require('../dist/cjs/index.js');
17
-
18
- const args = process.argv.slice(2);
19
- const command = args[0];
20
-
21
- // Parse CLI arguments
22
- function parseArgs(args) {
23
- const result = { flags: {}, positional: [] };
24
- for (let i = 0; i < args.length; i++) {
25
- const arg = args[i];
26
- if (arg.startsWith('--')) {
27
- const key = arg.slice(2);
28
- const nextArg = args[i + 1];
29
- if (nextArg && !nextArg.startsWith('--')) {
30
- result.flags[key] = nextArg;
31
- i++;
32
- } else {
33
- result.flags[key] = true;
34
- }
35
- } else if (!result.command) {
36
- result.command = arg;
37
- } else {
38
- result.positional.push(arg);
39
- }
40
- }
41
- return result;
42
- }
43
-
44
- // Format output
45
- function formatJson(obj) {
46
- return JSON.stringify(obj, null, 2);
47
- }
48
-
49
- function formatTable(data) {
50
- const maxKeyLen = Math.max(...Object.keys(data).map(k => k.length));
51
- return Object.entries(data)
52
- .map(([k, v]) => ` ${k.padEnd(maxKeyLen)} : ${v}`)
53
- .join('\n');
54
- }
55
-
56
- // Commands
57
- async function runQuery(llm, text, flags) {
58
- const config = {};
59
- if (flags.temperature) config.temperature = parseFloat(flags.temperature);
60
- if (flags['max-tokens']) config.maxTokens = parseInt(flags['max-tokens']);
61
- if (flags['top-p']) config.topP = parseFloat(flags['top-p']);
62
- if (flags['top-k']) config.topK = parseInt(flags['top-k']);
63
-
64
- const response = llm.query(text, config);
65
-
66
- if (flags.json) {
67
- console.log(formatJson(response));
68
- } else {
69
- console.log('\n' + response.text);
70
- console.log(`\n--- Model: ${response.model} | Confidence: ${(response.confidence * 100).toFixed(1)}% | Latency: ${response.latencyMs.toFixed(2)}ms ---`);
71
- }
72
- }
73
-
74
- async function runGenerate(llm, prompt, flags) {
75
- console.error('Warning: Built-in SIMD inference is experimental. For production use, configure an external LLM provider (Ollama, OpenAI, etc.).');
76
-
77
- const config = {};
78
- if (flags.temperature) config.temperature = parseFloat(flags.temperature);
79
- if (flags['max-tokens']) config.maxTokens = parseInt(flags['max-tokens']);
80
- if (flags['top-p']) config.topP = parseFloat(flags['top-p']);
81
-
82
- const text = llm.generate(prompt, config);
83
- console.log(text);
84
- }
85
-
86
- async function runMemoryAdd(llm, content, flags) {
87
- const metadata = flags.metadata ? JSON.parse(flags.metadata) : undefined;
88
- const id = llm.addMemory(content, metadata);
89
- console.log(`Added memory with ID: ${id}`);
90
- }
91
-
92
- async function runMemorySearch(llm, query, flags) {
93
- const k = flags.k ? parseInt(flags.k) : 10;
94
- const results = llm.searchMemory(query, k);
95
-
96
- if (flags.json) {
97
- console.log(formatJson(results));
98
- } else {
99
- if (results.length === 0) {
100
- console.log('No results found.');
101
- return;
102
- }
103
- results.forEach((r, i) => {
104
- console.log(`\n[${i + 1}] Score: ${r.score.toFixed(4)} | ID: ${r.id}`);
105
- console.log(` ${r.content.slice(0, 100)}${r.content.length > 100 ? '...' : ''}`);
106
- });
107
- }
108
- }
109
-
110
- async function runStats(llm, flags) {
111
- let stats;
112
- try {
113
- stats = llm.stats();
114
- } catch (e) {
115
- stats = null;
116
- }
117
-
118
- if (!stats) {
119
- if (flags.json) {
120
- console.log(formatJson({ error: 'No inference data available' }));
121
- } else {
122
- console.log('\nNo inference data available. Run some queries first.');
123
- }
124
- return;
125
- }
126
-
127
- if (flags.json) {
128
- console.log(formatJson(stats));
129
- } else {
130
- console.log('\nRuvLLM Statistics:');
131
- console.log(formatTable({
132
- 'Total Queries': stats.totalQueries ?? 0,
133
- 'Memory Nodes': stats.memoryNodes ?? 0,
134
- 'Patterns Learned': stats.patternsLearned ?? 0,
135
- 'Avg Latency': `${(stats.avgLatencyMs ?? 0).toFixed(2)}ms`,
136
- 'Cache Hit Rate': `${((stats.cacheHitRate ?? 0) * 100).toFixed(1)}%`,
137
- 'Router Accuracy': `${((stats.routerAccuracy ?? 0) * 100).toFixed(1)}%`,
138
- }));
139
- }
140
- }
141
-
142
- async function runRoute(llm, text, flags) {
143
- const decision = llm.route(text);
144
-
145
- if (flags.json) {
146
- console.log(formatJson(decision));
147
- } else {
148
- console.log('\nRouting Decision:');
149
- console.log(formatTable({
150
- 'Model': decision.model,
151
- 'Context Size': decision.contextSize,
152
- 'Temperature': decision.temperature.toFixed(2),
153
- 'Top-P': decision.topP.toFixed(2),
154
- 'Confidence': `${(decision.confidence * 100).toFixed(1)}%`,
155
- }));
156
- }
157
- }
158
-
159
- async function runEmbed(llm, text, flags) {
160
- const embedding = llm.embed(text);
161
-
162
- if (flags.json) {
163
- console.log(formatJson({ embedding, dimensions: embedding.length }));
164
- } else {
165
- console.log(`Embedding (${embedding.length} dimensions):`);
166
- console.log(` First 10: [${embedding.slice(0, 10).map(x => x.toFixed(4)).join(', ')}...]`);
167
- console.log(` Norm: ${Math.sqrt(embedding.reduce((s, x) => s + x * x, 0)).toFixed(4)}`);
168
- }
169
- }
170
-
171
- async function runSimilarity(llm, text1, text2, flags) {
172
- const score = llm.similarity(text1, text2);
173
-
174
- if (flags.json) {
175
- console.log(formatJson({ text1, text2, similarity: score }));
176
- } else {
177
- console.log(`Similarity: ${(score * 100).toFixed(2)}%`);
178
- }
179
- }
180
-
181
- async function runBenchmark(flags) {
182
- const simd = new SimdOps();
183
- const dims = flags.dims ? parseInt(flags.dims) : 768;
184
- const iterations = flags.iterations ? parseInt(flags.iterations) : 1000;
185
-
186
- // Generate test vectors
187
- const a = Array.from({ length: dims }, () => Math.random());
188
- const b = Array.from({ length: dims }, () => Math.random());
189
-
190
- console.log(`\nBenchmark: ${dims} dimensions, ${iterations} iterations`);
191
- console.log(`SIMD: ${simd.isNative() ? 'Native' : 'JavaScript fallback'}`);
192
- console.log(`Capabilities: ${simd.capabilities().join(', ')}`);
193
- console.log('');
194
-
195
- // Dot product benchmark
196
- let start = Date.now();
197
- for (let i = 0; i < iterations; i++) {
198
- simd.dotProduct(a, b);
199
- }
200
- let elapsed = Date.now() - start;
201
- console.log(`Dot Product: ${elapsed}ms (${(iterations / elapsed * 1000).toFixed(0)} ops/sec)`);
202
-
203
- // Cosine similarity benchmark
204
- start = Date.now();
205
- for (let i = 0; i < iterations; i++) {
206
- simd.cosineSimilarity(a, b);
207
- }
208
- elapsed = Date.now() - start;
209
- console.log(`Cosine Similarity: ${elapsed}ms (${(iterations / elapsed * 1000).toFixed(0)} ops/sec)`);
210
-
211
- // L2 distance benchmark
212
- start = Date.now();
213
- for (let i = 0; i < iterations; i++) {
214
- simd.l2Distance(a, b);
215
- }
216
- elapsed = Date.now() - start;
217
- console.log(`L2 Distance: ${elapsed}ms (${(iterations / elapsed * 1000).toFixed(0)} ops/sec)`);
218
-
219
- // Softmax benchmark
220
- start = Date.now();
221
- for (let i = 0; i < iterations; i++) {
222
- simd.softmax(a);
223
- }
224
- elapsed = Date.now() - start;
225
- console.log(`Softmax: ${elapsed}ms (${(iterations / elapsed * 1000).toFixed(0)} ops/sec)`);
226
- }
227
-
228
- async function runInfo(flags) {
229
- const llm = new RuvLLM();
230
-
231
- const info = {
232
- version: version(),
233
- native: llm.isNativeLoaded(),
234
- simd: hasSimdSupport(),
235
- capabilities: llm.simdCapabilities(),
236
- platform: process.platform,
237
- arch: process.arch,
238
- nodeVersion: process.version,
239
- };
240
-
241
- if (flags.json) {
242
- console.log(formatJson(info));
243
- } else {
244
- console.log('\nRuvLLM Info:');
245
- console.log(formatTable({
246
- 'Version': info.version,
247
- 'Native Module': info.native ? 'Loaded' : 'Fallback (JS)',
248
- 'SIMD Support': info.simd ? 'Yes' : 'No',
249
- 'Capabilities': info.capabilities.join(', '),
250
- 'Platform': `${info.platform}-${info.arch}`,
251
- 'Node.js': info.nodeVersion,
252
- }));
253
- }
254
- }
255
-
256
- // Model management commands
257
- async function runModelsList(flags) {
258
- const downloader = new ModelDownloader();
259
- const status = downloader.getStatus();
260
-
261
- if (flags.json) {
262
- console.log(formatJson(status));
263
- } else {
264
- console.log('\n╔══════════════════════════════════════════════════════════════════════════╗');
265
- console.log('║ RuvLTRA Models ║');
266
- console.log('║ https://huggingface.co/ruv/ruvltra ║');
267
- console.log('╠══════════════════════════════════════════════════════════════════════════╣');
268
- console.log('║ Model │ Size │ Params │ Status │ Use Case ║');
269
- console.log('╠══════════════════════════════════════════════════════════════════════════╣');
270
-
271
- for (const { model, downloaded } of status) {
272
- const statusIcon = downloaded ? '✓ Ready ' : '○ Not DL ';
273
- const name = model.id.padEnd(12);
274
- const size = model.size.padEnd(7);
275
- const params = model.parameters.padEnd(6);
276
- const useCase = model.useCase.slice(0, 24).padEnd(24);
277
- console.log(`║ ${name} │ ${size} │ ${params} │ ${statusIcon} │ ${useCase} ║`);
278
- }
279
-
280
- console.log('╚══════════════════════════════════════════════════════════════════════════╝');
281
- console.log(`\nModels directory: ${getDefaultModelsDir()}`);
282
- console.log('\nDownload with: ruvllm models download <model-id>');
283
- console.log(' Examples: ruvllm models download claude-code');
284
- console.log(' ruvllm models download --all');
285
- }
286
- }
287
-
288
- async function runModelsDownload(modelId, flags) {
289
- const downloader = new ModelDownloader();
290
-
291
- if (flags.all) {
292
- console.log('\nDownloading all RuvLTRA models...\n');
293
- const models = listModels();
294
-
295
- for (const model of models) {
296
- console.log(`\n[${model.id}] ${model.name} (${model.size})`);
297
-
298
- if (downloader.isDownloaded(model.id) && !flags.force) {
299
- console.log(' Already downloaded, skipping (use --force to re-download)');
300
- continue;
301
- }
302
-
303
- try {
304
- const lastPercent = { value: -1 };
305
- const path = await downloader.download(model.id, {
306
- force: flags.force,
307
- onProgress: (p) => {
308
- const percent = Math.floor(p.percent / 5) * 5; // Round to 5%
309
- if (percent !== lastPercent.value) {
310
- const bar = '█'.repeat(percent / 5) + '░'.repeat(20 - percent / 5);
311
- const speed = (p.speedBps / 1024 / 1024).toFixed(1);
312
- const eta = p.etaSeconds < 60
313
- ? `${Math.ceil(p.etaSeconds)}s`
314
- : `${Math.ceil(p.etaSeconds / 60)}m`;
315
- process.stdout.write(`\r [${bar}] ${p.percent}% | ${speed} MB/s | ETA: ${eta} `);
316
- lastPercent.value = percent;
317
- }
318
- },
319
- });
320
- console.log(`\n ✓ Downloaded to: ${path}`);
321
- } catch (error) {
322
- console.error(`\n ✗ Failed: ${error.message}`);
323
- }
324
- }
325
-
326
- console.log('\n\nDownload complete!');
327
- return;
328
- }
329
-
330
- if (!modelId) {
331
- console.error('Error: model ID required. Use --all to download all models.');
332
- console.error('\nAvailable models:');
333
- listModels().forEach(m => console.error(` - ${m.id}: ${m.name} (${m.size})`));
334
- process.exit(1);
335
- }
336
-
337
- const model = getModelInfo(modelId);
338
- if (!model) {
339
- console.error(`Error: Unknown model "${modelId}"`);
340
- console.error('\nAvailable models:');
341
- listModels().forEach(m => console.error(` - ${m.id}: ${m.name} (${m.size})`));
342
- process.exit(1);
343
- }
344
-
345
- console.log(`\nDownloading ${model.name} (${model.size})...`);
346
- console.log(`From: ${model.url}\n`);
347
-
348
- if (downloader.isDownloaded(modelId) && !flags.force) {
349
- const path = downloader.getModelPath(modelId);
350
- console.log(`Model already downloaded at: ${path}`);
351
- console.log('Use --force to re-download.');
352
- return;
353
- }
354
-
355
- const lastPercent = { value: -1 };
356
- try {
357
- const path = await downloader.download(modelId, {
358
- force: flags.force,
359
- onProgress: (p) => {
360
- const percent = Math.floor(p.percent / 2) * 2; // Round to 2%
361
- if (percent !== lastPercent.value) {
362
- const bar = '█'.repeat(Math.floor(percent / 5)) + '░'.repeat(20 - Math.floor(percent / 5));
363
- const downloaded = (p.downloaded / 1024 / 1024).toFixed(1);
364
- const total = (p.total / 1024 / 1024).toFixed(1);
365
- const speed = (p.speedBps / 1024 / 1024).toFixed(1);
366
- const eta = p.etaSeconds < 60
367
- ? `${Math.ceil(p.etaSeconds)}s`
368
- : `${Math.ceil(p.etaSeconds / 60)}m`;
369
- process.stdout.write(`\r[${bar}] ${p.percent}% | ${downloaded}/${total} MB | ${speed} MB/s | ETA: ${eta} `);
370
- lastPercent.value = percent;
371
- }
372
- },
373
- });
374
- console.log(`\n\n✓ Downloaded to: ${path}`);
375
- console.log(`\nModel ready to use!`);
376
- console.log(` Context length: ${model.contextLength} tokens`);
377
- console.log(` Quantization: ${model.quantization}`);
378
- } catch (error) {
379
- console.error(`\n\n✗ Download failed: ${error.message}`);
380
- process.exit(1);
381
- }
382
- }
383
-
384
- async function runModelsStatus(flags) {
385
- const downloader = new ModelDownloader();
386
- const status = downloader.getStatus();
387
-
388
- if (flags.json) {
389
- console.log(formatJson(status.map(s => ({
390
- id: s.model.id,
391
- name: s.model.name,
392
- downloaded: s.downloaded,
393
- path: s.path,
394
- size: s.model.size,
395
- }))));
396
- } else {
397
- console.log('\nModel Status:');
398
- console.log(`Directory: ${getDefaultModelsDir()}\n`);
399
-
400
- for (const { model, downloaded, path } of status) {
401
- const icon = downloaded ? '✓' : '○';
402
- const status = downloaded ? 'Ready' : 'Not downloaded';
403
- console.log(` ${icon} ${model.name.padEnd(25)} ${status.padEnd(15)} ${model.size}`);
404
- if (downloaded) {
405
- console.log(` Path: ${path}`);
406
- }
407
- }
408
- }
409
- }
410
-
411
- async function runModelsDelete(modelId, flags) {
412
- const downloader = new ModelDownloader();
413
-
414
- if (flags.all) {
415
- const count = downloader.deleteAll();
416
- console.log(`Deleted ${count} model(s).`);
417
- return;
418
- }
419
-
420
- if (!modelId) {
421
- console.error('Error: model ID required. Use --all to delete all models.');
422
- process.exit(1);
423
- }
424
-
425
- if (downloader.delete(modelId)) {
426
- console.log(`Deleted model: ${modelId}`);
427
- } else {
428
- console.log(`Model not found or not downloaded: ${modelId}`);
429
- }
430
- }
431
-
432
- // Benchmark commands for Claude Code use cases
433
- async function runBenchmarkRouting(flags) {
434
- console.log('\nRunning Routing Benchmark...');
435
- console.log(`Testing ${ROUTING_TEST_CASES.length} task routing scenarios\n`);
436
-
437
- const llm = new RuvLLM({ embeddingDim: 768, learningEnabled: false });
438
-
439
- // Router function using the model
440
- const modelRouter = (task) => {
441
- try {
442
- const decision = llm.route(task);
443
- return { agent: decision.model, confidence: decision.confidence };
444
- } catch (e) {
445
- // Fallback to keyword router if model not available
446
- return baselineKeywordRouter(task);
447
- }
448
- };
449
-
450
- // Run with baseline (keyword) router
451
- console.log('Baseline (keyword matching):');
452
- const baselineResults = runRoutingBenchmark(baselineKeywordRouter);
453
-
454
- if (flags.json) {
455
- console.log(JSON.stringify(baselineResults, null, 2));
456
- } else {
457
- console.log(formatRoutingResults(baselineResults));
458
- }
459
-
460
- // Try model router if native is available
461
- if (llm.isNativeLoaded() && !flags['baseline-only']) {
462
- console.log('\nModel Router (RuvLTRA):');
463
- const modelResults = runRoutingBenchmark(modelRouter);
464
- if (flags.json) {
465
- console.log(JSON.stringify(modelResults, null, 2));
466
- } else {
467
- console.log(formatRoutingResults(modelResults));
468
- }
469
-
470
- // Comparison
471
- const improvement = modelResults.accuracy - baselineResults.accuracy;
472
- console.log(`\nComparison: Model ${improvement >= 0 ? '+' : ''}${(improvement * 100).toFixed(1)}% vs baseline`);
473
- }
474
- }
475
-
476
- async function runBenchmarkEmbedding(flags) {
477
- console.log('\nRunning Embedding Benchmark...');
478
- console.log('Testing similarity detection, clustering, and search quality\n');
479
-
480
- const llm = new RuvLLM({ embeddingDim: 768, learningEnabled: false });
481
- const simd = new SimdOps();
482
-
483
- // Embedder function
484
- const embedder = (text) => {
485
- try {
486
- return llm.embed(text);
487
- } catch (e) {
488
- // Fallback: simple hash-based embedding for testing
489
- const hash = text.split('').reduce((h, c) => ((h << 5) - h + c.charCodeAt(0)) | 0, 0);
490
- return Array.from({ length: 768 }, (_, i) => Math.sin(hash + i) * 0.5);
491
- }
492
- };
493
-
494
- // Similarity function
495
- const similarity = (a, b) => {
496
- try {
497
- return simd.cosineSimilarity(a, b);
498
- } catch (e) {
499
- // Fallback cosine similarity
500
- let dot = 0, normA = 0, normB = 0;
501
- for (let i = 0; i < a.length; i++) {
502
- dot += a[i] * b[i];
503
- normA += a[i] * a[i];
504
- normB += b[i] * b[i];
505
- }
506
- return dot / (Math.sqrt(normA) * Math.sqrt(normB));
507
- }
508
- };
509
-
510
- const results = runEmbeddingBenchmark(embedder, similarity);
511
-
512
- if (flags.json) {
513
- console.log(JSON.stringify(results, null, 2));
514
- } else {
515
- console.log(formatEmbeddingResults(results));
516
- }
517
- }
518
-
519
- async function runBenchmarkCompare(flags) {
520
- try {
521
- const results = await runFullComparison();
522
-
523
- if (flags.json) {
524
- console.log(JSON.stringify(results, null, 2));
525
- } else {
526
- console.log(formatComparisonResults(results));
527
- }
528
- } catch (error) {
529
- console.error('Comparison failed:', error.message);
530
- process.exit(1);
531
- }
532
- }
533
-
534
- // Training commands
535
- async function runTrainContrastive(flags) {
536
- console.log('\n╔═══════════════════════════════════════════════════════════════════════════╗');
537
- console.log('║ RuvLTRA Contrastive Fine-tuning Pipeline ║');
538
- console.log('╚═══════════════════════════════════════════════════════════════════════════╝\n');
539
-
540
- const stats = getDatasetStats();
541
- const outputPath = flags.output || './training-output';
542
-
543
- console.log(`Training examples: ${stats.totalExamples}`);
544
- console.log(`Contrastive pairs: ${stats.contrastivePairs}`);
545
- console.log(`Agent types: ${stats.agentTypes}`);
546
- console.log(`Output: ${outputPath}\n`);
547
-
548
- // Create trainer
549
- const trainer = new ContrastiveTrainer({
550
- epochs: parseInt(flags.epochs) || 10,
551
- batchSize: parseInt(flags['batch-size']) || 16,
552
- learningRate: parseFloat(flags.lr) || 0.0001,
553
- margin: parseFloat(flags.margin) || 0.5,
554
- outputPath,
555
- });
556
-
557
- // Get embeddings for agents
558
- const llm = new RuvLLM({ embeddingDim: 768, learningEnabled: false });
559
- const agentEmbeddings = {};
560
-
561
- console.log('Computing agent embeddings...');
562
- for (const [agent, data] of Object.entries(AGENT_TRAINING_DATA)) {
563
- process.stdout.write(` ${agent}... `);
564
- try {
565
- const emb = llm.embed(data.description);
566
- agentEmbeddings[agent] = emb;
567
- trainer.addAgentEmbedding(agent, emb);
568
- console.log('done');
569
- } catch (e) {
570
- // Fallback embedding
571
- const hash = data.description.split('').reduce((h, c) => ((h << 5) - h + c.charCodeAt(0)) | 0, 0);
572
- const emb = Array.from({ length: 768 }, (_, i) => Math.sin(hash + i) * 0.5);
573
- agentEmbeddings[agent] = emb;
574
- trainer.addAgentEmbedding(agent, emb);
575
- console.log('done (fallback)');
576
- }
577
- }
578
-
579
- // Generate triplets
580
- console.log('\nGenerating training triplets...');
581
- const examples = generateTrainingDataset();
582
- const agents = Object.keys(AGENT_TRAINING_DATA);
583
-
584
- let tripletCount = 0;
585
- for (const example of examples.slice(0, 200)) { // Limit for speed
586
- let taskEmb;
587
- try {
588
- taskEmb = llm.embed(example.task);
589
- } catch (e) {
590
- const hash = example.task.split('').reduce((h, c) => ((h << 5) - h + c.charCodeAt(0)) | 0, 0);
591
- taskEmb = Array.from({ length: 768 }, (_, i) => Math.sin(hash + i) * 0.5);
592
- }
593
-
594
- const positiveAgent = example.agent;
595
- const positiveEmb = agentEmbeddings[positiveAgent];
596
-
597
- // Hard negatives
598
- const hardNegatives = example.confusing_with
599
- ? [example.confusing_with]
600
- : agents.filter(a => a !== positiveAgent).slice(0, 2);
601
-
602
- for (const negAgent of hardNegatives) {
603
- const negEmb = agentEmbeddings[negAgent];
604
- if (negEmb) {
605
- trainer.addTriplet(
606
- example.task, taskEmb,
607
- positiveAgent, positiveEmb,
608
- negAgent, negEmb,
609
- !!example.confusing_with
610
- );
611
- tripletCount++;
612
- }
613
- }
614
- }
615
-
616
- console.log(`Created ${tripletCount} triplets\n`);
617
-
618
- // Train
619
- console.log('Training...');
620
- const result = trainer.train();
621
-
622
- // Export data
623
- console.log('\nExporting training data...');
624
- const exportPath = trainer.exportTrainingData();
625
- const loraConfig = trainer.generateLoRAConfig();
626
- const scriptPath = trainer.generateTrainingScript();
627
-
628
- // Summary
629
- console.log('\n═══════════════════════════════════════════════════════════════════════════');
630
- console.log(' TRAINING SUMMARY');
631
- console.log('═══════════════════════════════════════════════════════════════════════════\n');
632
-
633
- console.log('Training data exported:');
634
- console.log(` - ${exportPath}/triplets.jsonl (${tripletCount} triplets)`);
635
- console.log(` - ${exportPath}/triplets.csv (spreadsheet format)`);
636
- console.log(` - ${exportPath}/embeddings.json (precomputed embeddings)`);
637
- console.log(` - ${exportPath}/lora_config.json (LoRA configuration)`);
638
- console.log(` - ${exportPath}/train.sh (training script)\n`);
639
-
640
- console.log('Training loss (simulated):');
641
- console.log(` Initial: ${result.initialLoss.toFixed(4)}`);
642
- console.log(` Final: ${result.finalLoss.toFixed(4)}`);
643
- console.log(` Improvement: ${result.improvement.toFixed(1)}%\n`);
644
-
645
- console.log('To fine-tune on GPU:');
646
- console.log(` cd ${exportPath}`);
647
- console.log(' chmod +x train.sh && ./train.sh\n');
648
- }
649
-
650
- async function runTrainDataset(flags) {
651
- console.log('\n╔═══════════════════════════════════════════════════════════════════════════╗');
652
- console.log('║ RuvLTRA Training Dataset Generator ║');
653
- console.log('╚═══════════════════════════════════════════════════════════════════════════╝\n');
654
-
655
- const stats = getDatasetStats();
656
- const outputPath = flags.output || './data/training';
657
-
658
- console.log(`Generating training data for ${stats.agentTypes} agent types...\n`);
659
-
660
- // Generate dataset
661
- const examples = generateTrainingDataset();
662
- const pairs = generateContrastivePairs();
663
-
664
- // Export
665
- const { existsSync, mkdirSync, writeFileSync } = require('fs');
666
- const { join } = require('path');
667
-
668
- if (!existsSync(outputPath)) {
669
- mkdirSync(outputPath, { recursive: true });
670
- }
671
-
672
- // JSONL format
673
- writeFileSync(
674
- join(outputPath, 'routing-examples.jsonl'),
675
- examples.map(e => JSON.stringify(e)).join('\n')
676
- );
677
-
678
- // Contrastive pairs
679
- writeFileSync(
680
- join(outputPath, 'contrastive-pairs.jsonl'),
681
- pairs.map(p => JSON.stringify(p)).join('\n')
682
- );
683
-
684
- // CSV format
685
- const csv = [
686
- 'task,agent,complexity,confusing_with',
687
- ...examples.map(e => `"${e.task.replace(/"/g, '""')}",${e.agent},${e.complexity || ''},${e.confusing_with || ''}`)
688
- ].join('\n');
689
- writeFileSync(join(outputPath, 'routing-examples.csv'), csv);
690
-
691
- console.log('Export complete:');
692
- console.log(` - ${join(outputPath, 'routing-examples.jsonl')} (${examples.length} examples)`);
693
- console.log(` - ${join(outputPath, 'contrastive-pairs.jsonl')} (${pairs.length} pairs)`);
694
- console.log(` - ${join(outputPath, 'routing-examples.csv')} (spreadsheet format)\n`);
695
-
696
- console.log('Dataset statistics:');
697
- console.log(` Total examples: ${stats.totalExamples}`);
698
- console.log(` Contrastive pairs: ${stats.contrastivePairs}`);
699
- console.log(` Agent types: ${stats.agentTypes}`);
700
- console.log(` Agents: ${stats.agents.join(', ')}\n`);
701
- }
702
-
703
- async function runTrainStats(flags) {
704
- const stats = getDatasetStats();
705
-
706
- if (flags.json) {
707
- console.log(JSON.stringify(stats, null, 2));
708
- } else {
709
- console.log('\nTraining Dataset Statistics:');
710
- console.log(` Total examples: ${stats.totalExamples}`);
711
- console.log(` Contrastive pairs: ${stats.contrastivePairs}`);
712
- console.log(` Agent types: ${stats.agentTypes}`);
713
- console.log('\nAgents:');
714
- for (const agent of stats.agents) {
715
- const data = AGENT_TRAINING_DATA[agent];
716
- console.log(` - ${agent}: ${data.examples.length} examples, ${data.keywords.length} keywords`);
717
- }
718
- }
719
- }
720
-
721
- async function runBenchmarkFull(flags) {
722
- console.log('\n╔═══════════════════════════════════════════════════════════════════════════╗');
723
- console.log('║ RUVLTRA FULL BENCHMARK SUITE ║');
724
- console.log('║ Evaluating for Claude Code Use Cases ║');
725
- console.log('╚═══════════════════════════════════════════════════════════════════════════╝\n');
726
-
727
- const llm = new RuvLLM({ embeddingDim: 768, learningEnabled: false });
728
- const simd = new SimdOps();
729
-
730
- const modelName = llm.isNativeLoaded() ? 'RuvLTRA (native)' : 'RuvLTRA (JS fallback)';
731
-
732
- // Router
733
- const router = (task) => {
734
- try {
735
- const decision = llm.route(task);
736
- return { agent: decision.model, confidence: decision.confidence };
737
- } catch (e) {
738
- return baselineKeywordRouter(task);
739
- }
740
- };
741
-
742
- // Embedder
743
- const embedder = (text) => {
744
- try {
745
- return llm.embed(text);
746
- } catch (e) {
747
- const hash = text.split('').reduce((h, c) => ((h << 5) - h + c.charCodeAt(0)) | 0, 0);
748
- return Array.from({ length: 768 }, (_, i) => Math.sin(hash + i) * 0.5);
749
- }
750
- };
751
-
752
- // Similarity
753
- const similarity = (a, b) => {
754
- try {
755
- return simd.cosineSimilarity(a, b);
756
- } catch (e) {
757
- let dot = 0, normA = 0, normB = 0;
758
- for (let i = 0; i < a.length; i++) {
759
- dot += a[i] * b[i];
760
- normA += a[i] * a[i];
761
- normB += b[i] * b[i];
762
- }
763
- return dot / (Math.sqrt(normA) * Math.sqrt(normB));
764
- }
765
- };
766
-
767
- const results = runFullBenchmark(router, embedder, similarity, modelName);
768
-
769
- if (flags.json) {
770
- console.log(JSON.stringify(results, null, 2));
771
- } else {
772
- console.log(formatFullResults(results));
773
- }
774
- }
775
-
776
- function printHelp() {
777
- console.log(`
778
- RuvLLM - Self-learning LLM Orchestration
779
-
780
- Usage: ruvllm <command> [options]
781
-
782
- Commands:
783
- query <text> Query the LLM with automatic routing
784
- generate <prompt> Generate text with SIMD inference
785
- route <text> Get routing decision for query
786
- memory add <content> Add content to memory
787
- memory search <query> Search memory for similar content
788
- embed <text> Get embedding for text
789
- similarity <t1> <t2> Compute similarity between texts
790
- stats Show engine statistics
791
- benchmark Run SIMD performance benchmark
792
- info Show system information
793
- help Show this help message
794
-
795
- Model Management:
796
- models list List available RuvLTRA models
797
- models download <id> Download a model from HuggingFace
798
- models download --all Download all available models
799
- models status Check which models are downloaded
800
- models delete <id> Delete a downloaded model
801
-
802
- Claude Code Benchmarks:
803
- benchmark routing Test agent routing accuracy (100 tasks)
804
- benchmark embedding Test embedding quality (similarity, search, clustering)
805
- benchmark full Run complete benchmark suite
806
- benchmark compare Compare Qwen base vs RuvLTRA Claude Code
807
- benchmark simd Run SIMD performance benchmark
808
-
809
- Training & Fine-tuning:
810
- train contrastive Run contrastive fine-tuning pipeline
811
- train dataset Generate training dataset (JSONL, CSV)
812
- train stats Show training dataset statistics
813
-
814
- Options:
815
- --json Output as JSON
816
- --temperature <float> Sampling temperature (0.0-2.0)
817
- --max-tokens <int> Maximum tokens to generate
818
- --top-p <float> Nucleus sampling (0.0-1.0)
819
- --top-k <int> Top-k sampling
820
- --k <int> Number of results for search
821
- --metadata <json> Metadata for memory add
822
- --dims <int> Dimensions for benchmark (default: 768)
823
- --iterations <int> Iterations for benchmark (default: 1000)
824
- --force Force re-download even if model exists
825
- --all Apply to all models (download/delete)
826
- --output <path> Output directory for training data
827
- --epochs <int> Number of training epochs (default: 10)
828
- --batch-size <int> Training batch size (default: 16)
829
- --lr <float> Learning rate (default: 0.0001)
830
- --margin <float> Triplet loss margin (default: 0.5)
831
-
832
- Available Models (from https://huggingface.co/ruv/ruvltra):
833
- claude-code RuvLTRA Claude Code (398MB) - Claude Code workflows
834
- small RuvLTRA Small (398MB) - Edge devices, IoT
835
- medium RuvLTRA Medium (669MB) - General purpose
836
-
837
- Examples:
838
- ruvllm query "What is machine learning?"
839
- ruvllm generate "Write a poem about AI" --temperature 0.9
840
- ruvllm memory add "Important context" --metadata '{"type":"note"}'
841
- ruvllm memory search "context" --k 5
842
- ruvllm similarity "hello world" "hi there"
843
- ruvllm benchmark --dims 1024 --iterations 5000
844
-
845
- # Model management
846
- ruvllm models list
847
- ruvllm models download claude-code
848
- ruvllm models download --all
849
- ruvllm models status
850
-
851
- # Claude Code benchmarks
852
- ruvllm benchmark routing # Test task routing accuracy
853
- ruvllm benchmark embedding # Test embedding quality
854
- ruvllm benchmark full # Run complete benchmark suite
855
-
856
- # Training & fine-tuning
857
- ruvllm train contrastive # Run contrastive fine-tuning
858
- ruvllm train dataset # Generate training dataset
859
- ruvllm train stats # Show dataset statistics
860
- ruvllm train contrastive --epochs 20 --output ./my-training
861
-
862
- Learn more: https://github.com/ruvnet/ruvector
863
- `);
864
- }
865
-
866
- // Main
867
- async function main() {
868
- const parsed = parseArgs(args);
869
- const { command, positional, flags } = parsed;
870
-
871
- if (!command || command === 'help' || flags.help) {
872
- printHelp();
873
- return;
874
- }
875
-
876
- // Create engine for commands that need it
877
- const llm = new RuvLLM({
878
- embeddingDim: flags.dim ? parseInt(flags.dim) : 768,
879
- learningEnabled: flags['no-learning'] ? false : true,
880
- });
881
-
882
- try {
883
- switch (command) {
884
- case 'query':
885
- if (!positional[0]) {
886
- console.error('Error: query text required');
887
- process.exit(1);
888
- }
889
- await runQuery(llm, positional[0], flags);
890
- break;
891
-
892
- case 'generate':
893
- if (!positional[0]) {
894
- console.error('Error: prompt required');
895
- process.exit(1);
896
- }
897
- await runGenerate(llm, positional[0], flags);
898
- break;
899
-
900
- case 'route':
901
- if (!positional[0]) {
902
- console.error('Error: text required');
903
- process.exit(1);
904
- }
905
- await runRoute(llm, positional[0], flags);
906
- break;
907
-
908
- case 'memory':
909
- const subcommand = positional[0];
910
- if (subcommand === 'add') {
911
- if (!positional[1]) {
912
- console.error('Error: content required');
913
- process.exit(1);
914
- }
915
- await runMemoryAdd(llm, positional[1], flags);
916
- } else if (subcommand === 'search') {
917
- if (!positional[1]) {
918
- console.error('Error: query required');
919
- process.exit(1);
920
- }
921
- await runMemorySearch(llm, positional[1], flags);
922
- } else {
923
- console.error('Error: unknown memory subcommand. Use "add" or "search"');
924
- process.exit(1);
925
- }
926
- break;
927
-
928
- case 'embed':
929
- if (!positional[0]) {
930
- console.error('Error: text required');
931
- process.exit(1);
932
- }
933
- await runEmbed(llm, positional[0], flags);
934
- break;
935
-
936
- case 'similarity':
937
- if (!positional[0] || !positional[1]) {
938
- console.error('Error: two texts required');
939
- process.exit(1);
940
- }
941
- await runSimilarity(llm, positional[0], positional[1], flags);
942
- break;
943
-
944
- case 'stats':
945
- await runStats(llm, flags);
946
- break;
947
-
948
- case 'benchmark':
949
- const benchSubcmd = positional[0];
950
- if (benchSubcmd === 'routing') {
951
- await runBenchmarkRouting(flags);
952
- } else if (benchSubcmd === 'embedding' || benchSubcmd === 'embeddings') {
953
- await runBenchmarkEmbedding(flags);
954
- } else if (benchSubcmd === 'full' || benchSubcmd === 'all') {
955
- await runBenchmarkFull(flags);
956
- } else if (benchSubcmd === 'compare') {
957
- await runBenchmarkCompare(flags);
958
- } else if (benchSubcmd === 'simd' || !benchSubcmd) {
959
- // Default to SIMD benchmark for backwards compatibility
960
- await runBenchmark(flags);
961
- } else {
962
- console.error(`Unknown benchmark type: ${benchSubcmd}`);
963
- console.error('Available: routing, embedding, full, simd, compare');
964
- process.exit(1);
965
- }
966
- break;
967
-
968
- case 'info':
969
- await runInfo(flags);
970
- break;
971
-
972
- case 'models':
973
- const modelsSubcmd = positional[0];
974
- if (!modelsSubcmd || modelsSubcmd === 'list') {
975
- await runModelsList(flags);
976
- } else if (modelsSubcmd === 'download') {
977
- await runModelsDownload(positional[1], flags);
978
- } else if (modelsSubcmd === 'status') {
979
- await runModelsStatus(flags);
980
- } else if (modelsSubcmd === 'delete' || modelsSubcmd === 'remove') {
981
- await runModelsDelete(positional[1], flags);
982
- } else {
983
- // Treat subcommand as model ID for download
984
- await runModelsDownload(modelsSubcmd, flags);
985
- }
986
- break;
987
-
988
- case 'train':
989
- const trainSubcmd = positional[0];
990
- if (!trainSubcmd || trainSubcmd === 'contrastive') {
991
- await runTrainContrastive(flags);
992
- } else if (trainSubcmd === 'dataset') {
993
- await runTrainDataset(flags);
994
- } else if (trainSubcmd === 'stats') {
995
- await runTrainStats(flags);
996
- } else {
997
- console.error(`Unknown train subcommand: ${trainSubcmd}`);
998
- console.error('Available: contrastive, dataset, stats');
999
- process.exit(1);
1000
- }
1001
- break;
1002
-
1003
- default:
1004
- console.error(`Unknown command: ${command}`);
1005
- console.error('Run "ruvllm help" for usage information.');
1006
- process.exit(1);
1007
- }
1008
- } catch (error) {
1009
- console.error('Error:', error.message);
1010
- if (flags.verbose) {
1011
- console.error(error.stack);
1012
- }
1013
- process.exit(1);
1014
- }
1015
- }
1016
-
1017
- main().catch(err => {
1018
- console.error('Fatal error:', err);
1019
- process.exit(1);
1020
- });
1
+ #!/usr/bin/env node
2
+ /**
3
+ * RuvLLM CLI - Self-learning LLM orchestration
4
+ *
5
+ * Usage:
6
+ * ruvllm query "What is machine learning?"
7
+ * ruvllm generate "Write a haiku about AI"
8
+ * ruvllm memory add "Important context"
9
+ * ruvllm memory search "context"
10
+ * ruvllm models list
11
+ * ruvllm models download claude-code
12
+ * ruvllm stats
13
+ * ruvllm benchmark
14
+ */
15
+
16
+ const { RuvLLM, SimdOps, version, hasSimdSupport, ModelDownloader, listModels, getModelInfo, RUVLTRA_MODELS, getDefaultModelsDir, runRoutingBenchmark, formatRoutingResults, baselineKeywordRouter, runEmbeddingBenchmark, formatEmbeddingResults, runFullBenchmark, formatFullResults, ROUTING_TEST_CASES, runFullComparison, formatComparisonResults, ContrastiveTrainer, AGENT_TRAINING_DATA, generateTrainingDataset, generateContrastivePairs, getDatasetStats, tripletLoss, cosineSimilarity } = require('../dist/cjs/index.js');
17
+
18
+ const args = process.argv.slice(2);
19
+ const command = args[0];
20
+
21
+ // Parse CLI arguments
22
+ function parseArgs(args) {
23
+ const result = { flags: {}, positional: [] };
24
+ for (let i = 0; i < args.length; i++) {
25
+ const arg = args[i];
26
+ if (arg.startsWith('--')) {
27
+ const key = arg.slice(2);
28
+ const nextArg = args[i + 1];
29
+ if (nextArg && !nextArg.startsWith('--')) {
30
+ result.flags[key] = nextArg;
31
+ i++;
32
+ } else {
33
+ result.flags[key] = true;
34
+ }
35
+ } else if (!result.command) {
36
+ result.command = arg;
37
+ } else {
38
+ result.positional.push(arg);
39
+ }
40
+ }
41
+ return result;
42
+ }
43
+
44
+ // Format output
45
+ function formatJson(obj) {
46
+ return JSON.stringify(obj, null, 2);
47
+ }
48
+
49
+ function formatTable(data) {
50
+ const maxKeyLen = Math.max(...Object.keys(data).map(k => k.length));
51
+ return Object.entries(data)
52
+ .map(([k, v]) => ` ${k.padEnd(maxKeyLen)} : ${v}`)
53
+ .join('\n');
54
+ }
55
+
56
+ // Commands
57
+ async function runQuery(llm, text, flags) {
58
+ const config = {};
59
+ if (flags.temperature) config.temperature = parseFloat(flags.temperature);
60
+ if (flags['max-tokens']) config.maxTokens = parseInt(flags['max-tokens']);
61
+ if (flags['top-p']) config.topP = parseFloat(flags['top-p']);
62
+ if (flags['top-k']) config.topK = parseInt(flags['top-k']);
63
+
64
+ const response = llm.query(text, config);
65
+
66
+ if (flags.json) {
67
+ console.log(formatJson(response));
68
+ } else {
69
+ console.log('\n' + response.text);
70
+ console.log(`\n--- Model: ${response.model} | Confidence: ${(response.confidence * 100).toFixed(1)}% | Latency: ${response.latencyMs.toFixed(2)}ms ---`);
71
+ }
72
+ }
73
+
74
+ async function runGenerate(llm, prompt, flags) {
75
+ console.error('Warning: Built-in SIMD inference is experimental. For production use, configure an external LLM provider (Ollama, OpenAI, etc.).');
76
+
77
+ const config = {};
78
+ if (flags.temperature) config.temperature = parseFloat(flags.temperature);
79
+ if (flags['max-tokens']) config.maxTokens = parseInt(flags['max-tokens']);
80
+ if (flags['top-p']) config.topP = parseFloat(flags['top-p']);
81
+
82
+ const text = llm.generate(prompt, config);
83
+ console.log(text);
84
+ }
85
+
86
+ async function runMemoryAdd(llm, content, flags) {
87
+ const metadata = flags.metadata ? JSON.parse(flags.metadata) : undefined;
88
+ const id = llm.addMemory(content, metadata);
89
+ console.log(`Added memory with ID: ${id}`);
90
+ }
91
+
92
+ async function runMemorySearch(llm, query, flags) {
93
+ const k = flags.k ? parseInt(flags.k) : 10;
94
+ const results = llm.searchMemory(query, k);
95
+
96
+ if (flags.json) {
97
+ console.log(formatJson(results));
98
+ } else {
99
+ if (results.length === 0) {
100
+ console.log('No results found.');
101
+ return;
102
+ }
103
+ results.forEach((r, i) => {
104
+ console.log(`\n[${i + 1}] Score: ${r.score.toFixed(4)} | ID: ${r.id}`);
105
+ console.log(` ${r.content.slice(0, 100)}${r.content.length > 100 ? '...' : ''}`);
106
+ });
107
+ }
108
+ }
109
+
110
+ async function runStats(llm, flags) {
111
+ let stats;
112
+ try {
113
+ stats = llm.stats();
114
+ } catch (e) {
115
+ stats = null;
116
+ }
117
+
118
+ if (!stats) {
119
+ if (flags.json) {
120
+ console.log(formatJson({ error: 'No inference data available' }));
121
+ } else {
122
+ console.log('\nNo inference data available. Run some queries first.');
123
+ }
124
+ return;
125
+ }
126
+
127
+ if (flags.json) {
128
+ console.log(formatJson(stats));
129
+ } else {
130
+ console.log('\nRuvLLM Statistics:');
131
+ console.log(formatTable({
132
+ 'Total Queries': stats.totalQueries ?? 0,
133
+ 'Memory Nodes': stats.memoryNodes ?? 0,
134
+ 'Patterns Learned': stats.patternsLearned ?? 0,
135
+ 'Avg Latency': `${(stats.avgLatencyMs ?? 0).toFixed(2)}ms`,
136
+ 'Cache Hit Rate': `${((stats.cacheHitRate ?? 0) * 100).toFixed(1)}%`,
137
+ 'Router Accuracy': `${((stats.routerAccuracy ?? 0) * 100).toFixed(1)}%`,
138
+ }));
139
+ }
140
+ }
141
+
142
+ async function runRoute(llm, text, flags) {
143
+ const decision = llm.route(text);
144
+
145
+ if (flags.json) {
146
+ console.log(formatJson(decision));
147
+ } else {
148
+ console.log('\nRouting Decision:');
149
+ console.log(formatTable({
150
+ 'Model': decision.model,
151
+ 'Context Size': decision.contextSize,
152
+ 'Temperature': decision.temperature.toFixed(2),
153
+ 'Top-P': decision.topP.toFixed(2),
154
+ 'Confidence': `${(decision.confidence * 100).toFixed(1)}%`,
155
+ }));
156
+ }
157
+ }
158
+
159
+ async function runEmbed(llm, text, flags) {
160
+ const embedding = llm.embed(text);
161
+
162
+ if (flags.json) {
163
+ console.log(formatJson({ embedding, dimensions: embedding.length }));
164
+ } else {
165
+ console.log(`Embedding (${embedding.length} dimensions):`);
166
+ console.log(` First 10: [${embedding.slice(0, 10).map(x => x.toFixed(4)).join(', ')}...]`);
167
+ console.log(` Norm: ${Math.sqrt(embedding.reduce((s, x) => s + x * x, 0)).toFixed(4)}`);
168
+ }
169
+ }
170
+
171
+ async function runSimilarity(llm, text1, text2, flags) {
172
+ const score = llm.similarity(text1, text2);
173
+
174
+ if (flags.json) {
175
+ console.log(formatJson({ text1, text2, similarity: score }));
176
+ } else {
177
+ console.log(`Similarity: ${(score * 100).toFixed(2)}%`);
178
+ }
179
+ }
180
+
181
+ async function runBenchmark(flags) {
182
+ const simd = new SimdOps();
183
+ const dims = flags.dims ? parseInt(flags.dims) : 768;
184
+ const iterations = flags.iterations ? parseInt(flags.iterations) : 1000;
185
+
186
+ // Generate test vectors
187
+ const a = Array.from({ length: dims }, () => Math.random());
188
+ const b = Array.from({ length: dims }, () => Math.random());
189
+
190
+ console.log(`\nBenchmark: ${dims} dimensions, ${iterations} iterations`);
191
+ console.log(`SIMD: ${simd.isNative() ? 'Native' : 'JavaScript fallback'}`);
192
+ console.log(`Capabilities: ${simd.capabilities().join(', ')}`);
193
+ console.log('');
194
+
195
+ // Dot product benchmark
196
+ let start = Date.now();
197
+ for (let i = 0; i < iterations; i++) {
198
+ simd.dotProduct(a, b);
199
+ }
200
+ let elapsed = Date.now() - start;
201
+ console.log(`Dot Product: ${elapsed}ms (${(iterations / elapsed * 1000).toFixed(0)} ops/sec)`);
202
+
203
+ // Cosine similarity benchmark
204
+ start = Date.now();
205
+ for (let i = 0; i < iterations; i++) {
206
+ simd.cosineSimilarity(a, b);
207
+ }
208
+ elapsed = Date.now() - start;
209
+ console.log(`Cosine Similarity: ${elapsed}ms (${(iterations / elapsed * 1000).toFixed(0)} ops/sec)`);
210
+
211
+ // L2 distance benchmark
212
+ start = Date.now();
213
+ for (let i = 0; i < iterations; i++) {
214
+ simd.l2Distance(a, b);
215
+ }
216
+ elapsed = Date.now() - start;
217
+ console.log(`L2 Distance: ${elapsed}ms (${(iterations / elapsed * 1000).toFixed(0)} ops/sec)`);
218
+
219
+ // Softmax benchmark
220
+ start = Date.now();
221
+ for (let i = 0; i < iterations; i++) {
222
+ simd.softmax(a);
223
+ }
224
+ elapsed = Date.now() - start;
225
+ console.log(`Softmax: ${elapsed}ms (${(iterations / elapsed * 1000).toFixed(0)} ops/sec)`);
226
+ }
227
+
228
+ async function runInfo(flags) {
229
+ const llm = new RuvLLM();
230
+
231
+ const info = {
232
+ version: version(),
233
+ native: llm.isNativeLoaded(),
234
+ simd: hasSimdSupport(),
235
+ capabilities: llm.simdCapabilities(),
236
+ platform: process.platform,
237
+ arch: process.arch,
238
+ nodeVersion: process.version,
239
+ };
240
+
241
+ if (flags.json) {
242
+ console.log(formatJson(info));
243
+ } else {
244
+ console.log('\nRuvLLM Info:');
245
+ console.log(formatTable({
246
+ 'Version': info.version,
247
+ 'Native Module': info.native ? 'Loaded' : 'Fallback (JS)',
248
+ 'SIMD Support': info.simd ? 'Yes' : 'No',
249
+ 'Capabilities': info.capabilities.join(', '),
250
+ 'Platform': `${info.platform}-${info.arch}`,
251
+ 'Node.js': info.nodeVersion,
252
+ }));
253
+ }
254
+ }
255
+
256
+ // Model management commands
257
+ async function runModelsList(flags) {
258
+ const downloader = new ModelDownloader();
259
+ const status = downloader.getStatus();
260
+
261
+ if (flags.json) {
262
+ console.log(formatJson(status));
263
+ } else {
264
+ console.log('\n╔══════════════════════════════════════════════════════════════════════════╗');
265
+ console.log('║ RuvLTRA Models ║');
266
+ console.log('║ https://huggingface.co/ruv/ruvltra ║');
267
+ console.log('╠══════════════════════════════════════════════════════════════════════════╣');
268
+ console.log('║ Model │ Size │ Params │ Status │ Use Case ║');
269
+ console.log('╠══════════════════════════════════════════════════════════════════════════╣');
270
+
271
+ for (const { model, downloaded } of status) {
272
+ const statusIcon = downloaded ? '✓ Ready ' : '○ Not DL ';
273
+ const name = model.id.padEnd(12);
274
+ const size = model.size.padEnd(7);
275
+ const params = model.parameters.padEnd(6);
276
+ const useCase = model.useCase.slice(0, 24).padEnd(24);
277
+ console.log(`║ ${name} │ ${size} │ ${params} │ ${statusIcon} │ ${useCase} ║`);
278
+ }
279
+
280
+ console.log('╚══════════════════════════════════════════════════════════════════════════╝');
281
+ console.log(`\nModels directory: ${getDefaultModelsDir()}`);
282
+ console.log('\nDownload with: ruvllm models download <model-id>');
283
+ console.log(' Examples: ruvllm models download claude-code');
284
+ console.log(' ruvllm models download --all');
285
+ }
286
+ }
287
+
288
+ async function runModelsDownload(modelId, flags) {
289
+ const downloader = new ModelDownloader();
290
+
291
+ if (flags.all) {
292
+ console.log('\nDownloading all RuvLTRA models...\n');
293
+ const models = listModels();
294
+
295
+ for (const model of models) {
296
+ console.log(`\n[${model.id}] ${model.name} (${model.size})`);
297
+
298
+ if (downloader.isDownloaded(model.id) && !flags.force) {
299
+ console.log(' Already downloaded, skipping (use --force to re-download)');
300
+ continue;
301
+ }
302
+
303
+ try {
304
+ const lastPercent = { value: -1 };
305
+ const path = await downloader.download(model.id, {
306
+ force: flags.force,
307
+ onProgress: (p) => {
308
+ const percent = Math.floor(p.percent / 5) * 5; // Round to 5%
309
+ if (percent !== lastPercent.value) {
310
+ const bar = '█'.repeat(percent / 5) + '░'.repeat(20 - percent / 5);
311
+ const speed = (p.speedBps / 1024 / 1024).toFixed(1);
312
+ const eta = p.etaSeconds < 60
313
+ ? `${Math.ceil(p.etaSeconds)}s`
314
+ : `${Math.ceil(p.etaSeconds / 60)}m`;
315
+ process.stdout.write(`\r [${bar}] ${p.percent}% | ${speed} MB/s | ETA: ${eta} `);
316
+ lastPercent.value = percent;
317
+ }
318
+ },
319
+ });
320
+ console.log(`\n ✓ Downloaded to: ${path}`);
321
+ } catch (error) {
322
+ console.error(`\n ✗ Failed: ${error.message}`);
323
+ }
324
+ }
325
+
326
+ console.log('\n\nDownload complete!');
327
+ return;
328
+ }
329
+
330
+ if (!modelId) {
331
+ console.error('Error: model ID required. Use --all to download all models.');
332
+ console.error('\nAvailable models:');
333
+ listModels().forEach(m => console.error(` - ${m.id}: ${m.name} (${m.size})`));
334
+ process.exit(1);
335
+ }
336
+
337
+ const model = getModelInfo(modelId);
338
+ if (!model) {
339
+ console.error(`Error: Unknown model "${modelId}"`);
340
+ console.error('\nAvailable models:');
341
+ listModels().forEach(m => console.error(` - ${m.id}: ${m.name} (${m.size})`));
342
+ process.exit(1);
343
+ }
344
+
345
+ console.log(`\nDownloading ${model.name} (${model.size})...`);
346
+ console.log(`From: ${model.url}\n`);
347
+
348
+ if (downloader.isDownloaded(modelId) && !flags.force) {
349
+ const path = downloader.getModelPath(modelId);
350
+ console.log(`Model already downloaded at: ${path}`);
351
+ console.log('Use --force to re-download.');
352
+ return;
353
+ }
354
+
355
+ const lastPercent = { value: -1 };
356
+ try {
357
+ const path = await downloader.download(modelId, {
358
+ force: flags.force,
359
+ onProgress: (p) => {
360
+ const percent = Math.floor(p.percent / 2) * 2; // Round to 2%
361
+ if (percent !== lastPercent.value) {
362
+ const bar = '█'.repeat(Math.floor(percent / 5)) + '░'.repeat(20 - Math.floor(percent / 5));
363
+ const downloaded = (p.downloaded / 1024 / 1024).toFixed(1);
364
+ const total = (p.total / 1024 / 1024).toFixed(1);
365
+ const speed = (p.speedBps / 1024 / 1024).toFixed(1);
366
+ const eta = p.etaSeconds < 60
367
+ ? `${Math.ceil(p.etaSeconds)}s`
368
+ : `${Math.ceil(p.etaSeconds / 60)}m`;
369
+ process.stdout.write(`\r[${bar}] ${p.percent}% | ${downloaded}/${total} MB | ${speed} MB/s | ETA: ${eta} `);
370
+ lastPercent.value = percent;
371
+ }
372
+ },
373
+ });
374
+ console.log(`\n\n✓ Downloaded to: ${path}`);
375
+ console.log(`\nModel ready to use!`);
376
+ console.log(` Context length: ${model.contextLength} tokens`);
377
+ console.log(` Quantization: ${model.quantization}`);
378
+ } catch (error) {
379
+ console.error(`\n\n✗ Download failed: ${error.message}`);
380
+ process.exit(1);
381
+ }
382
+ }
383
+
384
+ async function runModelsStatus(flags) {
385
+ const downloader = new ModelDownloader();
386
+ const status = downloader.getStatus();
387
+
388
+ if (flags.json) {
389
+ console.log(formatJson(status.map(s => ({
390
+ id: s.model.id,
391
+ name: s.model.name,
392
+ downloaded: s.downloaded,
393
+ path: s.path,
394
+ size: s.model.size,
395
+ }))));
396
+ } else {
397
+ console.log('\nModel Status:');
398
+ console.log(`Directory: ${getDefaultModelsDir()}\n`);
399
+
400
+ for (const { model, downloaded, path } of status) {
401
+ const icon = downloaded ? '✓' : '○';
402
+ const status = downloaded ? 'Ready' : 'Not downloaded';
403
+ console.log(` ${icon} ${model.name.padEnd(25)} ${status.padEnd(15)} ${model.size}`);
404
+ if (downloaded) {
405
+ console.log(` Path: ${path}`);
406
+ }
407
+ }
408
+ }
409
+ }
410
+
411
+ async function runModelsDelete(modelId, flags) {
412
+ const downloader = new ModelDownloader();
413
+
414
+ if (flags.all) {
415
+ const count = downloader.deleteAll();
416
+ console.log(`Deleted ${count} model(s).`);
417
+ return;
418
+ }
419
+
420
+ if (!modelId) {
421
+ console.error('Error: model ID required. Use --all to delete all models.');
422
+ process.exit(1);
423
+ }
424
+
425
+ if (downloader.delete(modelId)) {
426
+ console.log(`Deleted model: ${modelId}`);
427
+ } else {
428
+ console.log(`Model not found or not downloaded: ${modelId}`);
429
+ }
430
+ }
431
+
432
+ // Benchmark commands for Claude Code use cases
433
+ async function runBenchmarkRouting(flags) {
434
+ console.log('\nRunning Routing Benchmark...');
435
+ console.log(`Testing ${ROUTING_TEST_CASES.length} task routing scenarios\n`);
436
+
437
+ const llm = new RuvLLM({ embeddingDim: 768, learningEnabled: false });
438
+
439
+ // Router function using the model
440
+ const modelRouter = (task) => {
441
+ try {
442
+ const decision = llm.route(task);
443
+ return { agent: decision.model, confidence: decision.confidence };
444
+ } catch (e) {
445
+ // Fallback to keyword router if model not available
446
+ return baselineKeywordRouter(task);
447
+ }
448
+ };
449
+
450
+ // Run with baseline (keyword) router
451
+ console.log('Baseline (keyword matching):');
452
+ const baselineResults = runRoutingBenchmark(baselineKeywordRouter);
453
+
454
+ if (flags.json) {
455
+ console.log(JSON.stringify(baselineResults, null, 2));
456
+ } else {
457
+ console.log(formatRoutingResults(baselineResults));
458
+ }
459
+
460
+ // Try model router if native is available
461
+ if (llm.isNativeLoaded() && !flags['baseline-only']) {
462
+ console.log('\nModel Router (RuvLTRA):');
463
+ const modelResults = runRoutingBenchmark(modelRouter);
464
+ if (flags.json) {
465
+ console.log(JSON.stringify(modelResults, null, 2));
466
+ } else {
467
+ console.log(formatRoutingResults(modelResults));
468
+ }
469
+
470
+ // Comparison
471
+ const improvement = modelResults.accuracy - baselineResults.accuracy;
472
+ console.log(`\nComparison: Model ${improvement >= 0 ? '+' : ''}${(improvement * 100).toFixed(1)}% vs baseline`);
473
+ }
474
+ }
475
+
476
+ async function runBenchmarkEmbedding(flags) {
477
+ console.log('\nRunning Embedding Benchmark...');
478
+ console.log('Testing similarity detection, clustering, and search quality\n');
479
+
480
+ const llm = new RuvLLM({ embeddingDim: 768, learningEnabled: false });
481
+ const simd = new SimdOps();
482
+
483
+ // Embedder function
484
+ const embedder = (text) => {
485
+ try {
486
+ return llm.embed(text);
487
+ } catch (e) {
488
+ // Fallback: simple hash-based embedding for testing
489
+ const hash = text.split('').reduce((h, c) => ((h << 5) - h + c.charCodeAt(0)) | 0, 0);
490
+ return Array.from({ length: 768 }, (_, i) => Math.sin(hash + i) * 0.5);
491
+ }
492
+ };
493
+
494
+ // Similarity function
495
+ const similarity = (a, b) => {
496
+ try {
497
+ return simd.cosineSimilarity(a, b);
498
+ } catch (e) {
499
+ // Fallback cosine similarity
500
+ let dot = 0, normA = 0, normB = 0;
501
+ for (let i = 0; i < a.length; i++) {
502
+ dot += a[i] * b[i];
503
+ normA += a[i] * a[i];
504
+ normB += b[i] * b[i];
505
+ }
506
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB));
507
+ }
508
+ };
509
+
510
+ const results = runEmbeddingBenchmark(embedder, similarity);
511
+
512
+ if (flags.json) {
513
+ console.log(JSON.stringify(results, null, 2));
514
+ } else {
515
+ console.log(formatEmbeddingResults(results));
516
+ }
517
+ }
518
+
519
+ async function runBenchmarkCompare(flags) {
520
+ try {
521
+ const results = await runFullComparison();
522
+
523
+ if (flags.json) {
524
+ console.log(JSON.stringify(results, null, 2));
525
+ } else {
526
+ console.log(formatComparisonResults(results));
527
+ }
528
+ } catch (error) {
529
+ console.error('Comparison failed:', error.message);
530
+ process.exit(1);
531
+ }
532
+ }
533
+
534
+ // Training commands
535
+ async function runTrainContrastive(flags) {
536
+ console.log('\n╔═══════════════════════════════════════════════════════════════════════════╗');
537
+ console.log('║ RuvLTRA Contrastive Fine-tuning Pipeline ║');
538
+ console.log('╚═══════════════════════════════════════════════════════════════════════════╝\n');
539
+
540
+ const stats = getDatasetStats();
541
+ const outputPath = flags.output || './training-output';
542
+
543
+ console.log(`Training examples: ${stats.totalExamples}`);
544
+ console.log(`Contrastive pairs: ${stats.contrastivePairs}`);
545
+ console.log(`Agent types: ${stats.agentTypes}`);
546
+ console.log(`Output: ${outputPath}\n`);
547
+
548
+ // Create trainer
549
+ const trainer = new ContrastiveTrainer({
550
+ epochs: parseInt(flags.epochs) || 10,
551
+ batchSize: parseInt(flags['batch-size']) || 16,
552
+ learningRate: parseFloat(flags.lr) || 0.0001,
553
+ margin: parseFloat(flags.margin) || 0.5,
554
+ outputPath,
555
+ });
556
+
557
+ // Get embeddings for agents
558
+ const llm = new RuvLLM({ embeddingDim: 768, learningEnabled: false });
559
+ const agentEmbeddings = {};
560
+
561
+ console.log('Computing agent embeddings...');
562
+ for (const [agent, data] of Object.entries(AGENT_TRAINING_DATA)) {
563
+ process.stdout.write(` ${agent}... `);
564
+ try {
565
+ const emb = llm.embed(data.description);
566
+ agentEmbeddings[agent] = emb;
567
+ trainer.addAgentEmbedding(agent, emb);
568
+ console.log('done');
569
+ } catch (e) {
570
+ // Fallback embedding
571
+ const hash = data.description.split('').reduce((h, c) => ((h << 5) - h + c.charCodeAt(0)) | 0, 0);
572
+ const emb = Array.from({ length: 768 }, (_, i) => Math.sin(hash + i) * 0.5);
573
+ agentEmbeddings[agent] = emb;
574
+ trainer.addAgentEmbedding(agent, emb);
575
+ console.log('done (fallback)');
576
+ }
577
+ }
578
+
579
+ // Generate triplets
580
+ console.log('\nGenerating training triplets...');
581
+ const examples = generateTrainingDataset();
582
+ const agents = Object.keys(AGENT_TRAINING_DATA);
583
+
584
+ let tripletCount = 0;
585
+ for (const example of examples.slice(0, 200)) { // Limit for speed
586
+ let taskEmb;
587
+ try {
588
+ taskEmb = llm.embed(example.task);
589
+ } catch (e) {
590
+ const hash = example.task.split('').reduce((h, c) => ((h << 5) - h + c.charCodeAt(0)) | 0, 0);
591
+ taskEmb = Array.from({ length: 768 }, (_, i) => Math.sin(hash + i) * 0.5);
592
+ }
593
+
594
+ const positiveAgent = example.agent;
595
+ const positiveEmb = agentEmbeddings[positiveAgent];
596
+
597
+ // Hard negatives
598
+ const hardNegatives = example.confusing_with
599
+ ? [example.confusing_with]
600
+ : agents.filter(a => a !== positiveAgent).slice(0, 2);
601
+
602
+ for (const negAgent of hardNegatives) {
603
+ const negEmb = agentEmbeddings[negAgent];
604
+ if (negEmb) {
605
+ trainer.addTriplet(
606
+ example.task, taskEmb,
607
+ positiveAgent, positiveEmb,
608
+ negAgent, negEmb,
609
+ !!example.confusing_with
610
+ );
611
+ tripletCount++;
612
+ }
613
+ }
614
+ }
615
+
616
+ console.log(`Created ${tripletCount} triplets\n`);
617
+
618
+ // Train
619
+ console.log('Training...');
620
+ const result = trainer.train();
621
+
622
+ // Export data
623
+ console.log('\nExporting training data...');
624
+ const exportPath = trainer.exportTrainingData();
625
+ const loraConfig = trainer.generateLoRAConfig();
626
+ const scriptPath = trainer.generateTrainingScript();
627
+
628
+ // Summary
629
+ console.log('\n═══════════════════════════════════════════════════════════════════════════');
630
+ console.log(' TRAINING SUMMARY');
631
+ console.log('═══════════════════════════════════════════════════════════════════════════\n');
632
+
633
+ console.log('Training data exported:');
634
+ console.log(` - ${exportPath}/triplets.jsonl (${tripletCount} triplets)`);
635
+ console.log(` - ${exportPath}/triplets.csv (spreadsheet format)`);
636
+ console.log(` - ${exportPath}/embeddings.json (precomputed embeddings)`);
637
+ console.log(` - ${exportPath}/lora_config.json (LoRA configuration)`);
638
+ console.log(` - ${exportPath}/train.sh (training script)\n`);
639
+
640
+ console.log('Training loss (simulated):');
641
+ console.log(` Initial: ${result.initialLoss.toFixed(4)}`);
642
+ console.log(` Final: ${result.finalLoss.toFixed(4)}`);
643
+ console.log(` Improvement: ${result.improvement.toFixed(1)}%\n`);
644
+
645
+ console.log('To fine-tune on GPU:');
646
+ console.log(` cd ${exportPath}`);
647
+ console.log(' chmod +x train.sh && ./train.sh\n');
648
+ }
649
+
650
+ async function runTrainDataset(flags) {
651
+ console.log('\n╔═══════════════════════════════════════════════════════════════════════════╗');
652
+ console.log('║ RuvLTRA Training Dataset Generator ║');
653
+ console.log('╚═══════════════════════════════════════════════════════════════════════════╝\n');
654
+
655
+ const stats = getDatasetStats();
656
+ const outputPath = flags.output || './data/training';
657
+
658
+ console.log(`Generating training data for ${stats.agentTypes} agent types...\n`);
659
+
660
+ // Generate dataset
661
+ const examples = generateTrainingDataset();
662
+ const pairs = generateContrastivePairs();
663
+
664
+ // Export
665
+ const { existsSync, mkdirSync, writeFileSync } = require('fs');
666
+ const { join } = require('path');
667
+
668
+ if (!existsSync(outputPath)) {
669
+ mkdirSync(outputPath, { recursive: true });
670
+ }
671
+
672
+ // JSONL format
673
+ writeFileSync(
674
+ join(outputPath, 'routing-examples.jsonl'),
675
+ examples.map(e => JSON.stringify(e)).join('\n')
676
+ );
677
+
678
+ // Contrastive pairs
679
+ writeFileSync(
680
+ join(outputPath, 'contrastive-pairs.jsonl'),
681
+ pairs.map(p => JSON.stringify(p)).join('\n')
682
+ );
683
+
684
+ // CSV format
685
+ const csv = [
686
+ 'task,agent,complexity,confusing_with',
687
+ ...examples.map(e => `"${e.task.replace(/"/g, '""')}",${e.agent},${e.complexity || ''},${e.confusing_with || ''}`)
688
+ ].join('\n');
689
+ writeFileSync(join(outputPath, 'routing-examples.csv'), csv);
690
+
691
+ console.log('Export complete:');
692
+ console.log(` - ${join(outputPath, 'routing-examples.jsonl')} (${examples.length} examples)`);
693
+ console.log(` - ${join(outputPath, 'contrastive-pairs.jsonl')} (${pairs.length} pairs)`);
694
+ console.log(` - ${join(outputPath, 'routing-examples.csv')} (spreadsheet format)\n`);
695
+
696
+ console.log('Dataset statistics:');
697
+ console.log(` Total examples: ${stats.totalExamples}`);
698
+ console.log(` Contrastive pairs: ${stats.contrastivePairs}`);
699
+ console.log(` Agent types: ${stats.agentTypes}`);
700
+ console.log(` Agents: ${stats.agents.join(', ')}\n`);
701
+ }
702
+
703
+ async function runTrainStats(flags) {
704
+ const stats = getDatasetStats();
705
+
706
+ if (flags.json) {
707
+ console.log(JSON.stringify(stats, null, 2));
708
+ } else {
709
+ console.log('\nTraining Dataset Statistics:');
710
+ console.log(` Total examples: ${stats.totalExamples}`);
711
+ console.log(` Contrastive pairs: ${stats.contrastivePairs}`);
712
+ console.log(` Agent types: ${stats.agentTypes}`);
713
+ console.log('\nAgents:');
714
+ for (const agent of stats.agents) {
715
+ const data = AGENT_TRAINING_DATA[agent];
716
+ console.log(` - ${agent}: ${data.examples.length} examples, ${data.keywords.length} keywords`);
717
+ }
718
+ }
719
+ }
720
+
721
+ async function runBenchmarkFull(flags) {
722
+ console.log('\n╔═══════════════════════════════════════════════════════════════════════════╗');
723
+ console.log('║ RUVLTRA FULL BENCHMARK SUITE ║');
724
+ console.log('║ Evaluating for Claude Code Use Cases ║');
725
+ console.log('╚═══════════════════════════════════════════════════════════════════════════╝\n');
726
+
727
+ const llm = new RuvLLM({ embeddingDim: 768, learningEnabled: false });
728
+ const simd = new SimdOps();
729
+
730
+ const modelName = llm.isNativeLoaded() ? 'RuvLTRA (native)' : 'RuvLTRA (JS fallback)';
731
+
732
+ // Router
733
+ const router = (task) => {
734
+ try {
735
+ const decision = llm.route(task);
736
+ return { agent: decision.model, confidence: decision.confidence };
737
+ } catch (e) {
738
+ return baselineKeywordRouter(task);
739
+ }
740
+ };
741
+
742
+ // Embedder
743
+ const embedder = (text) => {
744
+ try {
745
+ return llm.embed(text);
746
+ } catch (e) {
747
+ const hash = text.split('').reduce((h, c) => ((h << 5) - h + c.charCodeAt(0)) | 0, 0);
748
+ return Array.from({ length: 768 }, (_, i) => Math.sin(hash + i) * 0.5);
749
+ }
750
+ };
751
+
752
+ // Similarity
753
+ const similarity = (a, b) => {
754
+ try {
755
+ return simd.cosineSimilarity(a, b);
756
+ } catch (e) {
757
+ let dot = 0, normA = 0, normB = 0;
758
+ for (let i = 0; i < a.length; i++) {
759
+ dot += a[i] * b[i];
760
+ normA += a[i] * a[i];
761
+ normB += b[i] * b[i];
762
+ }
763
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB));
764
+ }
765
+ };
766
+
767
+ const results = runFullBenchmark(router, embedder, similarity, modelName);
768
+
769
+ if (flags.json) {
770
+ console.log(JSON.stringify(results, null, 2));
771
+ } else {
772
+ console.log(formatFullResults(results));
773
+ }
774
+ }
775
+
776
+ function printHelp() {
777
+ console.log(`
778
+ RuvLLM - Self-learning LLM Orchestration
779
+
780
+ Usage: ruvllm <command> [options]
781
+
782
+ Commands:
783
+ query <text> Query the LLM with automatic routing
784
+ generate <prompt> Generate text with SIMD inference
785
+ route <text> Get routing decision for query
786
+ memory add <content> Add content to memory
787
+ memory search <query> Search memory for similar content
788
+ embed <text> Get embedding for text
789
+ similarity <t1> <t2> Compute similarity between texts
790
+ stats Show engine statistics
791
+ benchmark Run SIMD performance benchmark
792
+ info Show system information
793
+ help Show this help message
794
+
795
+ Model Management:
796
+ models list List available RuvLTRA models
797
+ models download <id> Download a model from HuggingFace
798
+ models download --all Download all available models
799
+ models status Check which models are downloaded
800
+ models delete <id> Delete a downloaded model
801
+
802
+ Claude Code Benchmarks:
803
+ benchmark routing Test agent routing accuracy (100 tasks)
804
+ benchmark embedding Test embedding quality (similarity, search, clustering)
805
+ benchmark full Run complete benchmark suite
806
+ benchmark compare Compare Qwen base vs RuvLTRA Claude Code
807
+ benchmark simd Run SIMD performance benchmark
808
+
809
+ Training & Fine-tuning:
810
+ train contrastive Run contrastive fine-tuning pipeline
811
+ train dataset Generate training dataset (JSONL, CSV)
812
+ train stats Show training dataset statistics
813
+
814
+ Options:
815
+ --json Output as JSON
816
+ --temperature <float> Sampling temperature (0.0-2.0)
817
+ --max-tokens <int> Maximum tokens to generate
818
+ --top-p <float> Nucleus sampling (0.0-1.0)
819
+ --top-k <int> Top-k sampling
820
+ --k <int> Number of results for search
821
+ --metadata <json> Metadata for memory add
822
+ --dims <int> Dimensions for benchmark (default: 768)
823
+ --iterations <int> Iterations for benchmark (default: 1000)
824
+ --force Force re-download even if model exists
825
+ --all Apply to all models (download/delete)
826
+ --output <path> Output directory for training data
827
+ --epochs <int> Number of training epochs (default: 10)
828
+ --batch-size <int> Training batch size (default: 16)
829
+ --lr <float> Learning rate (default: 0.0001)
830
+ --margin <float> Triplet loss margin (default: 0.5)
831
+
832
+ Available Models (from https://huggingface.co/ruv/ruvltra):
833
+ claude-code RuvLTRA Claude Code (398MB) - Claude Code workflows
834
+ small RuvLTRA Small (398MB) - Edge devices, IoT
835
+ medium RuvLTRA Medium (669MB) - General purpose
836
+
837
+ Examples:
838
+ ruvllm query "What is machine learning?"
839
+ ruvllm generate "Write a poem about AI" --temperature 0.9
840
+ ruvllm memory add "Important context" --metadata '{"type":"note"}'
841
+ ruvllm memory search "context" --k 5
842
+ ruvllm similarity "hello world" "hi there"
843
+ ruvllm benchmark --dims 1024 --iterations 5000
844
+
845
+ # Model management
846
+ ruvllm models list
847
+ ruvllm models download claude-code
848
+ ruvllm models download --all
849
+ ruvllm models status
850
+
851
+ # Claude Code benchmarks
852
+ ruvllm benchmark routing # Test task routing accuracy
853
+ ruvllm benchmark embedding # Test embedding quality
854
+ ruvllm benchmark full # Run complete benchmark suite
855
+
856
+ # Training & fine-tuning
857
+ ruvllm train contrastive # Run contrastive fine-tuning
858
+ ruvllm train dataset # Generate training dataset
859
+ ruvllm train stats # Show dataset statistics
860
+ ruvllm train contrastive --epochs 20 --output ./my-training
861
+
862
+ Learn more: https://github.com/ruvnet/ruvector
863
+ `);
864
+ }
865
+
866
+ // Main
867
+ async function main() {
868
+ const parsed = parseArgs(args);
869
+ const { command, positional, flags } = parsed;
870
+
871
+ if (!command || command === 'help' || flags.help) {
872
+ printHelp();
873
+ return;
874
+ }
875
+
876
+ // Create engine for commands that need it
877
+ const llm = new RuvLLM({
878
+ embeddingDim: flags.dim ? parseInt(flags.dim) : 768,
879
+ learningEnabled: flags['no-learning'] ? false : true,
880
+ });
881
+
882
+ try {
883
+ switch (command) {
884
+ case 'query':
885
+ if (!positional[0]) {
886
+ console.error('Error: query text required');
887
+ process.exit(1);
888
+ }
889
+ await runQuery(llm, positional[0], flags);
890
+ break;
891
+
892
+ case 'generate':
893
+ if (!positional[0]) {
894
+ console.error('Error: prompt required');
895
+ process.exit(1);
896
+ }
897
+ await runGenerate(llm, positional[0], flags);
898
+ break;
899
+
900
+ case 'route':
901
+ if (!positional[0]) {
902
+ console.error('Error: text required');
903
+ process.exit(1);
904
+ }
905
+ await runRoute(llm, positional[0], flags);
906
+ break;
907
+
908
+ case 'memory':
909
+ const subcommand = positional[0];
910
+ if (subcommand === 'add') {
911
+ if (!positional[1]) {
912
+ console.error('Error: content required');
913
+ process.exit(1);
914
+ }
915
+ await runMemoryAdd(llm, positional[1], flags);
916
+ } else if (subcommand === 'search') {
917
+ if (!positional[1]) {
918
+ console.error('Error: query required');
919
+ process.exit(1);
920
+ }
921
+ await runMemorySearch(llm, positional[1], flags);
922
+ } else {
923
+ console.error('Error: unknown memory subcommand. Use "add" or "search"');
924
+ process.exit(1);
925
+ }
926
+ break;
927
+
928
+ case 'embed':
929
+ if (!positional[0]) {
930
+ console.error('Error: text required');
931
+ process.exit(1);
932
+ }
933
+ await runEmbed(llm, positional[0], flags);
934
+ break;
935
+
936
+ case 'similarity':
937
+ if (!positional[0] || !positional[1]) {
938
+ console.error('Error: two texts required');
939
+ process.exit(1);
940
+ }
941
+ await runSimilarity(llm, positional[0], positional[1], flags);
942
+ break;
943
+
944
+ case 'stats':
945
+ await runStats(llm, flags);
946
+ break;
947
+
948
+ case 'benchmark':
949
+ const benchSubcmd = positional[0];
950
+ if (benchSubcmd === 'routing') {
951
+ await runBenchmarkRouting(flags);
952
+ } else if (benchSubcmd === 'embedding' || benchSubcmd === 'embeddings') {
953
+ await runBenchmarkEmbedding(flags);
954
+ } else if (benchSubcmd === 'full' || benchSubcmd === 'all') {
955
+ await runBenchmarkFull(flags);
956
+ } else if (benchSubcmd === 'compare') {
957
+ await runBenchmarkCompare(flags);
958
+ } else if (benchSubcmd === 'simd' || !benchSubcmd) {
959
+ // Default to SIMD benchmark for backwards compatibility
960
+ await runBenchmark(flags);
961
+ } else {
962
+ console.error(`Unknown benchmark type: ${benchSubcmd}`);
963
+ console.error('Available: routing, embedding, full, simd, compare');
964
+ process.exit(1);
965
+ }
966
+ break;
967
+
968
+ case 'info':
969
+ await runInfo(flags);
970
+ break;
971
+
972
+ case 'models':
973
+ const modelsSubcmd = positional[0];
974
+ if (!modelsSubcmd || modelsSubcmd === 'list') {
975
+ await runModelsList(flags);
976
+ } else if (modelsSubcmd === 'download') {
977
+ await runModelsDownload(positional[1], flags);
978
+ } else if (modelsSubcmd === 'status') {
979
+ await runModelsStatus(flags);
980
+ } else if (modelsSubcmd === 'delete' || modelsSubcmd === 'remove') {
981
+ await runModelsDelete(positional[1], flags);
982
+ } else {
983
+ // Treat subcommand as model ID for download
984
+ await runModelsDownload(modelsSubcmd, flags);
985
+ }
986
+ break;
987
+
988
+ case 'train':
989
+ const trainSubcmd = positional[0];
990
+ if (!trainSubcmd || trainSubcmd === 'contrastive') {
991
+ await runTrainContrastive(flags);
992
+ } else if (trainSubcmd === 'dataset') {
993
+ await runTrainDataset(flags);
994
+ } else if (trainSubcmd === 'stats') {
995
+ await runTrainStats(flags);
996
+ } else {
997
+ console.error(`Unknown train subcommand: ${trainSubcmd}`);
998
+ console.error('Available: contrastive, dataset, stats');
999
+ process.exit(1);
1000
+ }
1001
+ break;
1002
+
1003
+ default:
1004
+ console.error(`Unknown command: ${command}`);
1005
+ console.error('Run "ruvllm help" for usage information.');
1006
+ process.exit(1);
1007
+ }
1008
+ } catch (error) {
1009
+ console.error('Error:', error.message);
1010
+ if (flags.verbose) {
1011
+ console.error(error.stack);
1012
+ }
1013
+ process.exit(1);
1014
+ }
1015
+ }
1016
+
1017
+ main().catch(err => {
1018
+ console.error('Fatal error:', err);
1019
+ process.exit(1);
1020
+ });