ruvector 0.2.29 → 0.2.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +91 -63
- package/bin/mcp-server.js +3854 -3854
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -183,7 +183,7 @@ program
|
|
|
183
183
|
.command('insert <database> <file>')
|
|
184
184
|
.description('Insert vectors from JSON file')
|
|
185
185
|
.option('-b, --batch-size <number>', 'Batch size for insertion', '1000')
|
|
186
|
-
.action((dbPath, file, options) => {
|
|
186
|
+
.action(async (dbPath, file, options) => {
|
|
187
187
|
requireRuvector();
|
|
188
188
|
const spinner = ora('Loading database...').start();
|
|
189
189
|
|
|
@@ -195,30 +195,34 @@ program
|
|
|
195
195
|
try { dimension = JSON.parse(fs.readFileSync(metaPath, 'utf8')).dimension || 384; } catch (_) {}
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
const db = new VectorDB({ dimensions: dimension, storagePath: dbPath });
|
|
199
|
-
|
|
200
|
-
if (fs.existsSync(dbPath)) {
|
|
201
|
-
db.load(dbPath);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
198
|
spinner.text = 'Reading vectors...';
|
|
205
199
|
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
206
200
|
const vectors = Array.isArray(data) ? data : [data];
|
|
207
201
|
|
|
202
|
+
// New database: derive dimension from the data and write the sidecar
|
|
203
|
+
// so later stats/search invocations open it correctly (#508).
|
|
204
|
+
if (!fs.existsSync(dbPath) && vectors.length > 0 && Array.isArray(vectors[0].vector)) {
|
|
205
|
+
dimension = vectors[0].vector.length;
|
|
206
|
+
try { fs.writeFileSync(metaPath, JSON.stringify({ dimension }, null, 2)); } catch (_) {}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// The native binding loads/persists through storagePath itself —
|
|
210
|
+
// VectorDB has no load()/save() methods (#508).
|
|
211
|
+
const db = new VectorDB({ dimensions: dimension, storagePath: dbPath });
|
|
212
|
+
|
|
208
213
|
spinner.text = `Inserting ${vectors.length} vectors...`;
|
|
209
214
|
const batchSize = parseInt(options.batchSize);
|
|
210
215
|
|
|
211
216
|
for (let i = 0; i < vectors.length; i += batchSize) {
|
|
212
217
|
const batch = vectors.slice(i, i + batchSize);
|
|
213
|
-
db.insertBatch(batch);
|
|
218
|
+
await db.insertBatch(batch);
|
|
214
219
|
spinner.text = `Inserted ${Math.min(i + batchSize, vectors.length)}/${vectors.length} vectors...`;
|
|
215
220
|
}
|
|
216
221
|
|
|
217
|
-
db.save(dbPath);
|
|
218
222
|
spinner.succeed(chalk.green(`Inserted ${vectors.length} vectors`));
|
|
219
223
|
|
|
220
|
-
const
|
|
221
|
-
console.log(chalk.gray(` Total vectors: ${
|
|
224
|
+
const count = await db.len();
|
|
225
|
+
console.log(chalk.gray(` Total vectors: ${count}`));
|
|
222
226
|
} catch (error) {
|
|
223
227
|
spinner.fail(chalk.red('Failed to insert vectors'));
|
|
224
228
|
console.error(chalk.red(error.message));
|
|
@@ -234,7 +238,7 @@ program
|
|
|
234
238
|
.option('-k, --top-k <number>', 'Number of results', '10')
|
|
235
239
|
.option('-t, --threshold <number>', 'Similarity threshold', '0.0')
|
|
236
240
|
.option('-f, --filter <json>', 'Metadata filter as JSON')
|
|
237
|
-
.action((dbPath, options) => {
|
|
241
|
+
.action(async (dbPath, options) => {
|
|
238
242
|
requireRuvector();
|
|
239
243
|
const spinner = ora('Loading database...').start();
|
|
240
244
|
|
|
@@ -246,8 +250,13 @@ program
|
|
|
246
250
|
try { dimension = JSON.parse(fs.readFileSync(metaPath, 'utf8')).dimension || 384; } catch (_) {}
|
|
247
251
|
}
|
|
248
252
|
|
|
253
|
+
if (!fs.existsSync(dbPath)) {
|
|
254
|
+
spinner.fail(chalk.red(`Database not found: ${dbPath}`));
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// storagePath loads the existing store; VectorDB has no load() (#508).
|
|
249
259
|
const db = new VectorDB({ dimensions: dimension, storagePath: dbPath });
|
|
250
|
-
db.load(dbPath);
|
|
251
260
|
|
|
252
261
|
spinner.text = 'Searching...';
|
|
253
262
|
|
|
@@ -262,7 +271,7 @@ program
|
|
|
262
271
|
query.filter = JSON.parse(options.filter);
|
|
263
272
|
}
|
|
264
273
|
|
|
265
|
-
const results = db.search(query);
|
|
274
|
+
const results = await db.search(query);
|
|
266
275
|
spinner.succeed(chalk.green(`Found ${results.length} results`));
|
|
267
276
|
|
|
268
277
|
console.log(chalk.cyan('\nSearch Results:'));
|
|
@@ -284,35 +293,40 @@ program
|
|
|
284
293
|
program
|
|
285
294
|
.command('stats <database>')
|
|
286
295
|
.description('Show database statistics')
|
|
287
|
-
.action((dbPath) => {
|
|
296
|
+
.action(async (dbPath) => {
|
|
288
297
|
requireRuvector();
|
|
289
298
|
const spinner = ora('Loading database...').start();
|
|
290
299
|
|
|
291
300
|
try {
|
|
292
|
-
// Read dimension from sidecar (avoids JSON-parsing binary redb)
|
|
301
|
+
// Read dimension/metric from sidecar (avoids JSON-parsing binary redb)
|
|
293
302
|
let dimension = 384;
|
|
303
|
+
let metric = 'cosine';
|
|
294
304
|
const metaPath = `${dbPath}.meta.json`;
|
|
295
305
|
if (fs.existsSync(metaPath)) {
|
|
296
|
-
try {
|
|
306
|
+
try {
|
|
307
|
+
const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
|
|
308
|
+
dimension = meta.dimension || dimension;
|
|
309
|
+
metric = meta.metric || metric;
|
|
310
|
+
} catch (_) {}
|
|
297
311
|
}
|
|
298
312
|
|
|
299
|
-
|
|
300
|
-
|
|
313
|
+
if (!fs.existsSync(dbPath)) {
|
|
314
|
+
spinner.fail(chalk.red(`Database not found: ${dbPath}`));
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
301
317
|
|
|
302
|
-
|
|
318
|
+
// storagePath loads the existing store; VectorDB exposes len(),
|
|
319
|
+
// not a stats() aggregate (#508).
|
|
320
|
+
const db = new VectorDB({ dimensions: dimension, storagePath: dbPath });
|
|
321
|
+
const count = await db.len();
|
|
303
322
|
spinner.succeed(chalk.green('Database statistics'));
|
|
304
323
|
|
|
305
324
|
console.log(chalk.cyan('\nDatabase Stats:'));
|
|
306
|
-
console.log(chalk.white(` Vector Count: ${chalk.yellow(
|
|
307
|
-
console.log(chalk.white(` Dimension: ${chalk.yellow(
|
|
308
|
-
console.log(chalk.white(` Metric: ${chalk.yellow(
|
|
325
|
+
console.log(chalk.white(` Vector Count: ${chalk.yellow(count)}`));
|
|
326
|
+
console.log(chalk.white(` Dimension: ${chalk.yellow(dimension)}`));
|
|
327
|
+
console.log(chalk.white(` Metric: ${chalk.yellow(metric)}`));
|
|
309
328
|
console.log(chalk.white(` Implementation: ${chalk.yellow(getImplementationType())}`));
|
|
310
329
|
|
|
311
|
-
if (stats.memoryUsage) {
|
|
312
|
-
const mb = (stats.memoryUsage / (1024 * 1024)).toFixed(2);
|
|
313
|
-
console.log(chalk.white(` Memory Usage: ${chalk.yellow(mb + ' MB')}`));
|
|
314
|
-
}
|
|
315
|
-
|
|
316
330
|
const fileStats = fs.statSync(dbPath);
|
|
317
331
|
const fileMb = (fileStats.size / (1024 * 1024)).toFixed(2);
|
|
318
332
|
console.log(chalk.white(` File Size: ${chalk.yellow(fileMb + ' MB')}`));
|
|
@@ -1920,35 +1934,33 @@ program
|
|
|
1920
1934
|
const spinner = ora('Exporting database...').start();
|
|
1921
1935
|
|
|
1922
1936
|
try {
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
// Load database
|
|
1926
|
-
const db = new VectorDB({ dimension: 384 }); // Will be overwritten by load
|
|
1927
|
-
if (fs.existsSync(dbPath)) {
|
|
1928
|
-
db.load(dbPath);
|
|
1929
|
-
} else {
|
|
1937
|
+
if (!fs.existsSync(dbPath)) {
|
|
1930
1938
|
spinner.fail(chalk.red(`Database not found: ${dbPath}`));
|
|
1931
1939
|
process.exit(1);
|
|
1932
1940
|
}
|
|
1933
1941
|
|
|
1934
|
-
const
|
|
1935
|
-
const data = {
|
|
1936
|
-
version: packageJson.version,
|
|
1937
|
-
exportedAt: new Date().toISOString(),
|
|
1938
|
-
stats: stats,
|
|
1939
|
-
vectors: [] // Would contain actual vector data
|
|
1940
|
-
};
|
|
1942
|
+
const outputFile = options.output || `${dbPath.replace(/\/$/, '')}_export.${options.format}`;
|
|
1941
1943
|
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1944
|
+
// Read dimension/metric from sidecar; storagePath loads the store (#508)
|
|
1945
|
+
let dimension = 384;
|
|
1946
|
+
const metaPath = `${dbPath}.meta.json`;
|
|
1947
|
+
if (fs.existsSync(metaPath)) {
|
|
1948
|
+
try { dimension = JSON.parse(fs.readFileSync(metaPath, 'utf8')).dimension || 384; } catch (_) {}
|
|
1947
1949
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1950
|
+
const db = new VectorDB({ dimensions: dimension, storagePath: dbPath });
|
|
1951
|
+
const count = await db.len();
|
|
1952
|
+
|
|
1953
|
+
// HONESTY: VectorDB has no enumeration API, so vector payloads cannot
|
|
1954
|
+
// be exported yet — only metadata. Refuse to write a file that import
|
|
1955
|
+
// would silently pretend to restore.
|
|
1956
|
+
spinner.fail(chalk.yellow(
|
|
1957
|
+
`Export is not supported yet: the database has ${count} vectors but ` +
|
|
1958
|
+
`the VectorDB API has no enumeration method to read them back out. ` +
|
|
1959
|
+
`The .db file itself is the portable artifact — copy it (with its ` +
|
|
1960
|
+
`.meta.json sidecar) to back up or move the database.`
|
|
1961
|
+
));
|
|
1962
|
+
console.log(chalk.gray(` Requested output: ${outputFile} (not written)`));
|
|
1963
|
+
process.exit(1);
|
|
1952
1964
|
} catch (error) {
|
|
1953
1965
|
spinner.fail(chalk.red('Export failed'));
|
|
1954
1966
|
console.error(chalk.red(error.message));
|
|
@@ -1975,20 +1987,29 @@ program
|
|
|
1975
1987
|
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
1976
1988
|
const dbPath = options.database || file.replace(/_export\.json$/, '');
|
|
1977
1989
|
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1990
|
+
// A plain JSON array of {vector, metadata} entries is importable via
|
|
1991
|
+
// the real API. The old _export.json format never contained vectors,
|
|
1992
|
+
// so importing it would fabricate an empty database (#508).
|
|
1993
|
+
const vectors = Array.isArray(data) ? data : null;
|
|
1994
|
+
if (!vectors || vectors.length === 0 || !vectors[0].vector) {
|
|
1995
|
+
spinner.fail(chalk.yellow(
|
|
1996
|
+
'Import expects a JSON array of {vector, metadata} entries ' +
|
|
1997
|
+
'(the same format `ruvector insert` accepts). Legacy _export.json ' +
|
|
1998
|
+
'files contain no vector data and cannot be restored. To move a ' +
|
|
1999
|
+
'database, copy the .db file and its .meta.json sidecar.'
|
|
2000
|
+
));
|
|
2001
|
+
process.exit(1);
|
|
2002
|
+
}
|
|
1985
2003
|
|
|
1986
|
-
|
|
1987
|
-
|
|
2004
|
+
spinner.text = `Importing ${vectors.length} vectors...`;
|
|
2005
|
+
const dimension = vectors[0].vector.length;
|
|
2006
|
+
const db = new VectorDB({ dimensions: dimension, storagePath: dbPath });
|
|
2007
|
+
await db.insertBatch(vectors);
|
|
2008
|
+
const count = await db.len();
|
|
1988
2009
|
|
|
1989
2010
|
spinner.succeed(chalk.green(`Imported to: ${dbPath}`));
|
|
1990
|
-
console.log(chalk.gray(`
|
|
1991
|
-
console.log(chalk.gray(`
|
|
2011
|
+
console.log(chalk.gray(` Vectors imported: ${vectors.length} (db total: ${count})`));
|
|
2012
|
+
console.log(chalk.gray(` Dimension: ${dimension}`));
|
|
1992
2013
|
} catch (error) {
|
|
1993
2014
|
spinner.fail(chalk.red('Import failed'));
|
|
1994
2015
|
console.error(chalk.red(error.message));
|
|
@@ -2572,7 +2593,11 @@ program
|
|
|
2572
2593
|
const spinner = ora('Creating demo database...').start();
|
|
2573
2594
|
|
|
2574
2595
|
try {
|
|
2575
|
-
|
|
2596
|
+
// Explicit path + sidecar so the stats/search/insert/export commands
|
|
2597
|
+
// can open this database afterwards with the right dimension (#508).
|
|
2598
|
+
const demoPath = './demo.db';
|
|
2599
|
+
const db = new VectorDB({ dimensions: 4, distanceMetric: 'cosine', storagePath: demoPath });
|
|
2600
|
+
fs.writeFileSync(`${demoPath}.meta.json`, JSON.stringify({ dimension: 4, metric: 'cosine' }, null, 2));
|
|
2576
2601
|
|
|
2577
2602
|
spinner.text = 'Inserting vectors...';
|
|
2578
2603
|
// VectorDBWrapper.insert takes a single object: { id?, vector, metadata? }.
|
|
@@ -2597,6 +2622,9 @@ program
|
|
|
2597
2622
|
});
|
|
2598
2623
|
|
|
2599
2624
|
console.log(chalk.green('\n Demo complete!'));
|
|
2625
|
+
console.log(chalk.cyan('\n The database persists at ./demo.db — try:'));
|
|
2626
|
+
console.log(chalk.white(' npx ruvector stats ./demo.db'));
|
|
2627
|
+
console.log(chalk.white(' npx ruvector search ./demo.db --vector "[0.8, 0.6, 0, 0]"'));
|
|
2600
2628
|
} catch (error) {
|
|
2601
2629
|
spinner.fail(chalk.red('Demo failed'));
|
|
2602
2630
|
console.error(chalk.red(error.message));
|