@ruvector/ruvllm 3.0.0 → 4.0.1

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