ruvector 0.1.38 โ 0.1.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-flow/metrics/agent-metrics.json +1 -0
- package/.claude-flow/metrics/performance.json +87 -0
- package/.claude-flow/metrics/task-metrics.json +10 -0
- package/PACKAGE_SUMMARY.md +409 -0
- package/README.md +1679 -508
- package/bin/cli.js +2427 -0
- package/dist/core/agentdb-fast.d.ts +149 -0
- package/dist/core/agentdb-fast.d.ts.map +1 -0
- package/dist/core/agentdb-fast.js +301 -0
- package/dist/core/attention-fallbacks.d.ts +221 -0
- package/dist/core/attention-fallbacks.d.ts.map +1 -0
- package/dist/core/attention-fallbacks.js +361 -0
- package/dist/core/gnn-wrapper.d.ts +143 -0
- package/dist/core/gnn-wrapper.d.ts.map +1 -0
- package/dist/core/gnn-wrapper.js +213 -0
- package/dist/core/index.d.ts +15 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +39 -0
- package/dist/core/sona-wrapper.d.ts +215 -0
- package/dist/core/sona-wrapper.d.ts.map +1 -0
- package/dist/core/sona-wrapper.js +258 -0
- package/dist/index.d.ts +87 -82
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +169 -89
- package/dist/services/embedding-service.d.ts +136 -0
- package/dist/services/embedding-service.d.ts.map +1 -0
- package/dist/services/embedding-service.js +294 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +26 -0
- package/dist/types.d.ts +145 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/examples/api-usage.js +211 -0
- package/examples/cli-demo.sh +85 -0
- package/package.json +41 -93
- package/bin/ruvector.js +0 -1150
- package/dist/index.d.mts +0 -95
- package/dist/index.mjs +0 -5
package/bin/cli.js
ADDED
|
@@ -0,0 +1,2427 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
// Lazy load ruvector (only when needed, not for install/help commands)
|
|
10
|
+
let VectorDB, getVersion, getImplementationType;
|
|
11
|
+
let ruvectorLoaded = false;
|
|
12
|
+
|
|
13
|
+
function loadRuvector() {
|
|
14
|
+
if (ruvectorLoaded) return true;
|
|
15
|
+
try {
|
|
16
|
+
const ruvector = require('../dist/index.js');
|
|
17
|
+
VectorDB = ruvector.VectorDB;
|
|
18
|
+
getVersion = ruvector.getVersion;
|
|
19
|
+
getImplementationType = ruvector.getImplementationType;
|
|
20
|
+
ruvectorLoaded = true;
|
|
21
|
+
return true;
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function requireRuvector() {
|
|
28
|
+
if (!loadRuvector()) {
|
|
29
|
+
console.error(chalk.red('Error: Failed to load ruvector. Please run: npm run build'));
|
|
30
|
+
console.error(chalk.yellow('Or install the package: npm install ruvector'));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Import GNN (optional - graceful fallback if not available)
|
|
36
|
+
let RuvectorLayer, TensorCompress, differentiableSearch, getCompressionLevel, hierarchicalForward;
|
|
37
|
+
let gnnAvailable = false;
|
|
38
|
+
try {
|
|
39
|
+
const gnn = require('@ruvector/gnn');
|
|
40
|
+
RuvectorLayer = gnn.RuvectorLayer;
|
|
41
|
+
TensorCompress = gnn.TensorCompress;
|
|
42
|
+
differentiableSearch = gnn.differentiableSearch;
|
|
43
|
+
getCompressionLevel = gnn.getCompressionLevel;
|
|
44
|
+
hierarchicalForward = gnn.hierarchicalForward;
|
|
45
|
+
gnnAvailable = true;
|
|
46
|
+
} catch (e) {
|
|
47
|
+
// GNN not available - commands will show helpful message
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Import Attention (optional - graceful fallback if not available)
|
|
51
|
+
let DotProductAttention, MultiHeadAttention, HyperbolicAttention, FlashAttention, LinearAttention, MoEAttention;
|
|
52
|
+
let GraphRoPeAttention, EdgeFeaturedAttention, DualSpaceAttention, LocalGlobalAttention;
|
|
53
|
+
let benchmarkAttention, computeAttentionAsync, batchAttentionCompute, parallelAttentionCompute;
|
|
54
|
+
let expMap, logMap, mobiusAddition, poincareDistance, projectToPoincareBall;
|
|
55
|
+
let attentionInfo, attentionVersion;
|
|
56
|
+
let attentionAvailable = false;
|
|
57
|
+
try {
|
|
58
|
+
const attention = require('@ruvector/attention');
|
|
59
|
+
// Core mechanisms
|
|
60
|
+
DotProductAttention = attention.DotProductAttention;
|
|
61
|
+
MultiHeadAttention = attention.MultiHeadAttention;
|
|
62
|
+
HyperbolicAttention = attention.HyperbolicAttention;
|
|
63
|
+
FlashAttention = attention.FlashAttention;
|
|
64
|
+
LinearAttention = attention.LinearAttention;
|
|
65
|
+
MoEAttention = attention.MoEAttention;
|
|
66
|
+
// Graph attention
|
|
67
|
+
GraphRoPeAttention = attention.GraphRoPeAttention;
|
|
68
|
+
EdgeFeaturedAttention = attention.EdgeFeaturedAttention;
|
|
69
|
+
DualSpaceAttention = attention.DualSpaceAttention;
|
|
70
|
+
LocalGlobalAttention = attention.LocalGlobalAttention;
|
|
71
|
+
// Utilities
|
|
72
|
+
benchmarkAttention = attention.benchmarkAttention;
|
|
73
|
+
computeAttentionAsync = attention.computeAttentionAsync;
|
|
74
|
+
batchAttentionCompute = attention.batchAttentionCompute;
|
|
75
|
+
parallelAttentionCompute = attention.parallelAttentionCompute;
|
|
76
|
+
// Hyperbolic math
|
|
77
|
+
expMap = attention.expMap;
|
|
78
|
+
logMap = attention.logMap;
|
|
79
|
+
mobiusAddition = attention.mobiusAddition;
|
|
80
|
+
poincareDistance = attention.poincareDistance;
|
|
81
|
+
projectToPoincareBall = attention.projectToPoincareBall;
|
|
82
|
+
// Meta
|
|
83
|
+
attentionInfo = attention.info;
|
|
84
|
+
attentionVersion = attention.version;
|
|
85
|
+
attentionAvailable = true;
|
|
86
|
+
} catch (e) {
|
|
87
|
+
// Attention not available - commands will show helpful message
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const program = new Command();
|
|
91
|
+
|
|
92
|
+
// Get package version from package.json
|
|
93
|
+
const packageJson = require('../package.json');
|
|
94
|
+
|
|
95
|
+
// Version and description (lazy load implementation info)
|
|
96
|
+
program
|
|
97
|
+
.name('ruvector')
|
|
98
|
+
.description(`${chalk.cyan('ruvector')} - High-performance vector database CLI`)
|
|
99
|
+
.version(packageJson.version);
|
|
100
|
+
|
|
101
|
+
// Create database
|
|
102
|
+
program
|
|
103
|
+
.command('create <path>')
|
|
104
|
+
.description('Create a new vector database')
|
|
105
|
+
.option('-d, --dimension <number>', 'Vector dimension', '384')
|
|
106
|
+
.option('-m, --metric <type>', 'Distance metric (cosine|euclidean|dot)', 'cosine')
|
|
107
|
+
.action((dbPath, options) => {
|
|
108
|
+
requireRuvector();
|
|
109
|
+
const spinner = ora('Creating database...').start();
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const dimension = parseInt(options.dimension);
|
|
113
|
+
const db = new VectorDB({
|
|
114
|
+
dimension,
|
|
115
|
+
metric: options.metric,
|
|
116
|
+
path: dbPath,
|
|
117
|
+
autoPersist: true
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
db.save(dbPath);
|
|
121
|
+
spinner.succeed(chalk.green(`Database created: ${dbPath}`));
|
|
122
|
+
console.log(chalk.gray(` Dimension: ${dimension}`));
|
|
123
|
+
console.log(chalk.gray(` Metric: ${options.metric}`));
|
|
124
|
+
console.log(chalk.gray(` Implementation: ${getImplementationType()}`));
|
|
125
|
+
} catch (error) {
|
|
126
|
+
spinner.fail(chalk.red('Failed to create database'));
|
|
127
|
+
console.error(chalk.red(error.message));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Insert vectors
|
|
133
|
+
program
|
|
134
|
+
.command('insert <database> <file>')
|
|
135
|
+
.description('Insert vectors from JSON file')
|
|
136
|
+
.option('-b, --batch-size <number>', 'Batch size for insertion', '1000')
|
|
137
|
+
.action((dbPath, file, options) => {
|
|
138
|
+
requireRuvector();
|
|
139
|
+
const spinner = ora('Loading database...').start();
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
// Read database metadata to get dimension
|
|
143
|
+
let dimension = 384; // default
|
|
144
|
+
if (fs.existsSync(dbPath)) {
|
|
145
|
+
const dbData = fs.readFileSync(dbPath, 'utf8');
|
|
146
|
+
const parsed = JSON.parse(dbData);
|
|
147
|
+
dimension = parsed.dimension || 384;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const db = new VectorDB({ dimension });
|
|
151
|
+
|
|
152
|
+
if (fs.existsSync(dbPath)) {
|
|
153
|
+
db.load(dbPath);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
spinner.text = 'Reading vectors...';
|
|
157
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
158
|
+
const vectors = Array.isArray(data) ? data : [data];
|
|
159
|
+
|
|
160
|
+
spinner.text = `Inserting ${vectors.length} vectors...`;
|
|
161
|
+
const batchSize = parseInt(options.batchSize);
|
|
162
|
+
|
|
163
|
+
for (let i = 0; i < vectors.length; i += batchSize) {
|
|
164
|
+
const batch = vectors.slice(i, i + batchSize);
|
|
165
|
+
db.insertBatch(batch);
|
|
166
|
+
spinner.text = `Inserted ${Math.min(i + batchSize, vectors.length)}/${vectors.length} vectors...`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
db.save(dbPath);
|
|
170
|
+
spinner.succeed(chalk.green(`Inserted ${vectors.length} vectors`));
|
|
171
|
+
|
|
172
|
+
const stats = db.stats();
|
|
173
|
+
console.log(chalk.gray(` Total vectors: ${stats.count}`));
|
|
174
|
+
} catch (error) {
|
|
175
|
+
spinner.fail(chalk.red('Failed to insert vectors'));
|
|
176
|
+
console.error(chalk.red(error.message));
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Search vectors
|
|
182
|
+
program
|
|
183
|
+
.command('search <database>')
|
|
184
|
+
.description('Search for similar vectors')
|
|
185
|
+
.requiredOption('-v, --vector <json>', 'Query vector as JSON array')
|
|
186
|
+
.option('-k, --top-k <number>', 'Number of results', '10')
|
|
187
|
+
.option('-t, --threshold <number>', 'Similarity threshold', '0.0')
|
|
188
|
+
.option('-f, --filter <json>', 'Metadata filter as JSON')
|
|
189
|
+
.action((dbPath, options) => {
|
|
190
|
+
requireRuvector();
|
|
191
|
+
const spinner = ora('Loading database...').start();
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
// Read database metadata
|
|
195
|
+
const dbData = fs.readFileSync(dbPath, 'utf8');
|
|
196
|
+
const parsed = JSON.parse(dbData);
|
|
197
|
+
const dimension = parsed.dimension || 384;
|
|
198
|
+
|
|
199
|
+
const db = new VectorDB({ dimension });
|
|
200
|
+
db.load(dbPath);
|
|
201
|
+
|
|
202
|
+
spinner.text = 'Searching...';
|
|
203
|
+
|
|
204
|
+
const vector = JSON.parse(options.vector);
|
|
205
|
+
const query = {
|
|
206
|
+
vector,
|
|
207
|
+
k: parseInt(options.topK),
|
|
208
|
+
threshold: parseFloat(options.threshold)
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
if (options.filter) {
|
|
212
|
+
query.filter = JSON.parse(options.filter);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const results = db.search(query);
|
|
216
|
+
spinner.succeed(chalk.green(`Found ${results.length} results`));
|
|
217
|
+
|
|
218
|
+
console.log(chalk.cyan('\nSearch Results:'));
|
|
219
|
+
results.forEach((result, i) => {
|
|
220
|
+
console.log(chalk.white(`\n${i + 1}. ID: ${result.id}`));
|
|
221
|
+
console.log(chalk.yellow(` Score: ${result.score.toFixed(4)}`));
|
|
222
|
+
if (result.metadata) {
|
|
223
|
+
console.log(chalk.gray(` Metadata: ${JSON.stringify(result.metadata)}`));
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
} catch (error) {
|
|
227
|
+
spinner.fail(chalk.red('Failed to search'));
|
|
228
|
+
console.error(chalk.red(error.message));
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Show stats
|
|
234
|
+
program
|
|
235
|
+
.command('stats <database>')
|
|
236
|
+
.description('Show database statistics')
|
|
237
|
+
.action((dbPath) => {
|
|
238
|
+
requireRuvector();
|
|
239
|
+
const spinner = ora('Loading database...').start();
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const dbData = fs.readFileSync(dbPath, 'utf8');
|
|
243
|
+
const parsed = JSON.parse(dbData);
|
|
244
|
+
const dimension = parsed.dimension || 384;
|
|
245
|
+
|
|
246
|
+
const db = new VectorDB({ dimension });
|
|
247
|
+
db.load(dbPath);
|
|
248
|
+
|
|
249
|
+
const stats = db.stats();
|
|
250
|
+
spinner.succeed(chalk.green('Database statistics'));
|
|
251
|
+
|
|
252
|
+
console.log(chalk.cyan('\nDatabase Stats:'));
|
|
253
|
+
console.log(chalk.white(` Vector Count: ${chalk.yellow(stats.count)}`));
|
|
254
|
+
console.log(chalk.white(` Dimension: ${chalk.yellow(stats.dimension)}`));
|
|
255
|
+
console.log(chalk.white(` Metric: ${chalk.yellow(stats.metric)}`));
|
|
256
|
+
console.log(chalk.white(` Implementation: ${chalk.yellow(getImplementationType())}`));
|
|
257
|
+
|
|
258
|
+
if (stats.memoryUsage) {
|
|
259
|
+
const mb = (stats.memoryUsage / (1024 * 1024)).toFixed(2);
|
|
260
|
+
console.log(chalk.white(` Memory Usage: ${chalk.yellow(mb + ' MB')}`));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const fileStats = fs.statSync(dbPath);
|
|
264
|
+
const fileMb = (fileStats.size / (1024 * 1024)).toFixed(2);
|
|
265
|
+
console.log(chalk.white(` File Size: ${chalk.yellow(fileMb + ' MB')}`));
|
|
266
|
+
} catch (error) {
|
|
267
|
+
spinner.fail(chalk.red('Failed to load database'));
|
|
268
|
+
console.error(chalk.red(error.message));
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Benchmark
|
|
274
|
+
program
|
|
275
|
+
.command('benchmark')
|
|
276
|
+
.description('Run performance benchmarks')
|
|
277
|
+
.option('-d, --dimension <number>', 'Vector dimension', '384')
|
|
278
|
+
.option('-n, --num-vectors <number>', 'Number of vectors', '10000')
|
|
279
|
+
.option('-q, --num-queries <number>', 'Number of queries', '1000')
|
|
280
|
+
.action((options) => {
|
|
281
|
+
requireRuvector();
|
|
282
|
+
console.log(chalk.cyan('\nruvector Performance Benchmark'));
|
|
283
|
+
console.log(chalk.gray(`Implementation: ${getImplementationType()}\n`));
|
|
284
|
+
|
|
285
|
+
const dimension = parseInt(options.dimension);
|
|
286
|
+
const numVectors = parseInt(options.numVectors);
|
|
287
|
+
const numQueries = parseInt(options.numQueries);
|
|
288
|
+
|
|
289
|
+
let spinner = ora('Creating database...').start();
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
const db = new VectorDB({ dimension, metric: 'cosine' });
|
|
293
|
+
spinner.succeed();
|
|
294
|
+
|
|
295
|
+
// Insert benchmark
|
|
296
|
+
spinner = ora(`Inserting ${numVectors} vectors...`).start();
|
|
297
|
+
const insertStart = Date.now();
|
|
298
|
+
|
|
299
|
+
const vectors = [];
|
|
300
|
+
for (let i = 0; i < numVectors; i++) {
|
|
301
|
+
vectors.push({
|
|
302
|
+
id: `vec_${i}`,
|
|
303
|
+
vector: Array.from({ length: dimension }, () => Math.random()),
|
|
304
|
+
metadata: { index: i, batch: Math.floor(i / 1000) }
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
db.insertBatch(vectors);
|
|
309
|
+
const insertTime = Date.now() - insertStart;
|
|
310
|
+
const insertRate = (numVectors / (insertTime / 1000)).toFixed(0);
|
|
311
|
+
|
|
312
|
+
spinner.succeed(chalk.green(`Inserted ${numVectors} vectors in ${insertTime}ms`));
|
|
313
|
+
console.log(chalk.gray(` Rate: ${chalk.yellow(insertRate)} vectors/sec`));
|
|
314
|
+
|
|
315
|
+
// Search benchmark
|
|
316
|
+
spinner = ora(`Running ${numQueries} searches...`).start();
|
|
317
|
+
const searchStart = Date.now();
|
|
318
|
+
|
|
319
|
+
for (let i = 0; i < numQueries; i++) {
|
|
320
|
+
const query = {
|
|
321
|
+
vector: Array.from({ length: dimension }, () => Math.random()),
|
|
322
|
+
k: 10
|
|
323
|
+
};
|
|
324
|
+
db.search(query);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const searchTime = Date.now() - searchStart;
|
|
328
|
+
const searchRate = (numQueries / (searchTime / 1000)).toFixed(0);
|
|
329
|
+
const avgLatency = (searchTime / numQueries).toFixed(2);
|
|
330
|
+
|
|
331
|
+
spinner.succeed(chalk.green(`Completed ${numQueries} searches in ${searchTime}ms`));
|
|
332
|
+
console.log(chalk.gray(` Rate: ${chalk.yellow(searchRate)} queries/sec`));
|
|
333
|
+
console.log(chalk.gray(` Avg Latency: ${chalk.yellow(avgLatency)}ms`));
|
|
334
|
+
|
|
335
|
+
// Stats
|
|
336
|
+
const stats = db.stats();
|
|
337
|
+
console.log(chalk.cyan('\nFinal Stats:'));
|
|
338
|
+
console.log(chalk.white(` Vector Count: ${chalk.yellow(stats.count)}`));
|
|
339
|
+
console.log(chalk.white(` Dimension: ${chalk.yellow(stats.dimension)}`));
|
|
340
|
+
console.log(chalk.white(` Implementation: ${chalk.yellow(getImplementationType())}`));
|
|
341
|
+
|
|
342
|
+
} catch (error) {
|
|
343
|
+
spinner.fail(chalk.red('Benchmark failed'));
|
|
344
|
+
console.error(chalk.red(error.message));
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Info command
|
|
350
|
+
program
|
|
351
|
+
.command('info')
|
|
352
|
+
.description('Show ruvector information')
|
|
353
|
+
.action(() => {
|
|
354
|
+
console.log(chalk.cyan('\nruvector Information'));
|
|
355
|
+
console.log(chalk.white(` CLI Version: ${chalk.yellow(packageJson.version)}`));
|
|
356
|
+
|
|
357
|
+
// Try to load ruvector for implementation info
|
|
358
|
+
if (loadRuvector()) {
|
|
359
|
+
const version = typeof getVersion === 'function' ? getVersion() : 'unknown';
|
|
360
|
+
const impl = typeof getImplementationType === 'function' ? getImplementationType() : 'native';
|
|
361
|
+
console.log(chalk.white(` Core Version: ${chalk.yellow(version)}`));
|
|
362
|
+
console.log(chalk.white(` Implementation: ${chalk.yellow(impl)}`));
|
|
363
|
+
} else {
|
|
364
|
+
console.log(chalk.white(` Core: ${chalk.gray('Not loaded (install @ruvector/core)')}`));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
console.log(chalk.white(` GNN Module: ${gnnAvailable ? chalk.green('Available') : chalk.gray('Not installed')}`));
|
|
368
|
+
console.log(chalk.white(` Node Version: ${chalk.yellow(process.version)}`));
|
|
369
|
+
console.log(chalk.white(` Platform: ${chalk.yellow(process.platform)}`));
|
|
370
|
+
console.log(chalk.white(` Architecture: ${chalk.yellow(process.arch)}`));
|
|
371
|
+
|
|
372
|
+
if (!gnnAvailable) {
|
|
373
|
+
console.log(chalk.gray('\n Install GNN with: npx ruvector install gnn'));
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// =============================================================================
|
|
378
|
+
// Install Command
|
|
379
|
+
// =============================================================================
|
|
380
|
+
|
|
381
|
+
program
|
|
382
|
+
.command('install [packages...]')
|
|
383
|
+
.description('Install optional ruvector packages')
|
|
384
|
+
.option('-a, --all', 'Install all optional packages')
|
|
385
|
+
.option('-l, --list', 'List available packages')
|
|
386
|
+
.option('-i, --interactive', 'Interactive package selection')
|
|
387
|
+
.action(async (packages, options) => {
|
|
388
|
+
const { execSync } = require('child_process');
|
|
389
|
+
|
|
390
|
+
// Available optional packages - all ruvector npm packages
|
|
391
|
+
const availablePackages = {
|
|
392
|
+
// Core packages
|
|
393
|
+
core: {
|
|
394
|
+
name: '@ruvector/core',
|
|
395
|
+
description: 'Core vector database with native Rust bindings (HNSW, SIMD)',
|
|
396
|
+
installed: true, // Always installed with ruvector
|
|
397
|
+
category: 'core'
|
|
398
|
+
},
|
|
399
|
+
gnn: {
|
|
400
|
+
name: '@ruvector/gnn',
|
|
401
|
+
description: 'Graph Neural Network layers, tensor compression, differentiable search',
|
|
402
|
+
installed: gnnAvailable,
|
|
403
|
+
category: 'core'
|
|
404
|
+
},
|
|
405
|
+
'graph-node': {
|
|
406
|
+
name: '@ruvector/graph-node',
|
|
407
|
+
description: 'Native Node.js bindings for hypergraph database with Cypher queries',
|
|
408
|
+
installed: false,
|
|
409
|
+
category: 'core'
|
|
410
|
+
},
|
|
411
|
+
'agentic-synth': {
|
|
412
|
+
name: '@ruvector/agentic-synth',
|
|
413
|
+
description: 'Synthetic data generator for AI/ML training, RAG, and agentic workflows',
|
|
414
|
+
installed: false,
|
|
415
|
+
category: 'tools'
|
|
416
|
+
},
|
|
417
|
+
extensions: {
|
|
418
|
+
name: 'ruvector-extensions',
|
|
419
|
+
description: 'Advanced features: embeddings, UI, exports, temporal tracking, persistence',
|
|
420
|
+
installed: false,
|
|
421
|
+
category: 'tools'
|
|
422
|
+
},
|
|
423
|
+
// Platform-specific native bindings for @ruvector/core
|
|
424
|
+
'node-linux-x64': {
|
|
425
|
+
name: '@ruvector/node-linux-x64-gnu',
|
|
426
|
+
description: 'Linux x64 native bindings for @ruvector/core',
|
|
427
|
+
installed: false,
|
|
428
|
+
category: 'platform'
|
|
429
|
+
},
|
|
430
|
+
'node-linux-arm64': {
|
|
431
|
+
name: '@ruvector/node-linux-arm64-gnu',
|
|
432
|
+
description: 'Linux ARM64 native bindings for @ruvector/core',
|
|
433
|
+
installed: false,
|
|
434
|
+
category: 'platform'
|
|
435
|
+
},
|
|
436
|
+
'node-darwin-x64': {
|
|
437
|
+
name: '@ruvector/node-darwin-x64',
|
|
438
|
+
description: 'macOS Intel x64 native bindings for @ruvector/core',
|
|
439
|
+
installed: false,
|
|
440
|
+
category: 'platform'
|
|
441
|
+
},
|
|
442
|
+
'node-darwin-arm64': {
|
|
443
|
+
name: '@ruvector/node-darwin-arm64',
|
|
444
|
+
description: 'macOS Apple Silicon native bindings for @ruvector/core',
|
|
445
|
+
installed: false,
|
|
446
|
+
category: 'platform'
|
|
447
|
+
},
|
|
448
|
+
'node-win32-x64': {
|
|
449
|
+
name: '@ruvector/node-win32-x64-msvc',
|
|
450
|
+
description: 'Windows x64 native bindings for @ruvector/core',
|
|
451
|
+
installed: false,
|
|
452
|
+
category: 'platform'
|
|
453
|
+
},
|
|
454
|
+
// Platform-specific native bindings for @ruvector/gnn
|
|
455
|
+
'gnn-linux-x64': {
|
|
456
|
+
name: '@ruvector/gnn-linux-x64-gnu',
|
|
457
|
+
description: 'Linux x64 native bindings for @ruvector/gnn',
|
|
458
|
+
installed: false,
|
|
459
|
+
category: 'platform'
|
|
460
|
+
},
|
|
461
|
+
'gnn-linux-arm64': {
|
|
462
|
+
name: '@ruvector/gnn-linux-arm64-gnu',
|
|
463
|
+
description: 'Linux ARM64 native bindings for @ruvector/gnn',
|
|
464
|
+
installed: false,
|
|
465
|
+
category: 'platform'
|
|
466
|
+
},
|
|
467
|
+
'gnn-darwin-x64': {
|
|
468
|
+
name: '@ruvector/gnn-darwin-x64',
|
|
469
|
+
description: 'macOS Intel x64 native bindings for @ruvector/gnn',
|
|
470
|
+
installed: false,
|
|
471
|
+
category: 'platform'
|
|
472
|
+
},
|
|
473
|
+
'gnn-darwin-arm64': {
|
|
474
|
+
name: '@ruvector/gnn-darwin-arm64',
|
|
475
|
+
description: 'macOS Apple Silicon native bindings for @ruvector/gnn',
|
|
476
|
+
installed: false,
|
|
477
|
+
category: 'platform'
|
|
478
|
+
},
|
|
479
|
+
'gnn-win32-x64': {
|
|
480
|
+
name: '@ruvector/gnn-win32-x64-msvc',
|
|
481
|
+
description: 'Windows x64 native bindings for @ruvector/gnn',
|
|
482
|
+
installed: false,
|
|
483
|
+
category: 'platform'
|
|
484
|
+
},
|
|
485
|
+
// Legacy/standalone packages
|
|
486
|
+
'ruvector-core': {
|
|
487
|
+
name: 'ruvector-core',
|
|
488
|
+
description: 'Standalone vector database (legacy, use @ruvector/core instead)',
|
|
489
|
+
installed: false,
|
|
490
|
+
category: 'legacy'
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
// Check which packages are actually installed
|
|
495
|
+
for (const [key, pkg] of Object.entries(availablePackages)) {
|
|
496
|
+
if (key !== 'core' && key !== 'gnn') {
|
|
497
|
+
try {
|
|
498
|
+
require.resolve(pkg.name);
|
|
499
|
+
pkg.installed = true;
|
|
500
|
+
} catch (e) {
|
|
501
|
+
pkg.installed = false;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// List packages
|
|
507
|
+
if (options.list || (packages.length === 0 && !options.all && !options.interactive)) {
|
|
508
|
+
console.log(chalk.cyan('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
509
|
+
console.log(chalk.cyan(' Ruvector Packages'));
|
|
510
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
511
|
+
|
|
512
|
+
const categories = {
|
|
513
|
+
core: { title: '๐ฆ Core Packages', packages: [] },
|
|
514
|
+
tools: { title: '๐ง Tools & Extensions', packages: [] },
|
|
515
|
+
platform: { title: '๐ฅ๏ธ Platform Bindings', packages: [] },
|
|
516
|
+
legacy: { title: '๐ Legacy Packages', packages: [] }
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
// Group by category
|
|
520
|
+
Object.entries(availablePackages).forEach(([key, pkg]) => {
|
|
521
|
+
if (categories[pkg.category]) {
|
|
522
|
+
categories[pkg.category].packages.push({ key, ...pkg });
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
// Display by category
|
|
527
|
+
for (const [catKey, cat] of Object.entries(categories)) {
|
|
528
|
+
if (cat.packages.length === 0) continue;
|
|
529
|
+
|
|
530
|
+
console.log(chalk.cyan(`${cat.title}`));
|
|
531
|
+
console.log(chalk.gray('โ'.repeat(60)));
|
|
532
|
+
|
|
533
|
+
cat.packages.forEach(pkg => {
|
|
534
|
+
const status = pkg.installed ? chalk.green('โ') : chalk.gray('โ');
|
|
535
|
+
const statusText = pkg.installed ? chalk.green('installed') : chalk.gray('available');
|
|
536
|
+
console.log(chalk.white(` ${status} ${chalk.yellow(pkg.key.padEnd(18))} ${statusText}`));
|
|
537
|
+
console.log(chalk.gray(` ${pkg.description}`));
|
|
538
|
+
console.log(chalk.gray(` npm: ${chalk.white(pkg.name)}\n`));
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
543
|
+
console.log(chalk.cyan('Usage:'));
|
|
544
|
+
console.log(chalk.white(' npx ruvector install gnn # Install GNN package'));
|
|
545
|
+
console.log(chalk.white(' npx ruvector install graph-node # Install graph database'));
|
|
546
|
+
console.log(chalk.white(' npx ruvector install agentic-synth # Install data generator'));
|
|
547
|
+
console.log(chalk.white(' npx ruvector install --all # Install all core packages'));
|
|
548
|
+
console.log(chalk.white(' npx ruvector install -i # Interactive selection'));
|
|
549
|
+
console.log(chalk.gray('\n Note: Platform bindings are auto-detected by @ruvector/core'));
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Interactive mode
|
|
554
|
+
if (options.interactive) {
|
|
555
|
+
const readline = require('readline');
|
|
556
|
+
const rl = readline.createInterface({
|
|
557
|
+
input: process.stdin,
|
|
558
|
+
output: process.stdout
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
console.log(chalk.cyan('\nSelect packages to install:\n'));
|
|
562
|
+
|
|
563
|
+
const notInstalled = Object.entries(availablePackages)
|
|
564
|
+
.filter(([_, pkg]) => !pkg.installed);
|
|
565
|
+
|
|
566
|
+
if (notInstalled.length === 0) {
|
|
567
|
+
console.log(chalk.green('All packages are already installed!'));
|
|
568
|
+
rl.close();
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
notInstalled.forEach(([key, pkg], i) => {
|
|
573
|
+
console.log(chalk.white(` ${i + 1}. ${chalk.yellow(key)} - ${pkg.description}`));
|
|
574
|
+
});
|
|
575
|
+
console.log(chalk.white(` ${notInstalled.length + 1}. ${chalk.yellow('all')} - Install all packages`));
|
|
576
|
+
console.log(chalk.white(` 0. ${chalk.gray('cancel')} - Exit without installing`));
|
|
577
|
+
|
|
578
|
+
rl.question(chalk.cyan('\nEnter selection (comma-separated for multiple): '), (answer) => {
|
|
579
|
+
rl.close();
|
|
580
|
+
|
|
581
|
+
const selections = answer.split(',').map(s => s.trim());
|
|
582
|
+
let toInstall = [];
|
|
583
|
+
|
|
584
|
+
for (const sel of selections) {
|
|
585
|
+
if (sel === '0' || sel.toLowerCase() === 'cancel') {
|
|
586
|
+
console.log(chalk.yellow('Installation cancelled.'));
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
if (sel === String(notInstalled.length + 1) || sel.toLowerCase() === 'all') {
|
|
590
|
+
toInstall = notInstalled.map(([_, pkg]) => pkg.name);
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
const idx = parseInt(sel) - 1;
|
|
594
|
+
if (idx >= 0 && idx < notInstalled.length) {
|
|
595
|
+
toInstall.push(notInstalled[idx][1].name);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (toInstall.length === 0) {
|
|
600
|
+
console.log(chalk.yellow('No valid packages selected.'));
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
installPackages(toInstall);
|
|
605
|
+
});
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Install all (core + tools only, not platform-specific or legacy)
|
|
610
|
+
if (options.all) {
|
|
611
|
+
const toInstall = Object.values(availablePackages)
|
|
612
|
+
.filter(pkg => !pkg.installed && (pkg.category === 'core' || pkg.category === 'tools'))
|
|
613
|
+
.map(pkg => pkg.name);
|
|
614
|
+
|
|
615
|
+
if (toInstall.length === 0) {
|
|
616
|
+
console.log(chalk.green('All core packages are already installed!'));
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
console.log(chalk.cyan(`Installing ${toInstall.length} packages...`));
|
|
621
|
+
installPackages(toInstall);
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// Install specific packages
|
|
626
|
+
const toInstall = [];
|
|
627
|
+
for (const pkg of packages) {
|
|
628
|
+
const key = pkg.toLowerCase().replace('@ruvector/', '');
|
|
629
|
+
if (availablePackages[key]) {
|
|
630
|
+
if (availablePackages[key].installed) {
|
|
631
|
+
console.log(chalk.yellow(`${availablePackages[key].name} is already installed`));
|
|
632
|
+
} else {
|
|
633
|
+
toInstall.push(availablePackages[key].name);
|
|
634
|
+
}
|
|
635
|
+
} else {
|
|
636
|
+
console.log(chalk.red(`Unknown package: ${pkg}`));
|
|
637
|
+
console.log(chalk.gray(`Available: ${Object.keys(availablePackages).join(', ')}`));
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (toInstall.length > 0) {
|
|
642
|
+
installPackages(toInstall);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function installPackages(pkgs) {
|
|
646
|
+
const spinner = ora(`Installing ${pkgs.join(', ')}...`).start();
|
|
647
|
+
|
|
648
|
+
try {
|
|
649
|
+
// Detect package manager
|
|
650
|
+
let pm = 'npm';
|
|
651
|
+
if (fs.existsSync('yarn.lock')) pm = 'yarn';
|
|
652
|
+
else if (fs.existsSync('pnpm-lock.yaml')) pm = 'pnpm';
|
|
653
|
+
else if (fs.existsSync('bun.lockb')) pm = 'bun';
|
|
654
|
+
|
|
655
|
+
const cmd = pm === 'yarn' ? `yarn add ${pkgs.join(' ')}`
|
|
656
|
+
: pm === 'pnpm' ? `pnpm add ${pkgs.join(' ')}`
|
|
657
|
+
: pm === 'bun' ? `bun add ${pkgs.join(' ')}`
|
|
658
|
+
: `npm install ${pkgs.join(' ')}`;
|
|
659
|
+
|
|
660
|
+
execSync(cmd, { stdio: 'pipe' });
|
|
661
|
+
|
|
662
|
+
spinner.succeed(chalk.green(`Installed: ${pkgs.join(', ')}`));
|
|
663
|
+
console.log(chalk.cyan('\nRun "npx ruvector info" to verify installation.'));
|
|
664
|
+
} catch (error) {
|
|
665
|
+
spinner.fail(chalk.red('Installation failed'));
|
|
666
|
+
console.error(chalk.red(error.message));
|
|
667
|
+
console.log(chalk.yellow(`\nTry manually: npm install ${pkgs.join(' ')}`));
|
|
668
|
+
process.exit(1);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
// =============================================================================
|
|
674
|
+
// GNN Commands
|
|
675
|
+
// =============================================================================
|
|
676
|
+
|
|
677
|
+
// Helper to check GNN availability
|
|
678
|
+
function requireGnn() {
|
|
679
|
+
if (!gnnAvailable) {
|
|
680
|
+
console.error(chalk.red('Error: GNN module not available.'));
|
|
681
|
+
console.error(chalk.yellow('Install it with: npm install @ruvector/gnn'));
|
|
682
|
+
process.exit(1);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// GNN parent command
|
|
687
|
+
const gnnCmd = program
|
|
688
|
+
.command('gnn')
|
|
689
|
+
.description('Graph Neural Network operations');
|
|
690
|
+
|
|
691
|
+
// GNN Layer command
|
|
692
|
+
gnnCmd
|
|
693
|
+
.command('layer')
|
|
694
|
+
.description('Create and test a GNN layer')
|
|
695
|
+
.requiredOption('-i, --input-dim <number>', 'Input dimension')
|
|
696
|
+
.requiredOption('-h, --hidden-dim <number>', 'Hidden dimension')
|
|
697
|
+
.option('-a, --heads <number>', 'Number of attention heads', '4')
|
|
698
|
+
.option('-d, --dropout <number>', 'Dropout rate', '0.1')
|
|
699
|
+
.option('--test', 'Run a test forward pass')
|
|
700
|
+
.option('-o, --output <file>', 'Save layer config to JSON file')
|
|
701
|
+
.action((options) => {
|
|
702
|
+
requireGnn();
|
|
703
|
+
const spinner = ora('Creating GNN layer...').start();
|
|
704
|
+
|
|
705
|
+
try {
|
|
706
|
+
const inputDim = parseInt(options.inputDim);
|
|
707
|
+
const hiddenDim = parseInt(options.hiddenDim);
|
|
708
|
+
const heads = parseInt(options.heads);
|
|
709
|
+
const dropout = parseFloat(options.dropout);
|
|
710
|
+
|
|
711
|
+
const layer = new RuvectorLayer(inputDim, hiddenDim, heads, dropout);
|
|
712
|
+
spinner.succeed(chalk.green('GNN Layer created'));
|
|
713
|
+
|
|
714
|
+
console.log(chalk.cyan('\nLayer Configuration:'));
|
|
715
|
+
console.log(chalk.white(` Input Dim: ${chalk.yellow(inputDim)}`));
|
|
716
|
+
console.log(chalk.white(` Hidden Dim: ${chalk.yellow(hiddenDim)}`));
|
|
717
|
+
console.log(chalk.white(` Heads: ${chalk.yellow(heads)}`));
|
|
718
|
+
console.log(chalk.white(` Dropout: ${chalk.yellow(dropout)}`));
|
|
719
|
+
|
|
720
|
+
if (options.test) {
|
|
721
|
+
spinner.start('Running test forward pass...');
|
|
722
|
+
|
|
723
|
+
// Create test data
|
|
724
|
+
const nodeEmbedding = Array.from({ length: inputDim }, () => Math.random());
|
|
725
|
+
const neighborEmbeddings = [
|
|
726
|
+
Array.from({ length: inputDim }, () => Math.random()),
|
|
727
|
+
Array.from({ length: inputDim }, () => Math.random())
|
|
728
|
+
];
|
|
729
|
+
const edgeWeights = [0.6, 0.4];
|
|
730
|
+
|
|
731
|
+
const output = layer.forward(nodeEmbedding, neighborEmbeddings, edgeWeights);
|
|
732
|
+
spinner.succeed(chalk.green('Forward pass completed'));
|
|
733
|
+
|
|
734
|
+
console.log(chalk.cyan('\nTest Results:'));
|
|
735
|
+
console.log(chalk.white(` Input shape: ${chalk.yellow(`[${inputDim}]`)}`));
|
|
736
|
+
console.log(chalk.white(` Output shape: ${chalk.yellow(`[${output.length}]`)}`));
|
|
737
|
+
console.log(chalk.white(` Output sample: ${chalk.gray(`[${output.slice(0, 4).map(v => v.toFixed(4)).join(', ')}...]`)}`));
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if (options.output) {
|
|
741
|
+
const config = layer.toJson();
|
|
742
|
+
fs.writeFileSync(options.output, config);
|
|
743
|
+
console.log(chalk.green(`\nLayer config saved to: ${options.output}`));
|
|
744
|
+
}
|
|
745
|
+
} catch (error) {
|
|
746
|
+
spinner.fail(chalk.red('Failed to create GNN layer'));
|
|
747
|
+
console.error(chalk.red(error.message));
|
|
748
|
+
process.exit(1);
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
// GNN Compress command
|
|
753
|
+
gnnCmd
|
|
754
|
+
.command('compress')
|
|
755
|
+
.description('Compress embeddings using adaptive tensor compression')
|
|
756
|
+
.requiredOption('-f, --file <path>', 'Input JSON file with embeddings')
|
|
757
|
+
.option('-l, --level <type>', 'Compression level (none|half|pq8|pq4|binary)', 'auto')
|
|
758
|
+
.option('-a, --access-freq <number>', 'Access frequency for auto compression (0.0-1.0)', '0.5')
|
|
759
|
+
.option('-o, --output <file>', 'Output file for compressed data')
|
|
760
|
+
.action((options) => {
|
|
761
|
+
requireGnn();
|
|
762
|
+
const spinner = ora('Loading embeddings...').start();
|
|
763
|
+
|
|
764
|
+
try {
|
|
765
|
+
const data = JSON.parse(fs.readFileSync(options.file, 'utf8'));
|
|
766
|
+
const embeddings = Array.isArray(data) ? data : [data];
|
|
767
|
+
|
|
768
|
+
spinner.text = 'Compressing embeddings...';
|
|
769
|
+
const compressor = new TensorCompress();
|
|
770
|
+
const accessFreq = parseFloat(options.accessFreq);
|
|
771
|
+
|
|
772
|
+
const results = [];
|
|
773
|
+
let totalOriginalSize = 0;
|
|
774
|
+
let totalCompressedSize = 0;
|
|
775
|
+
|
|
776
|
+
for (const embedding of embeddings) {
|
|
777
|
+
const vec = embedding.vector || embedding;
|
|
778
|
+
totalOriginalSize += vec.length * 4; // float32 = 4 bytes
|
|
779
|
+
|
|
780
|
+
let compressed;
|
|
781
|
+
if (options.level === 'auto') {
|
|
782
|
+
compressed = compressor.compress(vec, accessFreq);
|
|
783
|
+
} else {
|
|
784
|
+
const levelConfig = { levelType: options.level };
|
|
785
|
+
if (options.level === 'pq8') {
|
|
786
|
+
levelConfig.subvectors = 8;
|
|
787
|
+
levelConfig.centroids = 256;
|
|
788
|
+
} else if (options.level === 'pq4') {
|
|
789
|
+
levelConfig.subvectors = 8;
|
|
790
|
+
}
|
|
791
|
+
compressed = compressor.compressWithLevel(vec, levelConfig);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
totalCompressedSize += compressed.length;
|
|
795
|
+
results.push({
|
|
796
|
+
id: embedding.id,
|
|
797
|
+
compressed
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const ratio = (totalOriginalSize / totalCompressedSize).toFixed(2);
|
|
802
|
+
const savings = ((1 - totalCompressedSize / totalOriginalSize) * 100).toFixed(1);
|
|
803
|
+
|
|
804
|
+
spinner.succeed(chalk.green(`Compressed ${embeddings.length} embeddings`));
|
|
805
|
+
|
|
806
|
+
console.log(chalk.cyan('\nCompression Results:'));
|
|
807
|
+
console.log(chalk.white(` Embeddings: ${chalk.yellow(embeddings.length)}`));
|
|
808
|
+
console.log(chalk.white(` Level: ${chalk.yellow(options.level === 'auto' ? `auto (${getCompressionLevel(accessFreq)})` : options.level)}`));
|
|
809
|
+
console.log(chalk.white(` Original: ${chalk.yellow((totalOriginalSize / 1024).toFixed(2) + ' KB')}`));
|
|
810
|
+
console.log(chalk.white(` Compressed: ${chalk.yellow((totalCompressedSize / 1024).toFixed(2) + ' KB')}`));
|
|
811
|
+
console.log(chalk.white(` Ratio: ${chalk.yellow(ratio + 'x')}`));
|
|
812
|
+
console.log(chalk.white(` Savings: ${chalk.yellow(savings + '%')}`));
|
|
813
|
+
|
|
814
|
+
if (options.output) {
|
|
815
|
+
fs.writeFileSync(options.output, JSON.stringify(results, null, 2));
|
|
816
|
+
console.log(chalk.green(`\nCompressed data saved to: ${options.output}`));
|
|
817
|
+
}
|
|
818
|
+
} catch (error) {
|
|
819
|
+
spinner.fail(chalk.red('Failed to compress embeddings'));
|
|
820
|
+
console.error(chalk.red(error.message));
|
|
821
|
+
process.exit(1);
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
// GNN Search command
|
|
826
|
+
gnnCmd
|
|
827
|
+
.command('search')
|
|
828
|
+
.description('Differentiable search with soft attention')
|
|
829
|
+
.requiredOption('-q, --query <json>', 'Query vector as JSON array')
|
|
830
|
+
.requiredOption('-c, --candidates <file>', 'Candidates file (JSON array of vectors)')
|
|
831
|
+
.option('-k, --top-k <number>', 'Number of results', '5')
|
|
832
|
+
.option('-t, --temperature <number>', 'Softmax temperature (lower=sharper)', '1.0')
|
|
833
|
+
.action((options) => {
|
|
834
|
+
requireGnn();
|
|
835
|
+
const spinner = ora('Loading candidates...').start();
|
|
836
|
+
|
|
837
|
+
try {
|
|
838
|
+
const query = JSON.parse(options.query);
|
|
839
|
+
const candidatesData = JSON.parse(fs.readFileSync(options.candidates, 'utf8'));
|
|
840
|
+
const candidates = candidatesData.map(c => c.vector || c);
|
|
841
|
+
const k = parseInt(options.topK);
|
|
842
|
+
const temperature = parseFloat(options.temperature);
|
|
843
|
+
|
|
844
|
+
spinner.text = 'Running differentiable search...';
|
|
845
|
+
const result = differentiableSearch(query, candidates, k, temperature);
|
|
846
|
+
|
|
847
|
+
spinner.succeed(chalk.green(`Found top-${k} results`));
|
|
848
|
+
|
|
849
|
+
console.log(chalk.cyan('\nSearch Results:'));
|
|
850
|
+
console.log(chalk.white(` Query dim: ${chalk.yellow(query.length)}`));
|
|
851
|
+
console.log(chalk.white(` Candidates: ${chalk.yellow(candidates.length)}`));
|
|
852
|
+
console.log(chalk.white(` Temperature: ${chalk.yellow(temperature)}`));
|
|
853
|
+
|
|
854
|
+
console.log(chalk.cyan('\nTop-K Results:'));
|
|
855
|
+
for (let i = 0; i < result.indices.length; i++) {
|
|
856
|
+
const idx = result.indices[i];
|
|
857
|
+
const weight = result.weights[i];
|
|
858
|
+
const id = candidatesData[idx]?.id || `candidate_${idx}`;
|
|
859
|
+
console.log(chalk.white(` ${i + 1}. ${chalk.yellow(id)} (index: ${idx})`));
|
|
860
|
+
console.log(chalk.gray(` Weight: ${weight.toFixed(6)}`));
|
|
861
|
+
}
|
|
862
|
+
} catch (error) {
|
|
863
|
+
spinner.fail(chalk.red('Failed to run search'));
|
|
864
|
+
console.error(chalk.red(error.message));
|
|
865
|
+
process.exit(1);
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
// GNN Info command
|
|
870
|
+
gnnCmd
|
|
871
|
+
.command('info')
|
|
872
|
+
.description('Show GNN module information')
|
|
873
|
+
.action(() => {
|
|
874
|
+
if (!gnnAvailable) {
|
|
875
|
+
console.log(chalk.yellow('\nGNN Module: Not installed'));
|
|
876
|
+
console.log(chalk.white('Install with: npm install @ruvector/gnn'));
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
console.log(chalk.cyan('\nGNN Module Information'));
|
|
881
|
+
console.log(chalk.white(` Status: ${chalk.green('Available')}`));
|
|
882
|
+
console.log(chalk.white(` Platform: ${chalk.yellow(process.platform)}`));
|
|
883
|
+
console.log(chalk.white(` Architecture: ${chalk.yellow(process.arch)}`));
|
|
884
|
+
|
|
885
|
+
console.log(chalk.cyan('\nAvailable Features:'));
|
|
886
|
+
console.log(chalk.white(` โข RuvectorLayer - GNN layer with multi-head attention`));
|
|
887
|
+
console.log(chalk.white(` โข TensorCompress - Adaptive tensor compression (5 levels)`));
|
|
888
|
+
console.log(chalk.white(` โข differentiableSearch - Soft attention-based search`));
|
|
889
|
+
console.log(chalk.white(` โข hierarchicalForward - Multi-layer GNN processing`));
|
|
890
|
+
|
|
891
|
+
console.log(chalk.cyan('\nCompression Levels:'));
|
|
892
|
+
console.log(chalk.gray(` none (freq > 0.8) - Full precision, hot data`));
|
|
893
|
+
console.log(chalk.gray(` half (freq > 0.4) - ~50% savings, warm data`));
|
|
894
|
+
console.log(chalk.gray(` pq8 (freq > 0.1) - ~8x compression, cool data`));
|
|
895
|
+
console.log(chalk.gray(` pq4 (freq > 0.01) - ~16x compression, cold data`));
|
|
896
|
+
console.log(chalk.gray(` binary (freq <= 0.01) - ~32x compression, archive`));
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
// =============================================================================
|
|
900
|
+
// Attention Commands
|
|
901
|
+
// =============================================================================
|
|
902
|
+
|
|
903
|
+
// Helper to require attention module
|
|
904
|
+
function requireAttention() {
|
|
905
|
+
if (!attentionAvailable) {
|
|
906
|
+
console.error(chalk.red('Error: @ruvector/attention is not installed'));
|
|
907
|
+
console.error(chalk.yellow('Install it with: npm install @ruvector/attention'));
|
|
908
|
+
process.exit(1);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Attention parent command
|
|
913
|
+
const attentionCmd = program
|
|
914
|
+
.command('attention')
|
|
915
|
+
.description('High-performance attention mechanism operations');
|
|
916
|
+
|
|
917
|
+
// Attention compute command - run attention on input vectors
|
|
918
|
+
attentionCmd
|
|
919
|
+
.command('compute')
|
|
920
|
+
.description('Compute attention over input vectors')
|
|
921
|
+
.requiredOption('-q, --query <json>', 'Query vector as JSON array')
|
|
922
|
+
.requiredOption('-k, --keys <file>', 'Keys file (JSON array of vectors)')
|
|
923
|
+
.option('-v, --values <file>', 'Values file (JSON array of vectors, defaults to keys)')
|
|
924
|
+
.option('-t, --type <type>', 'Attention type (dot|multi-head|flash|hyperbolic|linear)', 'dot')
|
|
925
|
+
.option('-h, --heads <number>', 'Number of attention heads (for multi-head)', '4')
|
|
926
|
+
.option('-d, --head-dim <number>', 'Head dimension (for multi-head)', '64')
|
|
927
|
+
.option('--curvature <number>', 'Curvature for hyperbolic attention', '1.0')
|
|
928
|
+
.option('-o, --output <file>', 'Output file for results')
|
|
929
|
+
.action((options) => {
|
|
930
|
+
requireAttention();
|
|
931
|
+
const spinner = ora('Loading keys...').start();
|
|
932
|
+
|
|
933
|
+
try {
|
|
934
|
+
const query = JSON.parse(options.query);
|
|
935
|
+
const keysData = JSON.parse(fs.readFileSync(options.keys, 'utf8'));
|
|
936
|
+
const keys = keysData.map(k => k.vector || k);
|
|
937
|
+
|
|
938
|
+
let values = keys;
|
|
939
|
+
if (options.values) {
|
|
940
|
+
const valuesData = JSON.parse(fs.readFileSync(options.values, 'utf8'));
|
|
941
|
+
values = valuesData.map(v => v.vector || v);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
spinner.text = `Computing ${options.type} attention...`;
|
|
945
|
+
|
|
946
|
+
let result;
|
|
947
|
+
let attentionWeights;
|
|
948
|
+
|
|
949
|
+
switch (options.type) {
|
|
950
|
+
case 'dot': {
|
|
951
|
+
const attn = new DotProductAttention();
|
|
952
|
+
const queryMat = [query];
|
|
953
|
+
const output = attn.forward(queryMat, keys, values);
|
|
954
|
+
result = output[0];
|
|
955
|
+
attentionWeights = attn.getLastWeights ? attn.getLastWeights()[0] : null;
|
|
956
|
+
break;
|
|
957
|
+
}
|
|
958
|
+
case 'multi-head': {
|
|
959
|
+
const numHeads = parseInt(options.heads);
|
|
960
|
+
const headDim = parseInt(options.headDim);
|
|
961
|
+
const attn = new MultiHeadAttention(query.length, numHeads, headDim);
|
|
962
|
+
const queryMat = [query];
|
|
963
|
+
const output = attn.forward(queryMat, keys, values);
|
|
964
|
+
result = output[0];
|
|
965
|
+
break;
|
|
966
|
+
}
|
|
967
|
+
case 'flash': {
|
|
968
|
+
const attn = new FlashAttention(query.length);
|
|
969
|
+
const queryMat = [query];
|
|
970
|
+
const output = attn.forward(queryMat, keys, values);
|
|
971
|
+
result = output[0];
|
|
972
|
+
break;
|
|
973
|
+
}
|
|
974
|
+
case 'hyperbolic': {
|
|
975
|
+
const curvature = parseFloat(options.curvature);
|
|
976
|
+
const attn = new HyperbolicAttention(query.length, curvature);
|
|
977
|
+
const queryMat = [query];
|
|
978
|
+
const output = attn.forward(queryMat, keys, values);
|
|
979
|
+
result = output[0];
|
|
980
|
+
break;
|
|
981
|
+
}
|
|
982
|
+
case 'linear': {
|
|
983
|
+
const attn = new LinearAttention(query.length);
|
|
984
|
+
const queryMat = [query];
|
|
985
|
+
const output = attn.forward(queryMat, keys, values);
|
|
986
|
+
result = output[0];
|
|
987
|
+
break;
|
|
988
|
+
}
|
|
989
|
+
default:
|
|
990
|
+
throw new Error(`Unknown attention type: ${options.type}`);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
spinner.succeed(chalk.green(`Attention computed (${options.type})`));
|
|
994
|
+
|
|
995
|
+
console.log(chalk.cyan('\nAttention Results:'));
|
|
996
|
+
console.log(chalk.white(` Type: ${chalk.yellow(options.type)}`));
|
|
997
|
+
console.log(chalk.white(` Query dim: ${chalk.yellow(query.length)}`));
|
|
998
|
+
console.log(chalk.white(` Num keys: ${chalk.yellow(keys.length)}`));
|
|
999
|
+
console.log(chalk.white(` Output dim: ${chalk.yellow(result.length)}`));
|
|
1000
|
+
console.log(chalk.white(` Output: ${chalk.gray(`[${result.slice(0, 4).map(v => v.toFixed(4)).join(', ')}...]`)}`));
|
|
1001
|
+
|
|
1002
|
+
if (attentionWeights) {
|
|
1003
|
+
console.log(chalk.cyan('\nAttention Weights:'));
|
|
1004
|
+
attentionWeights.slice(0, 5).forEach((w, i) => {
|
|
1005
|
+
console.log(chalk.gray(` Key ${i}: ${w.toFixed(4)}`));
|
|
1006
|
+
});
|
|
1007
|
+
if (attentionWeights.length > 5) {
|
|
1008
|
+
console.log(chalk.gray(` ... and ${attentionWeights.length - 5} more`));
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
if (options.output) {
|
|
1013
|
+
const outputData = { result, attentionWeights };
|
|
1014
|
+
fs.writeFileSync(options.output, JSON.stringify(outputData, null, 2));
|
|
1015
|
+
console.log(chalk.green(`\nResults saved to: ${options.output}`));
|
|
1016
|
+
}
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
spinner.fail(chalk.red('Failed to compute attention'));
|
|
1019
|
+
console.error(chalk.red(error.message));
|
|
1020
|
+
process.exit(1);
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
// Attention benchmark command
|
|
1025
|
+
attentionCmd
|
|
1026
|
+
.command('benchmark')
|
|
1027
|
+
.description('Benchmark attention mechanisms')
|
|
1028
|
+
.option('-d, --dimension <number>', 'Vector dimension', '256')
|
|
1029
|
+
.option('-n, --num-vectors <number>', 'Number of vectors', '100')
|
|
1030
|
+
.option('-i, --iterations <number>', 'Benchmark iterations', '100')
|
|
1031
|
+
.option('-t, --types <list>', 'Attention types to benchmark (comma-separated)', 'dot,flash,linear')
|
|
1032
|
+
.action((options) => {
|
|
1033
|
+
requireAttention();
|
|
1034
|
+
const spinner = ora('Setting up benchmark...').start();
|
|
1035
|
+
|
|
1036
|
+
try {
|
|
1037
|
+
const dim = parseInt(options.dimension);
|
|
1038
|
+
const numVectors = parseInt(options.numVectors);
|
|
1039
|
+
const iterations = parseInt(options.iterations);
|
|
1040
|
+
const types = options.types.split(',').map(t => t.trim());
|
|
1041
|
+
|
|
1042
|
+
// Generate random test data
|
|
1043
|
+
spinner.text = 'Generating test data...';
|
|
1044
|
+
const query = Array.from({ length: dim }, () => Math.random());
|
|
1045
|
+
const keys = Array.from({ length: numVectors }, () =>
|
|
1046
|
+
Array.from({ length: dim }, () => Math.random())
|
|
1047
|
+
);
|
|
1048
|
+
|
|
1049
|
+
console.log(chalk.cyan('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1050
|
+
console.log(chalk.cyan(' Attention Mechanism Benchmark'));
|
|
1051
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1052
|
+
|
|
1053
|
+
console.log(chalk.white(` Dimension: ${chalk.yellow(dim)}`));
|
|
1054
|
+
console.log(chalk.white(` Vectors: ${chalk.yellow(numVectors)}`));
|
|
1055
|
+
console.log(chalk.white(` Iterations: ${chalk.yellow(iterations)}`));
|
|
1056
|
+
console.log('');
|
|
1057
|
+
|
|
1058
|
+
const results = [];
|
|
1059
|
+
|
|
1060
|
+
// Convert to Float32Arrays for compute()
|
|
1061
|
+
const queryF32 = new Float32Array(query);
|
|
1062
|
+
const keysF32 = keys.map(k => new Float32Array(k));
|
|
1063
|
+
|
|
1064
|
+
for (const type of types) {
|
|
1065
|
+
spinner.text = `Benchmarking ${type} attention...`;
|
|
1066
|
+
spinner.start();
|
|
1067
|
+
|
|
1068
|
+
let attn;
|
|
1069
|
+
try {
|
|
1070
|
+
switch (type) {
|
|
1071
|
+
case 'dot':
|
|
1072
|
+
attn = new DotProductAttention(dim);
|
|
1073
|
+
break;
|
|
1074
|
+
case 'flash':
|
|
1075
|
+
attn = new FlashAttention(dim, 64); // dim, block_size
|
|
1076
|
+
break;
|
|
1077
|
+
case 'linear':
|
|
1078
|
+
attn = new LinearAttention(dim, 64); // dim, num_features
|
|
1079
|
+
break;
|
|
1080
|
+
case 'hyperbolic':
|
|
1081
|
+
attn = new HyperbolicAttention(dim, 1.0);
|
|
1082
|
+
break;
|
|
1083
|
+
case 'multi-head':
|
|
1084
|
+
attn = new MultiHeadAttention(dim, 4); // dim, num_heads
|
|
1085
|
+
break;
|
|
1086
|
+
default:
|
|
1087
|
+
console.log(chalk.yellow(` Skipping unknown type: ${type}`));
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
} catch (e) {
|
|
1091
|
+
console.log(chalk.yellow(` ${type}: not available (${e.message})`));
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// Warm up
|
|
1096
|
+
for (let i = 0; i < 5; i++) {
|
|
1097
|
+
try {
|
|
1098
|
+
attn.compute(queryF32, keysF32, keysF32);
|
|
1099
|
+
} catch (e) {
|
|
1100
|
+
// Some mechanisms may fail warmup
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// Benchmark
|
|
1105
|
+
const start = process.hrtime.bigint();
|
|
1106
|
+
for (let i = 0; i < iterations; i++) {
|
|
1107
|
+
attn.compute(queryF32, keysF32, keysF32);
|
|
1108
|
+
}
|
|
1109
|
+
const end = process.hrtime.bigint();
|
|
1110
|
+
const totalMs = Number(end - start) / 1_000_000;
|
|
1111
|
+
const avgMs = totalMs / iterations;
|
|
1112
|
+
const opsPerSec = 1000 / avgMs;
|
|
1113
|
+
|
|
1114
|
+
results.push({ type, avgMs, opsPerSec });
|
|
1115
|
+
spinner.succeed(chalk.green(`${type}: ${avgMs.toFixed(3)} ms/op (${opsPerSec.toFixed(1)} ops/sec)`));
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// Summary
|
|
1119
|
+
if (results.length > 0) {
|
|
1120
|
+
console.log(chalk.cyan('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1121
|
+
console.log(chalk.cyan(' Summary'));
|
|
1122
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1123
|
+
|
|
1124
|
+
const fastest = results.reduce((a, b) => a.avgMs < b.avgMs ? a : b);
|
|
1125
|
+
console.log(chalk.green(` Fastest: ${fastest.type} (${fastest.avgMs.toFixed(3)} ms/op)\n`));
|
|
1126
|
+
|
|
1127
|
+
console.log(chalk.white(' Relative Performance:'));
|
|
1128
|
+
for (const r of results) {
|
|
1129
|
+
const relPerf = (fastest.avgMs / r.avgMs * 100).toFixed(1);
|
|
1130
|
+
const bar = 'โ'.repeat(Math.round(relPerf / 5));
|
|
1131
|
+
console.log(chalk.white(` ${r.type.padEnd(12)} ${chalk.cyan(bar)} ${relPerf}%`));
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
} catch (error) {
|
|
1135
|
+
spinner.fail(chalk.red('Benchmark failed'));
|
|
1136
|
+
console.error(chalk.red(error.message));
|
|
1137
|
+
process.exit(1);
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
// Hyperbolic math command
|
|
1142
|
+
attentionCmd
|
|
1143
|
+
.command('hyperbolic')
|
|
1144
|
+
.description('Hyperbolic geometry operations')
|
|
1145
|
+
.requiredOption('-a, --action <type>', 'Action: exp-map|log-map|distance|project|mobius-add')
|
|
1146
|
+
.requiredOption('-v, --vector <json>', 'Input vector(s) as JSON')
|
|
1147
|
+
.option('-b, --vector-b <json>', 'Second vector for binary operations')
|
|
1148
|
+
.option('-c, --curvature <number>', 'Poincarรฉ ball curvature', '1.0')
|
|
1149
|
+
.option('-o, --origin <json>', 'Origin point for exp/log maps')
|
|
1150
|
+
.action((options) => {
|
|
1151
|
+
requireAttention();
|
|
1152
|
+
|
|
1153
|
+
try {
|
|
1154
|
+
const vecArray = JSON.parse(options.vector);
|
|
1155
|
+
const vec = new Float32Array(vecArray);
|
|
1156
|
+
const curvature = parseFloat(options.curvature);
|
|
1157
|
+
|
|
1158
|
+
let result;
|
|
1159
|
+
let description;
|
|
1160
|
+
|
|
1161
|
+
switch (options.action) {
|
|
1162
|
+
case 'exp-map': {
|
|
1163
|
+
const originArray = options.origin ? JSON.parse(options.origin) : Array(vec.length).fill(0);
|
|
1164
|
+
const origin = new Float32Array(originArray);
|
|
1165
|
+
result = expMap(origin, vec, curvature);
|
|
1166
|
+
description = 'Exponential map (tangent โ Poincarรฉ ball)';
|
|
1167
|
+
break;
|
|
1168
|
+
}
|
|
1169
|
+
case 'log-map': {
|
|
1170
|
+
const originArray = options.origin ? JSON.parse(options.origin) : Array(vec.length).fill(0);
|
|
1171
|
+
const origin = new Float32Array(originArray);
|
|
1172
|
+
result = logMap(origin, vec, curvature);
|
|
1173
|
+
description = 'Logarithmic map (Poincarรฉ ball โ tangent)';
|
|
1174
|
+
break;
|
|
1175
|
+
}
|
|
1176
|
+
case 'distance': {
|
|
1177
|
+
if (!options.vectorB) {
|
|
1178
|
+
throw new Error('--vector-b required for distance calculation');
|
|
1179
|
+
}
|
|
1180
|
+
const vecBArray = JSON.parse(options.vectorB);
|
|
1181
|
+
const vecB = new Float32Array(vecBArray);
|
|
1182
|
+
result = poincareDistance(vec, vecB, curvature);
|
|
1183
|
+
description = 'Poincarรฉ distance';
|
|
1184
|
+
break;
|
|
1185
|
+
}
|
|
1186
|
+
case 'project': {
|
|
1187
|
+
result = projectToPoincareBall(vec, curvature);
|
|
1188
|
+
description = 'Project to Poincarรฉ ball';
|
|
1189
|
+
break;
|
|
1190
|
+
}
|
|
1191
|
+
case 'mobius-add': {
|
|
1192
|
+
if (!options.vectorB) {
|
|
1193
|
+
throw new Error('--vector-b required for Mรถbius addition');
|
|
1194
|
+
}
|
|
1195
|
+
const vecBArray = JSON.parse(options.vectorB);
|
|
1196
|
+
const vecB = new Float32Array(vecBArray);
|
|
1197
|
+
result = mobiusAddition(vec, vecB, curvature);
|
|
1198
|
+
description = 'Mรถbius addition';
|
|
1199
|
+
break;
|
|
1200
|
+
}
|
|
1201
|
+
default:
|
|
1202
|
+
throw new Error(`Unknown action: ${options.action}`);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
console.log(chalk.cyan('\nHyperbolic Operation:'));
|
|
1206
|
+
console.log(chalk.white(` Action: ${chalk.yellow(description)}`));
|
|
1207
|
+
console.log(chalk.white(` Curvature: ${chalk.yellow(curvature)}`));
|
|
1208
|
+
|
|
1209
|
+
if (typeof result === 'number') {
|
|
1210
|
+
console.log(chalk.white(` Result: ${chalk.green(result.toFixed(6))}`));
|
|
1211
|
+
} else {
|
|
1212
|
+
const resultArray = Array.from(result);
|
|
1213
|
+
console.log(chalk.white(` Input dim: ${chalk.yellow(vec.length)}`));
|
|
1214
|
+
console.log(chalk.white(` Output dim: ${chalk.yellow(resultArray.length)}`));
|
|
1215
|
+
console.log(chalk.white(` Result: ${chalk.gray(`[${resultArray.slice(0, 5).map(v => v.toFixed(4)).join(', ')}...]`)}`));
|
|
1216
|
+
|
|
1217
|
+
// Compute norm to verify it's in the ball
|
|
1218
|
+
const norm = Math.sqrt(resultArray.reduce((sum, x) => sum + x * x, 0));
|
|
1219
|
+
console.log(chalk.white(` Norm: ${chalk.yellow(norm.toFixed(6))} ${norm < 1 ? chalk.green('(inside ball)') : chalk.red('(outside ball)')}`));
|
|
1220
|
+
}
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
console.error(chalk.red('Hyperbolic operation failed:'), error.message);
|
|
1223
|
+
process.exit(1);
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
|
|
1227
|
+
// Attention info command
|
|
1228
|
+
attentionCmd
|
|
1229
|
+
.command('info')
|
|
1230
|
+
.description('Show attention module information')
|
|
1231
|
+
.action(() => {
|
|
1232
|
+
if (!attentionAvailable) {
|
|
1233
|
+
console.log(chalk.yellow('\nAttention Module: Not installed'));
|
|
1234
|
+
console.log(chalk.white('Install with: npm install @ruvector/attention'));
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
console.log(chalk.cyan('\nAttention Module Information'));
|
|
1239
|
+
console.log(chalk.white(` Status: ${chalk.green('Available')}`));
|
|
1240
|
+
console.log(chalk.white(` Version: ${chalk.yellow(attentionVersion ? attentionVersion() : 'unknown')}`));
|
|
1241
|
+
console.log(chalk.white(` Platform: ${chalk.yellow(process.platform)}`));
|
|
1242
|
+
console.log(chalk.white(` Architecture: ${chalk.yellow(process.arch)}`));
|
|
1243
|
+
|
|
1244
|
+
console.log(chalk.cyan('\nCore Attention Mechanisms:'));
|
|
1245
|
+
console.log(chalk.white(` โข DotProductAttention - Scaled dot-product attention`));
|
|
1246
|
+
console.log(chalk.white(` โข MultiHeadAttention - Multi-head self-attention`));
|
|
1247
|
+
console.log(chalk.white(` โข FlashAttention - Memory-efficient IO-aware attention`));
|
|
1248
|
+
console.log(chalk.white(` โข HyperbolicAttention - Poincarรฉ ball attention`));
|
|
1249
|
+
console.log(chalk.white(` โข LinearAttention - O(n) linear complexity attention`));
|
|
1250
|
+
console.log(chalk.white(` โข MoEAttention - Mixture of Experts attention`));
|
|
1251
|
+
|
|
1252
|
+
console.log(chalk.cyan('\nGraph Attention:'));
|
|
1253
|
+
console.log(chalk.white(` โข GraphRoPeAttention - Rotary position embeddings for graphs`));
|
|
1254
|
+
console.log(chalk.white(` โข EdgeFeaturedAttention - Edge feature-enhanced attention`));
|
|
1255
|
+
console.log(chalk.white(` โข DualSpaceAttention - Euclidean + hyperbolic dual space`));
|
|
1256
|
+
console.log(chalk.white(` โข LocalGlobalAttention - Local-global graph attention`));
|
|
1257
|
+
|
|
1258
|
+
console.log(chalk.cyan('\nHyperbolic Math:'));
|
|
1259
|
+
console.log(chalk.white(` โข expMap, logMap - Exponential/logarithmic maps`));
|
|
1260
|
+
console.log(chalk.white(` โข mobiusAddition - Mรถbius addition in Poincarรฉ ball`));
|
|
1261
|
+
console.log(chalk.white(` โข poincareDistance - Hyperbolic distance metric`));
|
|
1262
|
+
console.log(chalk.white(` โข projectToPoincareBall - Project vectors to ball`));
|
|
1263
|
+
|
|
1264
|
+
console.log(chalk.cyan('\nTraining Utilities:'));
|
|
1265
|
+
console.log(chalk.white(` โข AdamOptimizer, AdamWOptimizer, SgdOptimizer`));
|
|
1266
|
+
console.log(chalk.white(` โข InfoNceLoss, LocalContrastiveLoss`));
|
|
1267
|
+
console.log(chalk.white(` โข CurriculumScheduler, TemperatureAnnealing`));
|
|
1268
|
+
console.log(chalk.white(` โข HardNegativeMiner, InBatchMiner`));
|
|
1269
|
+
});
|
|
1270
|
+
|
|
1271
|
+
// Attention list command - list available mechanisms
|
|
1272
|
+
attentionCmd
|
|
1273
|
+
.command('list')
|
|
1274
|
+
.description('List all available attention mechanisms')
|
|
1275
|
+
.option('-v, --verbose', 'Show detailed information')
|
|
1276
|
+
.action((options) => {
|
|
1277
|
+
console.log(chalk.cyan('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1278
|
+
console.log(chalk.cyan(' Available Attention Mechanisms'));
|
|
1279
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1280
|
+
|
|
1281
|
+
const mechanisms = [
|
|
1282
|
+
{ name: 'DotProductAttention', type: 'core', complexity: 'O(nยฒ)', available: !!DotProductAttention },
|
|
1283
|
+
{ name: 'MultiHeadAttention', type: 'core', complexity: 'O(nยฒ)', available: !!MultiHeadAttention },
|
|
1284
|
+
{ name: 'FlashAttention', type: 'core', complexity: 'O(nยฒ) IO-optimized', available: !!FlashAttention },
|
|
1285
|
+
{ name: 'HyperbolicAttention', type: 'core', complexity: 'O(nยฒ)', available: !!HyperbolicAttention },
|
|
1286
|
+
{ name: 'LinearAttention', type: 'core', complexity: 'O(n)', available: !!LinearAttention },
|
|
1287
|
+
{ name: 'MoEAttention', type: 'core', complexity: 'O(n*k)', available: !!MoEAttention },
|
|
1288
|
+
{ name: 'GraphRoPeAttention', type: 'graph', complexity: 'O(nยฒ)', available: !!GraphRoPeAttention },
|
|
1289
|
+
{ name: 'EdgeFeaturedAttention', type: 'graph', complexity: 'O(nยฒ)', available: !!EdgeFeaturedAttention },
|
|
1290
|
+
{ name: 'DualSpaceAttention', type: 'graph', complexity: 'O(nยฒ)', available: !!DualSpaceAttention },
|
|
1291
|
+
{ name: 'LocalGlobalAttention', type: 'graph', complexity: 'O(n*k)', available: !!LocalGlobalAttention },
|
|
1292
|
+
];
|
|
1293
|
+
|
|
1294
|
+
console.log(chalk.white(' Core Attention:'));
|
|
1295
|
+
mechanisms.filter(m => m.type === 'core').forEach(m => {
|
|
1296
|
+
const status = m.available ? chalk.green('โ') : chalk.red('โ');
|
|
1297
|
+
console.log(chalk.white(` ${status} ${m.name.padEnd(22)} ${chalk.gray(m.complexity)}`));
|
|
1298
|
+
});
|
|
1299
|
+
|
|
1300
|
+
console.log(chalk.white('\n Graph Attention:'));
|
|
1301
|
+
mechanisms.filter(m => m.type === 'graph').forEach(m => {
|
|
1302
|
+
const status = m.available ? chalk.green('โ') : chalk.red('โ');
|
|
1303
|
+
console.log(chalk.white(` ${status} ${m.name.padEnd(22)} ${chalk.gray(m.complexity)}`));
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
if (!attentionAvailable) {
|
|
1307
|
+
console.log(chalk.yellow('\n Note: @ruvector/attention not installed'));
|
|
1308
|
+
console.log(chalk.white(' Install with: npm install @ruvector/attention'));
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
if (options.verbose) {
|
|
1312
|
+
console.log(chalk.cyan('\n Usage Examples:'));
|
|
1313
|
+
console.log(chalk.gray(' # Compute dot-product attention'));
|
|
1314
|
+
console.log(chalk.white(' npx ruvector attention compute -q "[1,2,3]" -k keys.json -t dot'));
|
|
1315
|
+
console.log(chalk.gray('\n # Benchmark attention mechanisms'));
|
|
1316
|
+
console.log(chalk.white(' npx ruvector attention benchmark -d 256 -n 100'));
|
|
1317
|
+
console.log(chalk.gray('\n # Hyperbolic distance'));
|
|
1318
|
+
console.log(chalk.white(' npx ruvector attention hyperbolic -a distance -v "[0.1,0.2]" -b "[0.3,0.4]"'));
|
|
1319
|
+
}
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
// =============================================================================
|
|
1323
|
+
// Doctor Command - Check system health and dependencies
|
|
1324
|
+
// =============================================================================
|
|
1325
|
+
|
|
1326
|
+
program
|
|
1327
|
+
.command('doctor')
|
|
1328
|
+
.description('Check system health and dependencies')
|
|
1329
|
+
.option('-v, --verbose', 'Show detailed information')
|
|
1330
|
+
.action(async (options) => {
|
|
1331
|
+
const { execSync } = require('child_process');
|
|
1332
|
+
|
|
1333
|
+
console.log(chalk.cyan('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1334
|
+
console.log(chalk.cyan(' RuVector Doctor'));
|
|
1335
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1336
|
+
|
|
1337
|
+
let issues = 0;
|
|
1338
|
+
let warnings = 0;
|
|
1339
|
+
|
|
1340
|
+
// Helper functions
|
|
1341
|
+
const check = (name, condition, fix) => {
|
|
1342
|
+
if (condition) {
|
|
1343
|
+
console.log(chalk.green(` โ ${name}`));
|
|
1344
|
+
return true;
|
|
1345
|
+
} else {
|
|
1346
|
+
console.log(chalk.red(` โ ${name}`));
|
|
1347
|
+
if (fix) console.log(chalk.gray(` Fix: ${fix}`));
|
|
1348
|
+
issues++;
|
|
1349
|
+
return false;
|
|
1350
|
+
}
|
|
1351
|
+
};
|
|
1352
|
+
|
|
1353
|
+
const warn = (name, condition, suggestion) => {
|
|
1354
|
+
if (condition) {
|
|
1355
|
+
console.log(chalk.green(` โ ${name}`));
|
|
1356
|
+
return true;
|
|
1357
|
+
} else {
|
|
1358
|
+
console.log(chalk.yellow(` ! ${name}`));
|
|
1359
|
+
if (suggestion) console.log(chalk.gray(` Suggestion: ${suggestion}`));
|
|
1360
|
+
warnings++;
|
|
1361
|
+
return false;
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
const getVersion = (cmd) => {
|
|
1366
|
+
try {
|
|
1367
|
+
return execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
1368
|
+
} catch (e) {
|
|
1369
|
+
return null;
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
|
|
1373
|
+
// System Information
|
|
1374
|
+
console.log(chalk.cyan('System Information:'));
|
|
1375
|
+
console.log(chalk.white(` Platform: ${chalk.yellow(process.platform)}`));
|
|
1376
|
+
console.log(chalk.white(` Architecture: ${chalk.yellow(process.arch)}`));
|
|
1377
|
+
console.log(chalk.white(` Node.js: ${chalk.yellow(process.version)}`));
|
|
1378
|
+
console.log('');
|
|
1379
|
+
|
|
1380
|
+
// Node.js Checks
|
|
1381
|
+
console.log(chalk.cyan('Node.js Environment:'));
|
|
1382
|
+
const nodeVersion = parseInt(process.version.slice(1).split('.')[0]);
|
|
1383
|
+
check('Node.js >= 14', nodeVersion >= 14, 'Upgrade Node.js: https://nodejs.org');
|
|
1384
|
+
|
|
1385
|
+
const npmVersion = getVersion('npm --version');
|
|
1386
|
+
if (npmVersion) {
|
|
1387
|
+
console.log(chalk.green(` โ npm ${npmVersion}`));
|
|
1388
|
+
} else {
|
|
1389
|
+
check('npm installed', false, 'Install npm or reinstall Node.js');
|
|
1390
|
+
}
|
|
1391
|
+
console.log('');
|
|
1392
|
+
|
|
1393
|
+
// RuVector Packages
|
|
1394
|
+
console.log(chalk.cyan('RuVector Packages:'));
|
|
1395
|
+
|
|
1396
|
+
// Check @ruvector/core
|
|
1397
|
+
let coreAvailable = false;
|
|
1398
|
+
try {
|
|
1399
|
+
require.resolve('@ruvector/core');
|
|
1400
|
+
coreAvailable = true;
|
|
1401
|
+
console.log(chalk.green(` โ @ruvector/core installed`));
|
|
1402
|
+
} catch (e) {
|
|
1403
|
+
console.log(chalk.yellow(` ! @ruvector/core not found (using WASM fallback)`));
|
|
1404
|
+
warnings++;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
// Check if native binding works
|
|
1408
|
+
if (coreAvailable && loadRuvector()) {
|
|
1409
|
+
const version = typeof getVersion === 'function' ? getVersion() : null;
|
|
1410
|
+
const impl = typeof getImplementationType === 'function' ? getImplementationType() : 'native';
|
|
1411
|
+
const versionStr = version ? `, v${version}` : '';
|
|
1412
|
+
console.log(chalk.green(` โ Native binding working (${impl}${versionStr})`));
|
|
1413
|
+
} else if (coreAvailable) {
|
|
1414
|
+
console.log(chalk.yellow(` ! Native binding failed to load`));
|
|
1415
|
+
warnings++;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
// Check @ruvector/gnn
|
|
1419
|
+
if (gnnAvailable) {
|
|
1420
|
+
console.log(chalk.green(` โ @ruvector/gnn installed`));
|
|
1421
|
+
} else {
|
|
1422
|
+
console.log(chalk.gray(` โ @ruvector/gnn not installed (optional)`));
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// Check @ruvector/attention
|
|
1426
|
+
if (attentionAvailable) {
|
|
1427
|
+
console.log(chalk.green(` โ @ruvector/attention installed`));
|
|
1428
|
+
} else {
|
|
1429
|
+
console.log(chalk.gray(` โ @ruvector/attention not installed (optional)`));
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
// Check @ruvector/graph-node
|
|
1433
|
+
try {
|
|
1434
|
+
require.resolve('@ruvector/graph-node');
|
|
1435
|
+
console.log(chalk.green(` โ @ruvector/graph-node installed`));
|
|
1436
|
+
} catch (e) {
|
|
1437
|
+
console.log(chalk.gray(` โ @ruvector/graph-node not installed (optional)`));
|
|
1438
|
+
}
|
|
1439
|
+
console.log('');
|
|
1440
|
+
|
|
1441
|
+
// Rust Toolchain (optional for development)
|
|
1442
|
+
console.log(chalk.cyan('Rust Toolchain (optional):'));
|
|
1443
|
+
|
|
1444
|
+
const rustVersion = getVersion('rustc --version');
|
|
1445
|
+
if (rustVersion) {
|
|
1446
|
+
console.log(chalk.green(` โ ${rustVersion}`));
|
|
1447
|
+
} else {
|
|
1448
|
+
console.log(chalk.gray(` โ Rust not installed (only needed for development)`));
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
const cargoVersion = getVersion('cargo --version');
|
|
1452
|
+
if (cargoVersion) {
|
|
1453
|
+
console.log(chalk.green(` โ ${cargoVersion}`));
|
|
1454
|
+
} else if (rustVersion) {
|
|
1455
|
+
console.log(chalk.yellow(` ! cargo not found`));
|
|
1456
|
+
warnings++;
|
|
1457
|
+
}
|
|
1458
|
+
console.log('');
|
|
1459
|
+
|
|
1460
|
+
// Build Tools (optional)
|
|
1461
|
+
if (options.verbose) {
|
|
1462
|
+
console.log(chalk.cyan('Build Tools (for native compilation):'));
|
|
1463
|
+
|
|
1464
|
+
const hasGcc = getVersion('gcc --version');
|
|
1465
|
+
const hasClang = getVersion('clang --version');
|
|
1466
|
+
const hasCc = getVersion('cc --version');
|
|
1467
|
+
|
|
1468
|
+
if (hasGcc || hasClang || hasCc) {
|
|
1469
|
+
console.log(chalk.green(` โ C compiler available`));
|
|
1470
|
+
} else {
|
|
1471
|
+
console.log(chalk.gray(` โ No C compiler found (only needed for building from source)`));
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
const hasMake = getVersion('make --version');
|
|
1475
|
+
if (hasMake) {
|
|
1476
|
+
console.log(chalk.green(` โ make available`));
|
|
1477
|
+
} else {
|
|
1478
|
+
console.log(chalk.gray(` โ make not found`));
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
const hasCmake = getVersion('cmake --version');
|
|
1482
|
+
if (hasCmake) {
|
|
1483
|
+
console.log(chalk.green(` โ cmake available`));
|
|
1484
|
+
} else {
|
|
1485
|
+
console.log(chalk.gray(` โ cmake not found`));
|
|
1486
|
+
}
|
|
1487
|
+
console.log('');
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// Summary
|
|
1491
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1492
|
+
if (issues === 0 && warnings === 0) {
|
|
1493
|
+
console.log(chalk.green('\n โ All checks passed! RuVector is ready to use.\n'));
|
|
1494
|
+
} else if (issues === 0) {
|
|
1495
|
+
console.log(chalk.yellow(`\n ! ${warnings} warning(s) found. RuVector should work but may have limited features.\n`));
|
|
1496
|
+
} else {
|
|
1497
|
+
console.log(chalk.red(`\n โ ${issues} issue(s) and ${warnings} warning(s) found.\n`));
|
|
1498
|
+
console.log(chalk.white(' Run "npx ruvector setup" for installation instructions.\n'));
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1501
|
+
|
|
1502
|
+
// =============================================================================
|
|
1503
|
+
// Setup Command - Installation instructions
|
|
1504
|
+
// =============================================================================
|
|
1505
|
+
|
|
1506
|
+
program
|
|
1507
|
+
.command('setup')
|
|
1508
|
+
.description('Show installation and setup instructions')
|
|
1509
|
+
.option('--rust', 'Show Rust installation instructions')
|
|
1510
|
+
.option('--npm', 'Show npm package installation instructions')
|
|
1511
|
+
.option('--all', 'Show all installation instructions')
|
|
1512
|
+
.action((options) => {
|
|
1513
|
+
const showAll = options.all || (!options.rust && !options.npm);
|
|
1514
|
+
|
|
1515
|
+
console.log(chalk.cyan('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1516
|
+
console.log(chalk.cyan(' RuVector Setup Guide'));
|
|
1517
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1518
|
+
|
|
1519
|
+
// Quick install
|
|
1520
|
+
console.log(chalk.cyan('Quick Install (one-liner):'));
|
|
1521
|
+
console.log(chalk.white(' curl -fsSL https://raw.githubusercontent.com/ruvnet/ruvector/main/install.sh | bash'));
|
|
1522
|
+
console.log('');
|
|
1523
|
+
|
|
1524
|
+
if (showAll || options.npm) {
|
|
1525
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1526
|
+
console.log(chalk.cyan('npm Packages'));
|
|
1527
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1528
|
+
|
|
1529
|
+
console.log(chalk.yellow('All-in-one CLI:'));
|
|
1530
|
+
console.log(chalk.white(' npm install -g ruvector'));
|
|
1531
|
+
console.log(chalk.white(' npx ruvector'));
|
|
1532
|
+
console.log('');
|
|
1533
|
+
|
|
1534
|
+
console.log(chalk.yellow('Core packages:'));
|
|
1535
|
+
console.log(chalk.white(' npm install @ruvector/core # Vector database'));
|
|
1536
|
+
console.log(chalk.white(' npm install @ruvector/gnn # Graph Neural Networks'));
|
|
1537
|
+
console.log(chalk.white(' npm install @ruvector/graph-node # Hypergraph database'));
|
|
1538
|
+
console.log('');
|
|
1539
|
+
|
|
1540
|
+
console.log(chalk.yellow('Install all optional packages:'));
|
|
1541
|
+
console.log(chalk.white(' npx ruvector install --all'));
|
|
1542
|
+
console.log('');
|
|
1543
|
+
|
|
1544
|
+
console.log(chalk.yellow('List available packages:'));
|
|
1545
|
+
console.log(chalk.white(' npx ruvector install'));
|
|
1546
|
+
console.log('');
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
if (showAll || options.rust) {
|
|
1550
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1551
|
+
console.log(chalk.cyan('Rust Installation'));
|
|
1552
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1553
|
+
|
|
1554
|
+
console.log(chalk.yellow('1. Install Rust:'));
|
|
1555
|
+
console.log(chalk.white(' curl --proto \'=https\' --tlsv1.2 -sSf https://sh.rustup.rs | sh'));
|
|
1556
|
+
console.log(chalk.gray(' # Follow the prompts, then restart your terminal or run:'));
|
|
1557
|
+
console.log(chalk.white(' source $HOME/.cargo/env'));
|
|
1558
|
+
console.log('');
|
|
1559
|
+
|
|
1560
|
+
console.log(chalk.yellow('2. Verify installation:'));
|
|
1561
|
+
console.log(chalk.white(' rustc --version'));
|
|
1562
|
+
console.log(chalk.white(' cargo --version'));
|
|
1563
|
+
console.log('');
|
|
1564
|
+
|
|
1565
|
+
console.log(chalk.yellow('3. Add RuVector crates to your project:'));
|
|
1566
|
+
console.log(chalk.white(' cargo add ruvector-core # Vector database'));
|
|
1567
|
+
console.log(chalk.white(' cargo add ruvector-graph # Hypergraph with Cypher'));
|
|
1568
|
+
console.log(chalk.white(' cargo add ruvector-gnn # Graph Neural Networks'));
|
|
1569
|
+
console.log('');
|
|
1570
|
+
|
|
1571
|
+
console.log(chalk.yellow('4. Other available crates:'));
|
|
1572
|
+
console.log(chalk.white(' cargo add ruvector-cluster # Distributed clustering'));
|
|
1573
|
+
console.log(chalk.white(' cargo add ruvector-raft # Raft consensus'));
|
|
1574
|
+
console.log(chalk.white(' cargo add ruvector-replication # Data replication'));
|
|
1575
|
+
console.log(chalk.white(' cargo add ruvector-tiny-dancer-core # AI routing'));
|
|
1576
|
+
console.log(chalk.white(' cargo add ruvector-router-core # Semantic routing'));
|
|
1577
|
+
console.log('');
|
|
1578
|
+
|
|
1579
|
+
console.log(chalk.yellow('Platform-specific notes:'));
|
|
1580
|
+
console.log('');
|
|
1581
|
+
|
|
1582
|
+
if (process.platform === 'darwin') {
|
|
1583
|
+
console.log(chalk.cyan(' macOS:'));
|
|
1584
|
+
console.log(chalk.white(' xcode-select --install # Install command line tools'));
|
|
1585
|
+
console.log('');
|
|
1586
|
+
} else if (process.platform === 'linux') {
|
|
1587
|
+
console.log(chalk.cyan(' Linux (Debian/Ubuntu):'));
|
|
1588
|
+
console.log(chalk.white(' sudo apt-get update'));
|
|
1589
|
+
console.log(chalk.white(' sudo apt-get install build-essential pkg-config libssl-dev'));
|
|
1590
|
+
console.log('');
|
|
1591
|
+
console.log(chalk.cyan(' Linux (RHEL/CentOS):'));
|
|
1592
|
+
console.log(chalk.white(' sudo yum groupinstall "Development Tools"'));
|
|
1593
|
+
console.log(chalk.white(' sudo yum install openssl-devel'));
|
|
1594
|
+
console.log('');
|
|
1595
|
+
} else if (process.platform === 'win32') {
|
|
1596
|
+
console.log(chalk.cyan(' Windows:'));
|
|
1597
|
+
console.log(chalk.white(' # Install Visual Studio Build Tools'));
|
|
1598
|
+
console.log(chalk.white(' # https://visualstudio.microsoft.com/visual-cpp-build-tools/'));
|
|
1599
|
+
console.log(chalk.white(' # Or use WSL2 for best experience'));
|
|
1600
|
+
console.log('');
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1605
|
+
console.log(chalk.cyan('Documentation & Resources'));
|
|
1606
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1607
|
+
|
|
1608
|
+
console.log(chalk.white(' GitHub: https://github.com/ruvnet/ruvector'));
|
|
1609
|
+
console.log(chalk.white(' npm: https://www.npmjs.com/package/ruvector'));
|
|
1610
|
+
console.log(chalk.white(' crates.io: https://crates.io/crates/ruvector-core'));
|
|
1611
|
+
console.log(chalk.white(' Issues: https://github.com/ruvnet/ruvector/issues'));
|
|
1612
|
+
console.log('');
|
|
1613
|
+
|
|
1614
|
+
console.log(chalk.cyan('Quick Commands:'));
|
|
1615
|
+
console.log(chalk.white(' npx ruvector doctor # Check system health'));
|
|
1616
|
+
console.log(chalk.white(' npx ruvector info # Show version info'));
|
|
1617
|
+
console.log(chalk.white(' npx ruvector benchmark # Run performance test'));
|
|
1618
|
+
console.log(chalk.white(' npx ruvector install # List available packages'));
|
|
1619
|
+
console.log('');
|
|
1620
|
+
});
|
|
1621
|
+
|
|
1622
|
+
// =============================================================================
|
|
1623
|
+
// Graph Commands - Cypher queries and graph operations
|
|
1624
|
+
// =============================================================================
|
|
1625
|
+
|
|
1626
|
+
program
|
|
1627
|
+
.command('graph')
|
|
1628
|
+
.description('Graph database operations (requires @ruvector/graph-node)')
|
|
1629
|
+
.option('-q, --query <cypher>', 'Execute Cypher query')
|
|
1630
|
+
.option('-c, --create <label>', 'Create a node with label')
|
|
1631
|
+
.option('-p, --properties <json>', 'Node properties as JSON')
|
|
1632
|
+
.option('-r, --relate <spec>', 'Create relationship (from:rel:to)')
|
|
1633
|
+
.option('--info', 'Show graph info and stats')
|
|
1634
|
+
.action(async (options) => {
|
|
1635
|
+
let graphNode;
|
|
1636
|
+
try {
|
|
1637
|
+
graphNode = require('@ruvector/graph-node');
|
|
1638
|
+
} catch (e) {
|
|
1639
|
+
console.log(chalk.yellow('\n @ruvector/graph-node is not installed.\n'));
|
|
1640
|
+
console.log(chalk.cyan(' Install with:'));
|
|
1641
|
+
console.log(chalk.white(' npm install @ruvector/graph-node\n'));
|
|
1642
|
+
console.log(chalk.cyan(' Features:'));
|
|
1643
|
+
console.log(chalk.gray(' - Cypher query language support'));
|
|
1644
|
+
console.log(chalk.gray(' - Hypergraph data structure'));
|
|
1645
|
+
console.log(chalk.gray(' - Knowledge graph operations'));
|
|
1646
|
+
console.log(chalk.gray(' - Neo4j-compatible syntax\n'));
|
|
1647
|
+
console.log(chalk.cyan(' Example usage:'));
|
|
1648
|
+
console.log(chalk.white(' npx ruvector graph --query "CREATE (n:Person {name: \'Alice\'})"'));
|
|
1649
|
+
console.log(chalk.white(' npx ruvector graph --query "MATCH (n) RETURN n"'));
|
|
1650
|
+
console.log('');
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
console.log(chalk.cyan('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1655
|
+
console.log(chalk.cyan(' RuVector Graph'));
|
|
1656
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1657
|
+
|
|
1658
|
+
if (options.info) {
|
|
1659
|
+
console.log(chalk.green(' @ruvector/graph-node is available!'));
|
|
1660
|
+
console.log(chalk.gray(` Platform: ${process.platform}-${process.arch}`));
|
|
1661
|
+
console.log('');
|
|
1662
|
+
console.log(chalk.yellow(' Available operations:'));
|
|
1663
|
+
console.log(chalk.white(' --query <cypher> Execute Cypher query'));
|
|
1664
|
+
console.log(chalk.white(' --create <label> Create node with label'));
|
|
1665
|
+
console.log(chalk.white(' --relate <spec> Create relationship'));
|
|
1666
|
+
console.log('');
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
if (options.query) {
|
|
1671
|
+
console.log(chalk.yellow(' Cypher Query:'), chalk.white(options.query));
|
|
1672
|
+
console.log('');
|
|
1673
|
+
// Actual implementation would execute the query
|
|
1674
|
+
console.log(chalk.gray(' Note: Full Cypher execution requires running ruvector-server'));
|
|
1675
|
+
console.log(chalk.gray(' See: npx ruvector server --help'));
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
if (options.create) {
|
|
1679
|
+
const label = options.create;
|
|
1680
|
+
const props = options.properties ? JSON.parse(options.properties) : {};
|
|
1681
|
+
console.log(chalk.yellow(' Creating node:'), chalk.white(label));
|
|
1682
|
+
console.log(chalk.gray(' Properties:'), JSON.stringify(props, null, 2));
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
console.log('');
|
|
1686
|
+
});
|
|
1687
|
+
|
|
1688
|
+
// =============================================================================
|
|
1689
|
+
// Router Commands - AI agent routing
|
|
1690
|
+
// =============================================================================
|
|
1691
|
+
|
|
1692
|
+
program
|
|
1693
|
+
.command('router')
|
|
1694
|
+
.description('AI semantic router operations (requires ruvector-router-core)')
|
|
1695
|
+
.option('--route <text>', 'Route text to best matching intent')
|
|
1696
|
+
.option('--intents <file>', 'Load intents from JSON file')
|
|
1697
|
+
.option('--add-intent <name>', 'Add new intent')
|
|
1698
|
+
.option('--examples <json>', 'Example utterances for intent')
|
|
1699
|
+
.option('--info', 'Show router info')
|
|
1700
|
+
.action(async (options) => {
|
|
1701
|
+
console.log(chalk.cyan('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1702
|
+
console.log(chalk.cyan(' RuVector Router'));
|
|
1703
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1704
|
+
|
|
1705
|
+
console.log(chalk.yellow(' Semantic Router for AI Agent Routing\n'));
|
|
1706
|
+
|
|
1707
|
+
if (options.info || (!options.route && !options.intents && !options.addIntent)) {
|
|
1708
|
+
console.log(chalk.cyan(' Features:'));
|
|
1709
|
+
console.log(chalk.gray(' - Semantic intent matching'));
|
|
1710
|
+
console.log(chalk.gray(' - Multi-agent routing'));
|
|
1711
|
+
console.log(chalk.gray(' - Dynamic intent registration'));
|
|
1712
|
+
console.log(chalk.gray(' - Vector-based similarity matching'));
|
|
1713
|
+
console.log('');
|
|
1714
|
+
console.log(chalk.cyan(' Status:'), chalk.yellow('Coming Soon'));
|
|
1715
|
+
console.log(chalk.gray(' The npm package for router is in development.'));
|
|
1716
|
+
console.log(chalk.gray(' Rust crate available: cargo add ruvector-router-core'));
|
|
1717
|
+
console.log('');
|
|
1718
|
+
console.log(chalk.cyan(' Usage (when available):'));
|
|
1719
|
+
console.log(chalk.white(' npx ruvector router --route "What is the weather?"'));
|
|
1720
|
+
console.log(chalk.white(' npx ruvector router --intents intents.json --route "query"'));
|
|
1721
|
+
console.log('');
|
|
1722
|
+
return;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
if (options.route) {
|
|
1726
|
+
console.log(chalk.yellow(' Input:'), chalk.white(options.route));
|
|
1727
|
+
console.log(chalk.gray(' Router package not yet available in npm.'));
|
|
1728
|
+
console.log(chalk.gray(' Check issue #20 for roadmap.'));
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
console.log('');
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1734
|
+
// =============================================================================
|
|
1735
|
+
// Server Commands - HTTP/gRPC server
|
|
1736
|
+
// =============================================================================
|
|
1737
|
+
|
|
1738
|
+
program
|
|
1739
|
+
.command('server')
|
|
1740
|
+
.description('Start RuVector HTTP/gRPC server')
|
|
1741
|
+
.option('-p, --port <number>', 'HTTP port', '8080')
|
|
1742
|
+
.option('-g, --grpc-port <number>', 'gRPC port', '50051')
|
|
1743
|
+
.option('-d, --data-dir <path>', 'Data directory', './ruvector-data')
|
|
1744
|
+
.option('--http-only', 'Start only HTTP server')
|
|
1745
|
+
.option('--grpc-only', 'Start only gRPC server')
|
|
1746
|
+
.option('--cors', 'Enable CORS for all origins')
|
|
1747
|
+
.option('--info', 'Show server info')
|
|
1748
|
+
.action(async (options) => {
|
|
1749
|
+
console.log(chalk.cyan('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1750
|
+
console.log(chalk.cyan(' RuVector Server'));
|
|
1751
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1752
|
+
|
|
1753
|
+
if (options.info || Object.keys(options).filter(k => k !== 'port' && k !== 'grpcPort' && k !== 'dataDir').length === 0) {
|
|
1754
|
+
console.log(chalk.cyan(' Status:'), chalk.yellow('Coming Soon'));
|
|
1755
|
+
console.log('');
|
|
1756
|
+
console.log(chalk.cyan(' Planned Features:'));
|
|
1757
|
+
console.log(chalk.gray(' - REST API for vector operations'));
|
|
1758
|
+
console.log(chalk.gray(' - gRPC high-performance interface'));
|
|
1759
|
+
console.log(chalk.gray(' - WebSocket real-time updates'));
|
|
1760
|
+
console.log(chalk.gray(' - OpenAPI/Swagger documentation'));
|
|
1761
|
+
console.log(chalk.gray(' - Prometheus metrics endpoint'));
|
|
1762
|
+
console.log(chalk.gray(' - Health check endpoints'));
|
|
1763
|
+
console.log('');
|
|
1764
|
+
console.log(chalk.cyan(' Rust binary available:'));
|
|
1765
|
+
console.log(chalk.white(' cargo install ruvector-server # When published'));
|
|
1766
|
+
console.log('');
|
|
1767
|
+
console.log(chalk.cyan(' Configuration (when available):'));
|
|
1768
|
+
console.log(chalk.white(` --port ${options.port} # HTTP port`));
|
|
1769
|
+
console.log(chalk.white(` --grpc-port ${options.grpcPort} # gRPC port`));
|
|
1770
|
+
console.log(chalk.white(` --data-dir ${options.dataDir} # Data directory`));
|
|
1771
|
+
console.log('');
|
|
1772
|
+
console.log(chalk.gray(' Track progress: https://github.com/ruvnet/ruvector/issues/20'));
|
|
1773
|
+
console.log('');
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
console.log(chalk.yellow(' Server package not yet available.'));
|
|
1778
|
+
console.log(chalk.gray(' Check issue #20 for roadmap.'));
|
|
1779
|
+
console.log('');
|
|
1780
|
+
});
|
|
1781
|
+
|
|
1782
|
+
// =============================================================================
|
|
1783
|
+
// Cluster Commands - Distributed operations
|
|
1784
|
+
// =============================================================================
|
|
1785
|
+
|
|
1786
|
+
program
|
|
1787
|
+
.command('cluster')
|
|
1788
|
+
.description('Distributed cluster operations')
|
|
1789
|
+
.option('--status', 'Show cluster status')
|
|
1790
|
+
.option('--join <address>', 'Join existing cluster')
|
|
1791
|
+
.option('--leave', 'Leave cluster')
|
|
1792
|
+
.option('--nodes', 'List cluster nodes')
|
|
1793
|
+
.option('--leader', 'Show current leader')
|
|
1794
|
+
.option('--info', 'Show cluster info')
|
|
1795
|
+
.action(async (options) => {
|
|
1796
|
+
console.log(chalk.cyan('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1797
|
+
console.log(chalk.cyan(' RuVector Cluster'));
|
|
1798
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1799
|
+
|
|
1800
|
+
console.log(chalk.cyan(' Status:'), chalk.yellow('Coming Soon'));
|
|
1801
|
+
console.log('');
|
|
1802
|
+
console.log(chalk.cyan(' Features:'));
|
|
1803
|
+
console.log(chalk.gray(' - Raft consensus for leader election'));
|
|
1804
|
+
console.log(chalk.gray(' - Automatic failover'));
|
|
1805
|
+
console.log(chalk.gray(' - Data replication'));
|
|
1806
|
+
console.log(chalk.gray(' - Sharding support'));
|
|
1807
|
+
console.log(chalk.gray(' - Distributed queries'));
|
|
1808
|
+
console.log('');
|
|
1809
|
+
console.log(chalk.cyan(' Rust crates available:'));
|
|
1810
|
+
console.log(chalk.white(' cargo add ruvector-cluster # Clustering'));
|
|
1811
|
+
console.log(chalk.white(' cargo add ruvector-raft # Raft consensus'));
|
|
1812
|
+
console.log(chalk.white(' cargo add ruvector-replication # Replication'));
|
|
1813
|
+
console.log('');
|
|
1814
|
+
console.log(chalk.cyan(' Commands (when available):'));
|
|
1815
|
+
console.log(chalk.white(' npx ruvector cluster --status'));
|
|
1816
|
+
console.log(chalk.white(' npx ruvector cluster --join 192.168.1.10:7000'));
|
|
1817
|
+
console.log(chalk.white(' npx ruvector cluster --nodes'));
|
|
1818
|
+
console.log('');
|
|
1819
|
+
console.log(chalk.gray(' Track progress: https://github.com/ruvnet/ruvector/issues/20'));
|
|
1820
|
+
console.log('');
|
|
1821
|
+
});
|
|
1822
|
+
|
|
1823
|
+
// =============================================================================
|
|
1824
|
+
// Export/Import Commands - Database backup/restore
|
|
1825
|
+
// =============================================================================
|
|
1826
|
+
|
|
1827
|
+
program
|
|
1828
|
+
.command('export <database>')
|
|
1829
|
+
.description('Export database to file')
|
|
1830
|
+
.option('-o, --output <file>', 'Output file path')
|
|
1831
|
+
.option('-f, --format <type>', 'Export format (json|binary|parquet)', 'json')
|
|
1832
|
+
.option('--compress', 'Compress output')
|
|
1833
|
+
.option('--vectors-only', 'Export only vectors (no metadata)')
|
|
1834
|
+
.action(async (dbPath, options) => {
|
|
1835
|
+
requireRuvector();
|
|
1836
|
+
const spinner = ora('Exporting database...').start();
|
|
1837
|
+
|
|
1838
|
+
try {
|
|
1839
|
+
const outputFile = options.output || `${dbPath.replace(/\/$/, '')}_export.${options.format}`;
|
|
1840
|
+
|
|
1841
|
+
// Load database
|
|
1842
|
+
const db = new VectorDB({ dimension: 384 }); // Will be overwritten by load
|
|
1843
|
+
if (fs.existsSync(dbPath)) {
|
|
1844
|
+
db.load(dbPath);
|
|
1845
|
+
} else {
|
|
1846
|
+
spinner.fail(chalk.red(`Database not found: ${dbPath}`));
|
|
1847
|
+
process.exit(1);
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
const stats = db.getStats();
|
|
1851
|
+
const data = {
|
|
1852
|
+
version: packageJson.version,
|
|
1853
|
+
exportedAt: new Date().toISOString(),
|
|
1854
|
+
stats: stats,
|
|
1855
|
+
vectors: [] // Would contain actual vector data
|
|
1856
|
+
};
|
|
1857
|
+
|
|
1858
|
+
if (options.format === 'json') {
|
|
1859
|
+
fs.writeFileSync(outputFile, JSON.stringify(data, null, 2));
|
|
1860
|
+
} else {
|
|
1861
|
+
spinner.fail(chalk.yellow(`Format '${options.format}' not yet supported. Using JSON.`));
|
|
1862
|
+
fs.writeFileSync(outputFile.replace(/\.[^.]+$/, '.json'), JSON.stringify(data, null, 2));
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
spinner.succeed(chalk.green(`Exported to: ${outputFile}`));
|
|
1866
|
+
console.log(chalk.gray(` Vectors: ${stats.count || 0}`));
|
|
1867
|
+
console.log(chalk.gray(` Format: ${options.format}`));
|
|
1868
|
+
} catch (error) {
|
|
1869
|
+
spinner.fail(chalk.red('Export failed'));
|
|
1870
|
+
console.error(chalk.red(error.message));
|
|
1871
|
+
process.exit(1);
|
|
1872
|
+
}
|
|
1873
|
+
});
|
|
1874
|
+
|
|
1875
|
+
program
|
|
1876
|
+
.command('import <file>')
|
|
1877
|
+
.description('Import database from file')
|
|
1878
|
+
.option('-d, --database <path>', 'Target database path')
|
|
1879
|
+
.option('--merge', 'Merge with existing data')
|
|
1880
|
+
.option('--replace', 'Replace existing data')
|
|
1881
|
+
.action(async (file, options) => {
|
|
1882
|
+
requireRuvector();
|
|
1883
|
+
const spinner = ora('Importing database...').start();
|
|
1884
|
+
|
|
1885
|
+
try {
|
|
1886
|
+
if (!fs.existsSync(file)) {
|
|
1887
|
+
spinner.fail(chalk.red(`File not found: ${file}`));
|
|
1888
|
+
process.exit(1);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
1892
|
+
const dbPath = options.database || file.replace(/_export\.json$/, '');
|
|
1893
|
+
|
|
1894
|
+
spinner.text = 'Creating database...';
|
|
1895
|
+
|
|
1896
|
+
const db = new VectorDB({
|
|
1897
|
+
dimension: data.stats?.dimension || 384,
|
|
1898
|
+
path: dbPath,
|
|
1899
|
+
autoPersist: true
|
|
1900
|
+
});
|
|
1901
|
+
|
|
1902
|
+
// Would import actual vectors here
|
|
1903
|
+
db.save(dbPath);
|
|
1904
|
+
|
|
1905
|
+
spinner.succeed(chalk.green(`Imported to: ${dbPath}`));
|
|
1906
|
+
console.log(chalk.gray(` Source version: ${data.version}`));
|
|
1907
|
+
console.log(chalk.gray(` Exported at: ${data.exportedAt}`));
|
|
1908
|
+
} catch (error) {
|
|
1909
|
+
spinner.fail(chalk.red('Import failed'));
|
|
1910
|
+
console.error(chalk.red(error.message));
|
|
1911
|
+
process.exit(1);
|
|
1912
|
+
}
|
|
1913
|
+
});
|
|
1914
|
+
|
|
1915
|
+
// =============================================================================
|
|
1916
|
+
// Embed Command - Generate embeddings
|
|
1917
|
+
// =============================================================================
|
|
1918
|
+
|
|
1919
|
+
program
|
|
1920
|
+
.command('embed')
|
|
1921
|
+
.description('Generate embeddings from text')
|
|
1922
|
+
.option('-t, --text <string>', 'Text to embed')
|
|
1923
|
+
.option('-f, --file <path>', 'File containing text (one per line)')
|
|
1924
|
+
.option('-m, --model <name>', 'Embedding model', 'all-minilm-l6-v2')
|
|
1925
|
+
.option('-o, --output <file>', 'Output file for embeddings')
|
|
1926
|
+
.option('--info', 'Show embedding info')
|
|
1927
|
+
.action(async (options) => {
|
|
1928
|
+
console.log(chalk.cyan('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1929
|
+
console.log(chalk.cyan(' RuVector Embed'));
|
|
1930
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1931
|
+
|
|
1932
|
+
if (options.info || (!options.text && !options.file)) {
|
|
1933
|
+
console.log(chalk.cyan(' Generate vector embeddings from text\n'));
|
|
1934
|
+
console.log(chalk.cyan(' Supported Models:'));
|
|
1935
|
+
console.log(chalk.gray(' - all-minilm-l6-v2 (384 dims, fast)'));
|
|
1936
|
+
console.log(chalk.gray(' - nomic-embed-text-v1.5 (768 dims, balanced)'));
|
|
1937
|
+
console.log(chalk.gray(' - openai/text-embedding-3-small (1536 dims, requires API key)'));
|
|
1938
|
+
console.log('');
|
|
1939
|
+
console.log(chalk.cyan(' Status:'), chalk.yellow('Coming Soon'));
|
|
1940
|
+
console.log(chalk.gray(' Built-in embedding generation is planned for future release.'));
|
|
1941
|
+
console.log('');
|
|
1942
|
+
console.log(chalk.cyan(' Current options:'));
|
|
1943
|
+
console.log(chalk.gray(' 1. Use external embedding API (OpenAI, Cohere, etc.)'));
|
|
1944
|
+
console.log(chalk.gray(' 2. Use transformers.js in your application'));
|
|
1945
|
+
console.log(chalk.gray(' 3. Pre-generate embeddings with Python'));
|
|
1946
|
+
console.log('');
|
|
1947
|
+
console.log(chalk.cyan(' Usage (when available):'));
|
|
1948
|
+
console.log(chalk.white(' npx ruvector embed --text "Hello world"'));
|
|
1949
|
+
console.log(chalk.white(' npx ruvector embed --file texts.txt --output embeddings.json'));
|
|
1950
|
+
console.log('');
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
if (options.text) {
|
|
1955
|
+
console.log(chalk.yellow(' Input text:'), chalk.white(options.text.substring(0, 50) + '...'));
|
|
1956
|
+
console.log(chalk.yellow(' Model:'), chalk.white(options.model));
|
|
1957
|
+
console.log('');
|
|
1958
|
+
console.log(chalk.gray(' Embedding generation not yet available in CLI.'));
|
|
1959
|
+
console.log(chalk.gray(' Use the SDK or external embedding services.'));
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
console.log('');
|
|
1963
|
+
});
|
|
1964
|
+
|
|
1965
|
+
// =============================================================================
|
|
1966
|
+
// Demo Command - Interactive tutorial
|
|
1967
|
+
// =============================================================================
|
|
1968
|
+
|
|
1969
|
+
program
|
|
1970
|
+
.command('demo')
|
|
1971
|
+
.description('Run interactive demo and tutorials')
|
|
1972
|
+
.option('--basic', 'Basic vector operations demo')
|
|
1973
|
+
.option('--gnn', 'GNN differentiable search demo')
|
|
1974
|
+
.option('--graph', 'Graph database demo')
|
|
1975
|
+
.option('--benchmark', 'Performance benchmark demo')
|
|
1976
|
+
.option('-i, --interactive', 'Interactive mode')
|
|
1977
|
+
.action(async (options) => {
|
|
1978
|
+
console.log(chalk.cyan('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
1979
|
+
console.log(chalk.cyan(' RuVector Demo'));
|
|
1980
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n'));
|
|
1981
|
+
|
|
1982
|
+
const showMenu = !options.basic && !options.gnn && !options.graph && !options.benchmark;
|
|
1983
|
+
|
|
1984
|
+
if (showMenu) {
|
|
1985
|
+
console.log(chalk.yellow(' Available Demos:\n'));
|
|
1986
|
+
console.log(chalk.white(' --basic '), chalk.gray('Basic vector operations (insert, search, delete)'));
|
|
1987
|
+
console.log(chalk.white(' --gnn '), chalk.gray('GNN differentiable search with gradients'));
|
|
1988
|
+
console.log(chalk.white(' --graph '), chalk.gray('Graph database and Cypher queries'));
|
|
1989
|
+
console.log(chalk.white(' --benchmark '), chalk.gray('Performance benchmark suite'));
|
|
1990
|
+
console.log('');
|
|
1991
|
+
console.log(chalk.cyan(' Run a demo:'));
|
|
1992
|
+
console.log(chalk.white(' npx ruvector demo --basic'));
|
|
1993
|
+
console.log(chalk.white(' npx ruvector demo --gnn'));
|
|
1994
|
+
console.log('');
|
|
1995
|
+
return;
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
if (options.basic) {
|
|
1999
|
+
requireRuvector();
|
|
2000
|
+
console.log(chalk.yellow(' Basic Vector Operations Demo\n'));
|
|
2001
|
+
|
|
2002
|
+
const spinner = ora('Creating demo database...').start();
|
|
2003
|
+
|
|
2004
|
+
try {
|
|
2005
|
+
const db = new VectorDB({ dimension: 4, metric: 'cosine' });
|
|
2006
|
+
|
|
2007
|
+
spinner.text = 'Inserting vectors...';
|
|
2008
|
+
db.insert('vec1', [1.0, 0.0, 0.0, 0.0], { label: 'x-axis' });
|
|
2009
|
+
db.insert('vec2', [0.0, 1.0, 0.0, 0.0], { label: 'y-axis' });
|
|
2010
|
+
db.insert('vec3', [0.0, 0.0, 1.0, 0.0], { label: 'z-axis' });
|
|
2011
|
+
db.insert('vec4', [0.7, 0.7, 0.0, 0.0], { label: 'xy-diagonal' });
|
|
2012
|
+
|
|
2013
|
+
spinner.succeed('Demo database created with 4 vectors');
|
|
2014
|
+
|
|
2015
|
+
console.log(chalk.cyan('\n Vectors inserted:'));
|
|
2016
|
+
console.log(chalk.gray(' vec1: [1,0,0,0] - x-axis'));
|
|
2017
|
+
console.log(chalk.gray(' vec2: [0,1,0,0] - y-axis'));
|
|
2018
|
+
console.log(chalk.gray(' vec3: [0,0,1,0] - z-axis'));
|
|
2019
|
+
console.log(chalk.gray(' vec4: [0.7,0.7,0,0] - xy-diagonal'));
|
|
2020
|
+
|
|
2021
|
+
console.log(chalk.cyan('\n Searching for nearest to [0.8, 0.6, 0, 0]:'));
|
|
2022
|
+
const results = db.search([0.8, 0.6, 0.0, 0.0], 3);
|
|
2023
|
+
results.forEach((r, i) => {
|
|
2024
|
+
console.log(chalk.gray(` ${i + 1}. ${r.id} (score: ${r.score.toFixed(4)})`));
|
|
2025
|
+
});
|
|
2026
|
+
|
|
2027
|
+
console.log(chalk.green('\n Demo complete!'));
|
|
2028
|
+
} catch (error) {
|
|
2029
|
+
spinner.fail(chalk.red('Demo failed'));
|
|
2030
|
+
console.error(chalk.red(error.message));
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
if (options.gnn) {
|
|
2035
|
+
if (!gnnAvailable) {
|
|
2036
|
+
console.log(chalk.yellow(' @ruvector/gnn not installed.'));
|
|
2037
|
+
console.log(chalk.white(' Install with: npm install @ruvector/gnn'));
|
|
2038
|
+
console.log('');
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
console.log(chalk.yellow(' GNN Differentiable Search Demo\n'));
|
|
2043
|
+
|
|
2044
|
+
try {
|
|
2045
|
+
console.log(chalk.cyan(' Running differentiable search with gradients...\n'));
|
|
2046
|
+
|
|
2047
|
+
const queryVec = [1.0, 0.5, 0.3, 0.1];
|
|
2048
|
+
const dbVectors = [
|
|
2049
|
+
[1.0, 0.0, 0.0, 0.0],
|
|
2050
|
+
[0.0, 1.0, 0.0, 0.0],
|
|
2051
|
+
[0.5, 0.5, 0.5, 0.5],
|
|
2052
|
+
[0.9, 0.4, 0.2, 0.1]
|
|
2053
|
+
];
|
|
2054
|
+
|
|
2055
|
+
const result = differentiableSearch(queryVec, dbVectors, 3, 10.0);
|
|
2056
|
+
|
|
2057
|
+
console.log(chalk.cyan(' Query:'), JSON.stringify(queryVec));
|
|
2058
|
+
console.log(chalk.cyan(' Top 3 results:'));
|
|
2059
|
+
result.indices.forEach((idx, i) => {
|
|
2060
|
+
console.log(chalk.gray(` ${i + 1}. Index ${idx} (attention: ${result.attention_weights[i].toFixed(4)})`));
|
|
2061
|
+
});
|
|
2062
|
+
|
|
2063
|
+
console.log(chalk.cyan('\n Gradient flow enabled:'), chalk.green('Yes'));
|
|
2064
|
+
console.log(chalk.gray(' Use for: Neural network training, learned retrieval'));
|
|
2065
|
+
|
|
2066
|
+
console.log(chalk.green('\n GNN demo complete!'));
|
|
2067
|
+
} catch (error) {
|
|
2068
|
+
console.error(chalk.red('GNN demo failed:', error.message));
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
if (options.graph) {
|
|
2073
|
+
console.log(chalk.yellow(' Graph Database Demo\n'));
|
|
2074
|
+
|
|
2075
|
+
let graphNode;
|
|
2076
|
+
try {
|
|
2077
|
+
graphNode = require('@ruvector/graph-node');
|
|
2078
|
+
console.log(chalk.green(' @ruvector/graph-node is available!'));
|
|
2079
|
+
console.log(chalk.gray(' Full graph demo coming soon.'));
|
|
2080
|
+
} catch (e) {
|
|
2081
|
+
console.log(chalk.yellow(' @ruvector/graph-node not installed.'));
|
|
2082
|
+
console.log(chalk.white(' Install with: npm install @ruvector/graph-node'));
|
|
2083
|
+
}
|
|
2084
|
+
console.log('');
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
if (options.benchmark) {
|
|
2088
|
+
console.log(chalk.yellow(' Redirecting to benchmark command...\n'));
|
|
2089
|
+
console.log(chalk.white(' Run: npx ruvector benchmark'));
|
|
2090
|
+
console.log('');
|
|
2091
|
+
}
|
|
2092
|
+
});
|
|
2093
|
+
|
|
2094
|
+
// ============================================
|
|
2095
|
+
// Self-Learning Intelligence Hooks
|
|
2096
|
+
// ============================================
|
|
2097
|
+
|
|
2098
|
+
const INTEL_PATH = path.join(require('os').homedir(), '.ruvector', 'intelligence.json');
|
|
2099
|
+
|
|
2100
|
+
class Intelligence {
|
|
2101
|
+
constructor() {
|
|
2102
|
+
this.data = this.load();
|
|
2103
|
+
this.alpha = 0.1;
|
|
2104
|
+
this.lastEditedFile = null;
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
load() {
|
|
2108
|
+
try {
|
|
2109
|
+
if (fs.existsSync(INTEL_PATH)) {
|
|
2110
|
+
return JSON.parse(fs.readFileSync(INTEL_PATH, 'utf-8'));
|
|
2111
|
+
}
|
|
2112
|
+
} catch {}
|
|
2113
|
+
return {
|
|
2114
|
+
patterns: {},
|
|
2115
|
+
memories: [],
|
|
2116
|
+
trajectories: [],
|
|
2117
|
+
errors: {},
|
|
2118
|
+
file_sequences: [],
|
|
2119
|
+
agents: {},
|
|
2120
|
+
edges: [],
|
|
2121
|
+
stats: { total_patterns: 0, total_memories: 0, total_trajectories: 0, total_errors: 0, session_count: 0, last_session: 0 }
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
save() {
|
|
2126
|
+
const dir = path.dirname(INTEL_PATH);
|
|
2127
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
2128
|
+
fs.writeFileSync(INTEL_PATH, JSON.stringify(this.data, null, 2));
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
now() { return Math.floor(Date.now() / 1000); }
|
|
2132
|
+
|
|
2133
|
+
embed(text) {
|
|
2134
|
+
const embedding = new Array(64).fill(0);
|
|
2135
|
+
for (let i = 0; i < text.length; i++) {
|
|
2136
|
+
const idx = (text.charCodeAt(i) + i * 7) % 64;
|
|
2137
|
+
embedding[idx] += 1.0;
|
|
2138
|
+
}
|
|
2139
|
+
const norm = Math.sqrt(embedding.reduce((a, b) => a + b * b, 0));
|
|
2140
|
+
if (norm > 0) for (let i = 0; i < embedding.length; i++) embedding[i] /= norm;
|
|
2141
|
+
return embedding;
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
similarity(a, b) {
|
|
2145
|
+
if (a.length !== b.length) return 0;
|
|
2146
|
+
const dot = a.reduce((sum, v, i) => sum + v * b[i], 0);
|
|
2147
|
+
const normA = Math.sqrt(a.reduce((sum, v) => sum + v * v, 0));
|
|
2148
|
+
const normB = Math.sqrt(b.reduce((sum, v) => sum + v * v, 0));
|
|
2149
|
+
return normA > 0 && normB > 0 ? dot / (normA * normB) : 0;
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
remember(memoryType, content, metadata = {}) {
|
|
2153
|
+
const id = `mem_${this.now()}`;
|
|
2154
|
+
this.data.memories.push({ id, memory_type: memoryType, content, embedding: this.embed(content), metadata, timestamp: this.now() });
|
|
2155
|
+
if (this.data.memories.length > 5000) this.data.memories.splice(0, 1000);
|
|
2156
|
+
this.data.stats.total_memories = this.data.memories.length;
|
|
2157
|
+
return id;
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
recall(query, topK) {
|
|
2161
|
+
const queryEmbed = this.embed(query);
|
|
2162
|
+
return this.data.memories
|
|
2163
|
+
.map(m => ({ score: this.similarity(queryEmbed, m.embedding), memory: m }))
|
|
2164
|
+
.sort((a, b) => b.score - a.score).slice(0, topK).map(r => r.memory);
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
getQ(state, action) {
|
|
2168
|
+
const key = `${state}|${action}`;
|
|
2169
|
+
return this.data.patterns[key]?.q_value ?? 0;
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
updateQ(state, action, reward) {
|
|
2173
|
+
const key = `${state}|${action}`;
|
|
2174
|
+
if (!this.data.patterns[key]) this.data.patterns[key] = { state, action, q_value: 0, visits: 0, last_update: 0 };
|
|
2175
|
+
const p = this.data.patterns[key];
|
|
2176
|
+
p.q_value = p.q_value + this.alpha * (reward - p.q_value);
|
|
2177
|
+
p.visits++;
|
|
2178
|
+
p.last_update = this.now();
|
|
2179
|
+
this.data.stats.total_patterns = Object.keys(this.data.patterns).length;
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
learn(state, action, outcome, reward) {
|
|
2183
|
+
const id = `traj_${this.now()}`;
|
|
2184
|
+
this.updateQ(state, action, reward);
|
|
2185
|
+
this.data.trajectories.push({ id, state, action, outcome, reward, timestamp: this.now() });
|
|
2186
|
+
if (this.data.trajectories.length > 1000) this.data.trajectories.splice(0, 200);
|
|
2187
|
+
this.data.stats.total_trajectories = this.data.trajectories.length;
|
|
2188
|
+
return id;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
suggest(state, actions) {
|
|
2192
|
+
let bestAction = actions[0] ?? '';
|
|
2193
|
+
let bestQ = -Infinity;
|
|
2194
|
+
for (const action of actions) {
|
|
2195
|
+
const q = this.getQ(state, action);
|
|
2196
|
+
if (q > bestQ) { bestQ = q; bestAction = action; }
|
|
2197
|
+
}
|
|
2198
|
+
return { action: bestAction, confidence: bestQ > 0 ? Math.min(bestQ, 1) : 0 };
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
route(task, file, crateName, operation = 'edit') {
|
|
2202
|
+
const fileType = file ? path.extname(file).slice(1) : 'unknown';
|
|
2203
|
+
const state = `${operation}_${fileType}_in_${crateName ?? 'project'}`;
|
|
2204
|
+
const agentMap = {
|
|
2205
|
+
rs: ['rust-developer', 'coder', 'reviewer', 'tester'],
|
|
2206
|
+
ts: ['typescript-developer', 'coder', 'frontend-dev'],
|
|
2207
|
+
tsx: ['typescript-developer', 'coder', 'frontend-dev'],
|
|
2208
|
+
js: ['coder', 'frontend-dev'],
|
|
2209
|
+
py: ['python-developer', 'coder', 'ml-developer'],
|
|
2210
|
+
md: ['docs-writer', 'coder']
|
|
2211
|
+
};
|
|
2212
|
+
const agents = agentMap[fileType] ?? ['coder', 'reviewer'];
|
|
2213
|
+
const { action, confidence } = this.suggest(state, agents);
|
|
2214
|
+
const reason = confidence > 0.5 ? 'learned from past success' : confidence > 0 ? 'based on patterns' : `default for ${fileType} files`;
|
|
2215
|
+
return { agent: action, confidence, reason };
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
shouldTest(file) {
|
|
2219
|
+
const ext = path.extname(file).slice(1);
|
|
2220
|
+
switch (ext) {
|
|
2221
|
+
case 'rs': {
|
|
2222
|
+
const crateMatch = file.match(/crates\/([^/]+)/);
|
|
2223
|
+
return crateMatch ? { suggest: true, command: `cargo test -p ${crateMatch[1]}` } : { suggest: true, command: 'cargo test' };
|
|
2224
|
+
}
|
|
2225
|
+
case 'ts': case 'tsx': case 'js': case 'jsx': return { suggest: true, command: 'npm test' };
|
|
2226
|
+
case 'py': return { suggest: true, command: 'pytest' };
|
|
2227
|
+
default: return { suggest: false, command: '' };
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
recordFileSequence(fromFile, toFile) {
|
|
2232
|
+
const existing = this.data.file_sequences.find(s => s.from_file === fromFile && s.to_file === toFile);
|
|
2233
|
+
if (existing) existing.count++;
|
|
2234
|
+
else this.data.file_sequences.push({ from_file: fromFile, to_file: toFile, count: 1 });
|
|
2235
|
+
this.lastEditedFile = toFile;
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
suggestNext(file, limit = 3) {
|
|
2239
|
+
return this.data.file_sequences.filter(s => s.from_file === file).sort((a, b) => b.count - a.count).slice(0, limit).map(s => ({ file: s.to_file, score: s.count }));
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
classifyCommand(command) {
|
|
2243
|
+
const cmd = command.toLowerCase();
|
|
2244
|
+
if (cmd.includes('cargo') || cmd.includes('rustc')) return { category: 'rust', subcategory: cmd.includes('test') ? 'test' : 'build', risk: 'low' };
|
|
2245
|
+
if (cmd.includes('npm') || cmd.includes('node')) return { category: 'javascript', subcategory: cmd.includes('test') ? 'test' : 'build', risk: 'low' };
|
|
2246
|
+
if (cmd.includes('git')) return { category: 'git', subcategory: 'vcs', risk: cmd.includes('push') ? 'medium' : 'low' };
|
|
2247
|
+
if (cmd.includes('rm') || cmd.includes('delete')) return { category: 'filesystem', subcategory: 'destructive', risk: 'high' };
|
|
2248
|
+
return { category: 'shell', subcategory: 'general', risk: 'low' };
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
swarmStats() {
|
|
2252
|
+
const agents = Object.keys(this.data.agents).length;
|
|
2253
|
+
const edges = this.data.edges.length;
|
|
2254
|
+
return { agents, edges };
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
stats() { return this.data.stats; }
|
|
2258
|
+
sessionStart() { this.data.stats.session_count++; this.data.stats.last_session = this.now(); }
|
|
2259
|
+
sessionEnd() {
|
|
2260
|
+
const duration = this.now() - this.data.stats.last_session;
|
|
2261
|
+
const actions = this.data.trajectories.filter(t => t.timestamp >= this.data.stats.last_session).length;
|
|
2262
|
+
return { duration, actions };
|
|
2263
|
+
}
|
|
2264
|
+
getLastEditedFile() { return this.lastEditedFile; }
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
// Hooks command group
|
|
2268
|
+
const hooksCmd = program.command('hooks').description('Self-learning intelligence hooks for Claude Code');
|
|
2269
|
+
|
|
2270
|
+
hooksCmd.command('init').description('Initialize hooks in current project').option('--force', 'Force overwrite').action((opts) => {
|
|
2271
|
+
const settingsPath = path.join(process.cwd(), '.claude', 'settings.json');
|
|
2272
|
+
const settingsDir = path.dirname(settingsPath);
|
|
2273
|
+
if (!fs.existsSync(settingsDir)) fs.mkdirSync(settingsDir, { recursive: true });
|
|
2274
|
+
let settings = {};
|
|
2275
|
+
if (fs.existsSync(settingsPath) && !opts.force) {
|
|
2276
|
+
try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')); } catch {}
|
|
2277
|
+
}
|
|
2278
|
+
settings.hooks = settings.hooks || {};
|
|
2279
|
+
settings.hooks.PreToolUse = [
|
|
2280
|
+
{ matcher: 'Edit|Write|MultiEdit', hooks: ['ruvector hooks pre-edit "$TOOL_INPUT_file_path"'] },
|
|
2281
|
+
{ matcher: 'Bash', hooks: ['ruvector hooks pre-command "$TOOL_INPUT_command"'] }
|
|
2282
|
+
];
|
|
2283
|
+
settings.hooks.PostToolUse = [
|
|
2284
|
+
{ matcher: 'Edit|Write|MultiEdit', hooks: ['ruvector hooks post-edit "$TOOL_INPUT_file_path"'] },
|
|
2285
|
+
{ matcher: 'Bash', hooks: ['ruvector hooks post-command "$TOOL_INPUT_command"'] }
|
|
2286
|
+
];
|
|
2287
|
+
settings.hooks.SessionStart = ['ruvector hooks session-start'];
|
|
2288
|
+
settings.hooks.Stop = ['ruvector hooks session-end'];
|
|
2289
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
2290
|
+
console.log(chalk.green('โ
Hooks initialized in .claude/settings.json'));
|
|
2291
|
+
});
|
|
2292
|
+
|
|
2293
|
+
hooksCmd.command('stats').description('Show intelligence statistics').action(() => {
|
|
2294
|
+
const intel = new Intelligence();
|
|
2295
|
+
const stats = intel.stats();
|
|
2296
|
+
const swarm = intel.swarmStats();
|
|
2297
|
+
console.log(chalk.bold.cyan('\n๐ง RuVector Intelligence Stats\n'));
|
|
2298
|
+
console.log(` ${chalk.green(stats.total_patterns)} Q-learning patterns`);
|
|
2299
|
+
console.log(` ${chalk.green(stats.total_memories)} vector memories`);
|
|
2300
|
+
console.log(` ${chalk.green(stats.total_trajectories)} learning trajectories`);
|
|
2301
|
+
console.log(` ${chalk.green(stats.total_errors)} error patterns\n`);
|
|
2302
|
+
console.log(chalk.bold('Swarm Status:'));
|
|
2303
|
+
console.log(` ${chalk.cyan(swarm.agents)} agents registered`);
|
|
2304
|
+
console.log(` ${chalk.cyan(swarm.edges)} coordination edges`);
|
|
2305
|
+
});
|
|
2306
|
+
|
|
2307
|
+
hooksCmd.command('session-start').description('Session start hook').option('--resume', 'Resume previous session').action(() => {
|
|
2308
|
+
const intel = new Intelligence();
|
|
2309
|
+
intel.sessionStart();
|
|
2310
|
+
intel.save();
|
|
2311
|
+
console.log(chalk.bold.cyan('๐ง RuVector Intelligence Layer Active'));
|
|
2312
|
+
console.log('โก Intelligence guides: agent routing, error fixes, file sequences');
|
|
2313
|
+
});
|
|
2314
|
+
|
|
2315
|
+
hooksCmd.command('session-end').description('Session end hook').option('--export-metrics', 'Export metrics').action((opts) => {
|
|
2316
|
+
const intel = new Intelligence();
|
|
2317
|
+
const sessionInfo = intel.sessionEnd();
|
|
2318
|
+
intel.save();
|
|
2319
|
+
console.log('๐ Session ended. Learning data saved.');
|
|
2320
|
+
if (opts.exportMetrics) console.log(JSON.stringify({ duration_seconds: sessionInfo.duration, actions_recorded: sessionInfo.actions }));
|
|
2321
|
+
});
|
|
2322
|
+
|
|
2323
|
+
hooksCmd.command('pre-edit').description('Pre-edit intelligence').argument('<file>', 'File path').action((file) => {
|
|
2324
|
+
const intel = new Intelligence();
|
|
2325
|
+
const fileName = path.basename(file);
|
|
2326
|
+
const crateMatch = file.match(/crates\/([^/]+)/);
|
|
2327
|
+
const crate = crateMatch?.[1];
|
|
2328
|
+
const { agent, confidence, reason } = intel.route(`edit ${fileName}`, file, crate, 'edit');
|
|
2329
|
+
console.log(chalk.bold('๐ง Intelligence Analysis:'));
|
|
2330
|
+
console.log(` ๐ ${chalk.cyan(crate ?? 'project')}/${fileName}`);
|
|
2331
|
+
console.log(` ๐ค Recommended: ${chalk.green.bold(agent)} (${(confidence * 100).toFixed(0)}% confidence)`);
|
|
2332
|
+
if (reason) console.log(` โ ${chalk.dim(reason)}`);
|
|
2333
|
+
const nextFiles = intel.suggestNext(file, 3);
|
|
2334
|
+
if (nextFiles.length > 0) {
|
|
2335
|
+
console.log(' ๐ Likely next files:');
|
|
2336
|
+
nextFiles.forEach(n => console.log(` - ${n.file} (${n.score} edits)`));
|
|
2337
|
+
}
|
|
2338
|
+
});
|
|
2339
|
+
|
|
2340
|
+
hooksCmd.command('post-edit').description('Post-edit learning').argument('<file>', 'File path').option('--success', 'Edit succeeded').option('--error <msg>', 'Error message').action((file, opts) => {
|
|
2341
|
+
const intel = new Intelligence();
|
|
2342
|
+
const success = opts.error ? false : (opts.success ?? true);
|
|
2343
|
+
const ext = path.extname(file).slice(1);
|
|
2344
|
+
const crateMatch = file.match(/crates\/([^/]+)/);
|
|
2345
|
+
const crate = crateMatch?.[1] ?? 'project';
|
|
2346
|
+
const state = `edit_${ext}_in_${crate}`;
|
|
2347
|
+
const lastFile = intel.getLastEditedFile();
|
|
2348
|
+
if (lastFile && lastFile !== file) intel.recordFileSequence(lastFile, file);
|
|
2349
|
+
intel.learn(state, success ? 'successful-edit' : 'failed-edit', success ? 'completed' : 'failed', success ? 1.0 : -0.5);
|
|
2350
|
+
intel.remember('edit', `${success ? 'successful' : 'failed'} edit of ${ext} in ${crate}`);
|
|
2351
|
+
intel.save();
|
|
2352
|
+
console.log(`๐ Learning recorded: ${success ? 'โ
' : 'โ'} ${path.basename(file)}`);
|
|
2353
|
+
const test = intel.shouldTest(file);
|
|
2354
|
+
if (test.suggest) console.log(` ๐งช Consider: ${chalk.cyan(test.command)}`);
|
|
2355
|
+
});
|
|
2356
|
+
|
|
2357
|
+
hooksCmd.command('pre-command').description('Pre-command intelligence').argument('<command...>', 'Command').action((command) => {
|
|
2358
|
+
const intel = new Intelligence();
|
|
2359
|
+
const cmd = command.join(' ');
|
|
2360
|
+
const classification = intel.classifyCommand(cmd);
|
|
2361
|
+
console.log(chalk.bold('๐ง Command Analysis:'));
|
|
2362
|
+
console.log(` ๐ฆ Category: ${chalk.cyan(classification.category)}`);
|
|
2363
|
+
console.log(` ๐ท๏ธ Type: ${classification.subcategory}`);
|
|
2364
|
+
if (classification.risk === 'high') console.log(` โ ๏ธ Risk: ${chalk.red('HIGH')} - Review carefully`);
|
|
2365
|
+
else if (classification.risk === 'medium') console.log(` โก Risk: ${chalk.yellow('MEDIUM')}`);
|
|
2366
|
+
else console.log(` โ
Risk: ${chalk.green('LOW')}`);
|
|
2367
|
+
});
|
|
2368
|
+
|
|
2369
|
+
hooksCmd.command('post-command').description('Post-command learning').argument('<command...>', 'Command').option('--success', 'Success').option('--error <msg>', 'Error message').action((command, opts) => {
|
|
2370
|
+
const intel = new Intelligence();
|
|
2371
|
+
const cmd = command.join(' ');
|
|
2372
|
+
const success = opts.error ? false : (opts.success ?? true);
|
|
2373
|
+
const classification = intel.classifyCommand(cmd);
|
|
2374
|
+
intel.learn(`cmd_${classification.category}_${classification.subcategory}`, success ? 'success' : 'failure', success ? 'completed' : 'failed', success ? 0.8 : -0.3);
|
|
2375
|
+
intel.remember('command', `${cmd} ${success ? 'succeeded' : 'failed'}`);
|
|
2376
|
+
intel.save();
|
|
2377
|
+
console.log(`๐ Command ${success ? 'โ
' : 'โ'} recorded`);
|
|
2378
|
+
});
|
|
2379
|
+
|
|
2380
|
+
hooksCmd.command('route').description('Route task to agent').argument('<task...>', 'Task').option('--file <file>', 'File').option('--crate <crate>', 'Crate').action((task, opts) => {
|
|
2381
|
+
const intel = new Intelligence();
|
|
2382
|
+
const result = intel.route(task.join(' '), opts.file, opts.crate);
|
|
2383
|
+
console.log(JSON.stringify({ task: task.join(' '), recommended: result.agent, confidence: result.confidence, reasoning: result.reason }, null, 2));
|
|
2384
|
+
});
|
|
2385
|
+
|
|
2386
|
+
hooksCmd.command('suggest-context').description('Suggest relevant context').action(() => {
|
|
2387
|
+
const intel = new Intelligence();
|
|
2388
|
+
const stats = intel.stats();
|
|
2389
|
+
console.log(`RuVector Intelligence: ${stats.total_patterns} learned patterns, ${stats.total_errors} error fixes available. Use 'ruvector hooks route' for agent suggestions.`);
|
|
2390
|
+
});
|
|
2391
|
+
|
|
2392
|
+
hooksCmd.command('remember').description('Store in memory').requiredOption('-t, --type <type>', 'Memory type').argument('<content...>', 'Content').action((content, opts) => {
|
|
2393
|
+
const intel = new Intelligence();
|
|
2394
|
+
const id = intel.remember(opts.type, content.join(' '));
|
|
2395
|
+
intel.save();
|
|
2396
|
+
console.log(JSON.stringify({ success: true, id }));
|
|
2397
|
+
});
|
|
2398
|
+
|
|
2399
|
+
hooksCmd.command('recall').description('Search memory').argument('<query...>', 'Query').option('-k, --top-k <n>', 'Results', '5').action((query, opts) => {
|
|
2400
|
+
const intel = new Intelligence();
|
|
2401
|
+
const results = intel.recall(query.join(' '), parseInt(opts.topK));
|
|
2402
|
+
console.log(JSON.stringify({ query: query.join(' '), results: results.map(r => ({ type: r.memory_type, content: r.content.slice(0, 200), timestamp: r.timestamp })) }, null, 2));
|
|
2403
|
+
});
|
|
2404
|
+
|
|
2405
|
+
hooksCmd.command('pre-compact').description('Pre-compact hook').option('--auto', 'Auto mode').action(() => {
|
|
2406
|
+
const intel = new Intelligence();
|
|
2407
|
+
intel.save();
|
|
2408
|
+
console.log('๐๏ธ Pre-compact: State saved');
|
|
2409
|
+
});
|
|
2410
|
+
|
|
2411
|
+
hooksCmd.command('swarm-recommend').description('Recommend agent for task').argument('<task-type>', 'Task type').action((taskType) => {
|
|
2412
|
+
console.log(JSON.stringify({ task_type: taskType, recommended: 'coder', type: 'default', score: 0.8 }));
|
|
2413
|
+
});
|
|
2414
|
+
|
|
2415
|
+
hooksCmd.command('async-agent').description('Async agent hook').option('--action <action>', 'Action').option('--agent-id <id>', 'Agent ID').option('--task <task>', 'Task').action((opts) => {
|
|
2416
|
+
console.log(JSON.stringify({ action: opts.action, agent_id: opts.agentId, status: 'ok' }));
|
|
2417
|
+
});
|
|
2418
|
+
|
|
2419
|
+
hooksCmd.command('lsp-diagnostic').description('LSP diagnostic hook').option('--file <file>', 'File').option('--severity <sev>', 'Severity').option('--message <msg>', 'Message').action((opts) => {
|
|
2420
|
+
console.log(JSON.stringify({ file: opts.file, severity: opts.severity, action: 'logged' }));
|
|
2421
|
+
});
|
|
2422
|
+
|
|
2423
|
+
hooksCmd.command('track-notification').description('Track notification').action(() => {
|
|
2424
|
+
console.log(JSON.stringify({ tracked: true }));
|
|
2425
|
+
});
|
|
2426
|
+
|
|
2427
|
+
program.parse();
|