@ruvector/postgres-cli
Version:
Advanced AI vector database CLI for PostgreSQL - pgvector drop-in replacement with 53+ SQL functions, 39 attention mechanisms, GNN layers, hyperbolic embeddings, and self-learning capabilities
984 lines (983 loc) • 39.6 kB
JavaScript
/**
* RuVector PostgreSQL CLI
* Comprehensive command-line interface for the RuVector PostgreSQL extension
*
* Features:
* - Vector operations (dense and sparse)
* - Attention mechanisms (scaled-dot, multi-head, flash)
* - Graph Neural Networks (GCN, GraphSAGE)
* - Graph operations with Cypher queries
* - Self-learning with ReasoningBank
* - Hyperbolic geometry (Poincare, Lorentz)
* - Agent routing (Tiny Dancer)
* - Vector quantization
* - Benchmarking
*/
import { Command } from 'commander';
import chalk from 'chalk';
import { RuVectorClient } from './client.js';
import { VectorCommands } from './commands/vector.js';
import { AttentionCommands } from './commands/attention.js';
import { GnnCommands } from './commands/gnn.js';
import { GraphCommands } from './commands/graph.js';
import { LearningCommands } from './commands/learning.js';
import { BenchmarkCommands } from './commands/benchmark.js';
import { SparseCommands } from './commands/sparse.js';
import { HyperbolicCommands } from './commands/hyperbolic.js';
import { RoutingCommands } from './commands/routing.js';
import { QuantizationCommands } from './commands/quantization.js';
import { InstallCommands } from './commands/install.js';
const program = new Command();
program
.name('ruvector-pg')
.description('RuVector PostgreSQL CLI - Advanced AI Vector Database Extension')
.version('0.2.0')
.option('-c, --connection <string>', 'PostgreSQL connection string', 'postgresql://localhost:5432/ruvector')
.option('-v, --verbose', 'Enable verbose output');
// ============================================================================
// Vector Operations
// ============================================================================
const vector = program.command('vector').description('Dense vector operations');
vector
.command('create <name>')
.description('Create a new vector table')
.option('-d, --dim <number>', 'Vector dimensions', '384')
.option('-i, --index <type>', 'Index type (hnsw, ivfflat)', 'hnsw')
.action(async (name, options) => {
const client = new RuVectorClient(program.opts().connection);
await VectorCommands.create(client, name, options);
});
vector
.command('insert <table>')
.description('Insert vectors into a table')
.option('-f, --file <path>', 'JSON file with vectors')
.option('-t, --text <content>', 'Text to embed')
.action(async (table, options) => {
const client = new RuVectorClient(program.opts().connection);
await VectorCommands.insert(client, table, options);
});
vector
.command('search <table>')
.description('Search for similar vectors')
.option('-q, --query <vector>', 'Query vector as JSON array')
.option('-t, --text <content>', 'Text query to embed and search')
.option('-k, --top-k <number>', 'Number of results', '10')
.option('-m, --metric <type>', 'Distance metric (cosine, l2, ip)', 'cosine')
.action(async (table, options) => {
const client = new RuVectorClient(program.opts().connection);
await VectorCommands.search(client, table, options);
});
vector
.command('distance')
.description('Compute distance between two vectors')
.requiredOption('-a, --a <vector>', 'First vector as JSON array')
.requiredOption('-b, --b <vector>', 'Second vector as JSON array')
.option('-m, --metric <type>', 'Distance metric (cosine, l2, ip)', 'cosine')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await VectorCommands.distance(client, options);
});
vector
.command('normalize')
.description('Normalize a vector to unit length')
.requiredOption('--vector <array>', 'Vector as JSON array')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await VectorCommands.normalize(client, options);
});
// ============================================================================
// Sparse Vector Operations
// ============================================================================
const sparse = program.command('sparse').description('Sparse vector operations');
sparse
.command('create')
.description('Create a sparse vector from indices and values')
.requiredOption('--indices <array>', 'Non-zero indices as JSON array')
.requiredOption('--values <array>', 'Values as JSON array')
.requiredOption('--dim <number>', 'Total dimensionality')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await SparseCommands.create(client, options);
});
sparse
.command('distance')
.description('Compute distance between sparse vectors')
.requiredOption('-a, --a <sparse>', 'First sparse vector')
.requiredOption('-b, --b <sparse>', 'Second sparse vector')
.option('-m, --metric <type>', 'Distance metric (dot, cosine, euclidean, manhattan)', 'cosine')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await SparseCommands.distance(client, options);
});
sparse
.command('bm25')
.description('Compute BM25 relevance score')
.requiredOption('--query <sparse>', 'Query sparse vector (IDF weights)')
.requiredOption('--doc <sparse>', 'Document sparse vector (term frequencies)')
.requiredOption('--doc-len <number>', 'Document length')
.requiredOption('--avg-doc-len <number>', 'Average document length')
.option('--k1 <number>', 'Term frequency saturation', '1.2')
.option('--b <number>', 'Length normalization', '0.75')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await SparseCommands.bm25(client, options);
});
sparse
.command('top-k')
.description('Keep only top-k elements by value')
.requiredOption('-s, --sparse <vector>', 'Sparse vector')
.requiredOption('-k, --k <number>', 'Number of elements to keep')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await SparseCommands.topK(client, options);
});
sparse
.command('prune')
.description('Remove elements below threshold')
.requiredOption('-s, --sparse <vector>', 'Sparse vector')
.requiredOption('--threshold <number>', 'Minimum absolute value threshold')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await SparseCommands.prune(client, options);
});
sparse
.command('dense-to-sparse')
.description('Convert dense vector to sparse')
.requiredOption('-d, --dense <array>', 'Dense vector as JSON array')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await SparseCommands.denseToSparse(client, options);
});
sparse
.command('sparse-to-dense <sparse>')
.description('Convert sparse vector to dense')
.action(async (sparseVec) => {
const client = new RuVectorClient(program.opts().connection);
await SparseCommands.sparseToDense(client, sparseVec);
});
sparse
.command('info <sparse>')
.description('Get sparse vector information')
.action(async (sparseVec) => {
const client = new RuVectorClient(program.opts().connection);
await SparseCommands.info(client, sparseVec);
});
sparse
.command('help')
.description('Show sparse vector help')
.action(() => SparseCommands.showHelp());
// ============================================================================
// Hyperbolic Operations
// ============================================================================
const hyperbolic = program.command('hyperbolic').description('Hyperbolic geometry operations');
hyperbolic
.command('poincare-distance')
.description('Compute Poincare ball distance')
.requiredOption('-a, --a <vector>', 'First vector as JSON array')
.requiredOption('-b, --b <vector>', 'Second vector as JSON array')
.option('--curvature <number>', 'Curvature (negative)', '-1.0')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await HyperbolicCommands.poincareDistance(client, options);
});
hyperbolic
.command('lorentz-distance')
.description('Compute Lorentz/hyperboloid distance')
.requiredOption('-a, --a <vector>', 'First vector as JSON array')
.requiredOption('-b, --b <vector>', 'Second vector as JSON array')
.option('--curvature <number>', 'Curvature (negative)', '-1.0')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await HyperbolicCommands.lorentzDistance(client, options);
});
hyperbolic
.command('mobius-add')
.description('Perform Mobius addition in Poincare ball')
.requiredOption('-a, --a <vector>', 'First vector as JSON array')
.requiredOption('-b, --b <vector>', 'Second vector as JSON array')
.option('--curvature <number>', 'Curvature (negative)', '-1.0')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await HyperbolicCommands.mobiusAdd(client, options);
});
hyperbolic
.command('exp-map')
.description('Exponential map: tangent space to manifold')
.requiredOption('--base <vector>', 'Base point on manifold')
.requiredOption('--tangent <vector>', 'Tangent vector at base')
.option('--curvature <number>', 'Curvature (negative)', '-1.0')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await HyperbolicCommands.expMap(client, options);
});
hyperbolic
.command('log-map')
.description('Logarithmic map: manifold to tangent space')
.requiredOption('--base <vector>', 'Base point on manifold')
.requiredOption('--target <vector>', 'Target point on manifold')
.option('--curvature <number>', 'Curvature (negative)', '-1.0')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await HyperbolicCommands.logMap(client, options);
});
hyperbolic
.command('poincare-to-lorentz')
.description('Convert Poincare to Lorentz coordinates')
.requiredOption('--vector <array>', 'Poincare vector')
.option('--curvature <number>', 'Curvature (negative)', '-1.0')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await HyperbolicCommands.poincareToLorentz(client, options);
});
hyperbolic
.command('lorentz-to-poincare')
.description('Convert Lorentz to Poincare coordinates')
.requiredOption('--vector <array>', 'Lorentz vector')
.option('--curvature <number>', 'Curvature (negative)', '-1.0')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await HyperbolicCommands.lorentzToPoincare(client, options);
});
hyperbolic
.command('minkowski-dot')
.description('Compute Minkowski inner product')
.requiredOption('-a, --a <vector>', 'First vector')
.requiredOption('-b, --b <vector>', 'Second vector')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await HyperbolicCommands.minkowskiDot(client, options.a, options.b);
});
hyperbolic
.command('help')
.description('Show hyperbolic geometry help')
.action(() => HyperbolicCommands.showHelp());
// ============================================================================
// Routing/Agent Operations
// ============================================================================
const routing = program.command('routing').description('Tiny Dancer agent routing');
routing
.command('register')
.description('Register a new agent')
.requiredOption('--name <name>', 'Agent name')
.requiredOption('--type <type>', 'Agent type (llm, embedding, specialized)')
.requiredOption('--capabilities <list>', 'Capabilities (comma-separated)')
.requiredOption('--cost <number>', 'Cost per request in dollars')
.requiredOption('--latency <number>', 'Average latency in ms')
.requiredOption('--quality <number>', 'Quality score (0-1)')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await RoutingCommands.registerAgent(client, options);
});
routing
.command('register-full')
.description('Register agent with full JSON config')
.requiredOption('--config <json>', 'Full agent configuration as JSON')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await RoutingCommands.registerAgentFull(client, options);
});
routing
.command('update')
.description('Update agent metrics after a request')
.requiredOption('--name <name>', 'Agent name')
.requiredOption('--latency <number>', 'Observed latency in ms')
.requiredOption('--success <boolean>', 'Whether request succeeded')
.option('--quality <number>', 'Quality score for this request')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await RoutingCommands.updateMetrics(client, {
...options,
success: options.success === 'true',
});
});
routing
.command('remove <name>')
.description('Remove an agent')
.action(async (name) => {
const client = new RuVectorClient(program.opts().connection);
await RoutingCommands.removeAgent(client, name);
});
routing
.command('set-active <name> <active>')
.description('Enable or disable an agent')
.action(async (name, active) => {
const client = new RuVectorClient(program.opts().connection);
await RoutingCommands.setActive(client, name, active === 'true');
});
routing
.command('route')
.description('Route a request to the best agent')
.requiredOption('--embedding <array>', 'Request embedding as JSON array')
.option('--optimize-for <target>', 'Optimization target (cost, latency, quality, balanced)', 'balanced')
.option('--constraints <json>', 'Routing constraints as JSON')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await RoutingCommands.route(client, options);
});
routing
.command('list')
.description('List all registered agents')
.action(async () => {
const client = new RuVectorClient(program.opts().connection);
await RoutingCommands.listAgents(client);
});
routing
.command('get <name>')
.description('Get detailed agent information')
.action(async (name) => {
const client = new RuVectorClient(program.opts().connection);
await RoutingCommands.getAgent(client, name);
});
routing
.command('find')
.description('Find agents by capability')
.requiredOption('--capability <name>', 'Capability to search for')
.option('--limit <number>', 'Maximum results', '10')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await RoutingCommands.findByCapability(client, options);
});
routing
.command('stats')
.description('Get routing statistics')
.action(async () => {
const client = new RuVectorClient(program.opts().connection);
await RoutingCommands.stats(client);
});
routing
.command('clear')
.description('Clear all agents')
.action(async () => {
const client = new RuVectorClient(program.opts().connection);
await RoutingCommands.clearAgents(client);
});
routing
.command('help')
.description('Show routing help')
.action(() => RoutingCommands.showHelp());
// ============================================================================
// Quantization Operations
// ============================================================================
const quantization = program.command('quantization').description('Vector quantization operations');
quantization.alias('quant');
quantization
.command('binary')
.description('Binary quantize a vector (1-bit per dimension)')
.requiredOption('--vector <array>', 'Vector as JSON array')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await QuantizationCommands.binaryQuantize(client, options);
});
quantization
.command('scalar')
.description('Scalar quantize a vector (8-bit per dimension)')
.requiredOption('--vector <array>', 'Vector as JSON array')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await QuantizationCommands.scalarQuantize(client, options);
});
quantization
.command('compare <vector>')
.description('Compare all quantization methods on a vector')
.action(async (vector) => {
const client = new RuVectorClient(program.opts().connection);
await QuantizationCommands.compare(client, vector);
});
quantization
.command('stats')
.description('Show quantization statistics')
.action(async () => {
const client = new RuVectorClient(program.opts().connection);
await QuantizationCommands.stats(client);
});
quantization
.command('help')
.description('Show quantization help')
.action(() => QuantizationCommands.showHelp());
// ============================================================================
// Attention Operations
// ============================================================================
const attention = program.command('attention').description('Attention mechanism operations');
attention
.command('compute')
.description('Compute attention between vectors')
.option('-q, --query <vector>', 'Query vector')
.option('-k, --keys <vectors>', 'Key vectors (JSON array)')
.option('-v, --values <vectors>', 'Value vectors (JSON array)')
.option('-t, --type <type>', 'Attention type (scaled_dot, multi_head, flash)', 'scaled_dot')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await AttentionCommands.compute(client, options);
});
attention
.command('list-types')
.description('List available attention types')
.action(async () => {
const client = new RuVectorClient(program.opts().connection);
await AttentionCommands.listTypes(client);
});
// ============================================================================
// GNN Operations
// ============================================================================
const gnn = program.command('gnn').description('Graph Neural Network operations');
gnn
.command('create <name>')
.description('Create a GNN layer')
.option('-t, --type <type>', 'GNN type (gcn, graphsage, gat, gin)', 'gcn')
.option('-i, --input-dim <number>', 'Input dimensions', '384')
.option('-o, --output-dim <number>', 'Output dimensions', '128')
.action(async (name, options) => {
const client = new RuVectorClient(program.opts().connection);
await GnnCommands.create(client, name, options);
});
gnn
.command('forward <layer>')
.description('Forward pass through GNN layer')
.option('-f, --features <path>', 'Node features file')
.option('-e, --edges <path>', 'Edge list file')
.action(async (layer, options) => {
const client = new RuVectorClient(program.opts().connection);
await GnnCommands.forward(client, layer, options);
});
// ============================================================================
// Graph Operations
// ============================================================================
const graph = program.command('graph').description('Graph and Cypher operations');
graph
.command('create <name>')
.description('Create a new graph')
.action(async (name) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
await client.createGraph(name);
console.log(chalk.green(`Graph '${name}' created successfully`));
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
graph
.command('query <graphName> <cypher>')
.description('Execute a Cypher query on a graph')
.action(async (graphName, cypher) => {
const client = new RuVectorClient(program.opts().connection);
await GraphCommands.query(client, `${graphName}:${cypher}`);
});
graph
.command('create-node <graphName>')
.description('Create a graph node')
.option('-l, --labels <labels>', 'Node labels (comma-separated)')
.option('-p, --properties <json>', 'Node properties as JSON')
.action(async (graphName, options) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const labels = options.labels ? options.labels.split(',').map((l) => l.trim()) : [];
const properties = options.properties ? JSON.parse(options.properties) : {};
const nodeId = await client.addNode(graphName, labels, properties);
console.log(chalk.green(`Node created with ID: ${nodeId}`));
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
graph
.command('create-edge <graphName>')
.description('Create a graph edge')
.requiredOption('--from <id>', 'Source node ID')
.requiredOption('--to <id>', 'Target node ID')
.requiredOption('--type <type>', 'Edge type/label')
.option('-p, --properties <json>', 'Edge properties as JSON', '{}')
.action(async (graphName, options) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const properties = JSON.parse(options.properties);
const edgeId = await client.addEdge(graphName, parseInt(options.from), parseInt(options.to), options.type, properties);
console.log(chalk.green(`Edge created with ID: ${edgeId}`));
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
graph
.command('shortest-path <graphName>')
.description('Find shortest path between nodes')
.requiredOption('--start <id>', 'Starting node ID')
.requiredOption('--end <id>', 'Ending node ID')
.option('--max-hops <number>', 'Maximum hops', '10')
.action(async (graphName, options) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const path = await client.shortestPath(graphName, parseInt(options.start), parseInt(options.end), parseInt(options.maxHops));
console.log(chalk.bold.blue('\nShortest Path:'));
console.log(` ${chalk.green('Length:')} ${path.length}`);
console.log(` ${chalk.green('Cost:')} ${path.cost}`);
console.log(` ${chalk.green('Nodes:')} ${path.nodes.join(' -> ')}`);
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
graph
.command('stats <graphName>')
.description('Get graph statistics')
.action(async (graphName) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const stats = await client.graphStats(graphName);
console.log(chalk.bold.blue(`\nGraph: ${stats.name}`));
console.log(chalk.gray('-'.repeat(40)));
console.log(` ${chalk.green('Nodes:')} ${stats.node_count}`);
console.log(` ${chalk.green('Edges:')} ${stats.edge_count}`);
console.log(` ${chalk.green('Labels:')} ${stats.labels.join(', ') || 'none'}`);
console.log(` ${chalk.green('Edge Types:')} ${stats.edge_types.join(', ') || 'none'}`);
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
graph
.command('list')
.description('List all graphs')
.action(async () => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const graphs = await client.listGraphs();
if (graphs.length === 0) {
console.log(chalk.yellow('No graphs found'));
}
else {
console.log(chalk.bold.blue(`\nGraphs (${graphs.length}):`));
graphs.forEach(g => console.log(` ${chalk.green('-')} ${g}`));
}
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
graph
.command('delete <graphName>')
.description('Delete a graph')
.action(async (graphName) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
await client.deleteGraph(graphName);
console.log(chalk.green(`Graph '${graphName}' deleted`));
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
graph
.command('traverse')
.description('Traverse the graph (legacy)')
.option('-s, --start <id>', 'Starting node ID')
.option('-d, --depth <number>', 'Max traversal depth', '3')
.option('-t, --type <type>', 'Traversal type (bfs, dfs)', 'bfs')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await GraphCommands.traverse(client, options);
});
// ============================================================================
// Learning Operations
// ============================================================================
const learning = program.command('learning').description('Self-learning and ReasoningBank operations');
learning
.command('enable <table>')
.description('Enable learning for a table')
.option('--max-trajectories <number>', 'Maximum trajectories to track', '1000')
.option('--num-clusters <number>', 'Number of clusters for patterns', '10')
.action(async (table, options) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const config = {
max_trajectories: parseInt(options.maxTrajectories),
num_clusters: parseInt(options.numClusters),
};
const result = await client.enableLearning(table, config);
console.log(chalk.green(result));
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
learning
.command('stats <table>')
.description('Get learning statistics for a table')
.action(async (table) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const stats = await client.learningStats(table);
console.log(chalk.bold.blue('\nLearning Statistics:'));
console.log(chalk.gray('-'.repeat(40)));
console.log(chalk.bold('Trajectories:'));
console.log(` ${chalk.green('Total:')} ${stats.trajectories.total}`);
console.log(` ${chalk.green('With Feedback:')} ${stats.trajectories.with_feedback}`);
console.log(` ${chalk.green('Avg Latency:')} ${stats.trajectories.avg_latency_us}us`);
console.log(` ${chalk.green('Avg Precision:')} ${(stats.trajectories.avg_precision * 100).toFixed(1)}%`);
console.log(` ${chalk.green('Avg Recall:')} ${(stats.trajectories.avg_recall * 100).toFixed(1)}%`);
console.log(chalk.bold('\nPatterns:'));
console.log(` ${chalk.green('Total:')} ${stats.patterns.total}`);
console.log(` ${chalk.green('Samples:')} ${stats.patterns.total_samples}`);
console.log(` ${chalk.green('Avg Confidence:')} ${(stats.patterns.avg_confidence * 100).toFixed(1)}%`);
console.log(` ${chalk.green('Total Usage:')} ${stats.patterns.total_usage}`);
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
learning
.command('auto-tune <table>')
.description('Auto-tune search parameters')
.option('--optimize-for <target>', 'Optimization target (speed, accuracy, balanced)', 'balanced')
.action(async (table, options) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const result = await client.autoTune(table, options.optimizeFor);
console.log(chalk.bold.blue('\nAuto-Tune Results:'));
console.log(JSON.stringify(result, null, 2));
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
learning
.command('extract-patterns <table>')
.description('Extract patterns from trajectories')
.option('--clusters <number>', 'Number of clusters', '10')
.action(async (table, options) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const result = await client.extractPatterns(table, parseInt(options.clusters));
console.log(chalk.green(result));
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
learning
.command('get-params <table>')
.description('Get optimized search parameters for a query')
.requiredOption('--query <vector>', 'Query vector as JSON array')
.action(async (table, options) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const queryVec = JSON.parse(options.query);
const params = await client.getSearchParams(table, queryVec);
console.log(chalk.bold.blue('\nOptimized Parameters:'));
console.log(` ${chalk.green('ef_search:')} ${params.ef_search}`);
console.log(` ${chalk.green('probes:')} ${params.probes}`);
console.log(` ${chalk.green('confidence:')} ${(params.confidence * 100).toFixed(1)}%`);
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
learning
.command('clear <table>')
.description('Clear all learning data for a table')
.action(async (table) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const result = await client.clearLearning(table);
console.log(chalk.green(result));
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
learning
.command('train')
.description('Train from trajectories (legacy)')
.option('-f, --file <path>', 'Trajectory data file')
.option('-e, --epochs <number>', 'Training epochs', '10')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await LearningCommands.train(client, options);
});
learning
.command('predict')
.description('Make a prediction (legacy)')
.option('-i, --input <vector>', 'Input vector')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await LearningCommands.predict(client, options);
});
// ============================================================================
// Benchmark Operations
// ============================================================================
const benchmark = program.command('bench').description('Benchmarking operations');
benchmark
.command('run')
.description('Run comprehensive benchmarks')
.option('-t, --type <type>', 'Benchmark type (vector, attention, gnn, all)', 'all')
.option('-s, --size <number>', 'Dataset size', '10000')
.option('-d, --dim <number>', 'Vector dimensions', '384')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await BenchmarkCommands.run(client, options);
});
benchmark
.command('report')
.description('Generate benchmark report')
.option('-f, --format <type>', 'Output format (json, table, markdown)', 'table')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
await BenchmarkCommands.report(client, options);
});
// ============================================================================
// Info & Utility Commands
// ============================================================================
program
.command('info')
.description('Show extension information and capabilities')
.action(async () => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const info = await client.getExtensionInfo();
console.log(chalk.bold.blue('\nRuVector PostgreSQL Extension'));
console.log(chalk.gray('='.repeat(50)));
console.log(`${chalk.green('Version:')} ${info.version}`);
if (info.simd_info) {
console.log(`${chalk.green('SIMD:')} ${info.simd_info}`);
}
console.log(`\n${chalk.green('Features:')}`);
info.features.forEach(f => console.log(` ${chalk.yellow('*')} ${f}`));
// Get memory stats
try {
const memStats = await client.getMemoryStats();
console.log(`\n${chalk.green('Memory Usage:')}`);
console.log(` Index Memory: ${memStats.index_memory_mb.toFixed(2)} MB`);
console.log(` Vector Cache: ${memStats.vector_cache_mb.toFixed(2)} MB`);
console.log(` Quantization: ${memStats.quantization_tables_mb.toFixed(2)} MB`);
console.log(` ${chalk.bold('Total:')} ${memStats.total_extension_mb.toFixed(2)} MB`);
}
catch {
// Memory stats may not be available
}
console.log();
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
program
.command('extension')
.description('Install/upgrade RuVector extension in existing PostgreSQL')
.option('--upgrade', 'Upgrade existing installation')
.action(async (options) => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
await client.installExtension(options.upgrade);
console.log(chalk.green('RuVector extension installed successfully!'));
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
program
.command('memory')
.description('Show memory statistics')
.action(async () => {
const client = new RuVectorClient(program.opts().connection);
try {
await client.connect();
const stats = await client.getMemoryStats();
console.log(chalk.bold.blue('\nMemory Statistics:'));
console.log(chalk.gray('-'.repeat(40)));
console.log(` ${chalk.green('Index Memory:')} ${stats.index_memory_mb.toFixed(2)} MB`);
console.log(` ${chalk.green('Vector Cache:')} ${stats.vector_cache_mb.toFixed(2)} MB`);
console.log(` ${chalk.green('Quantization:')} ${stats.quantization_tables_mb.toFixed(2)} MB`);
console.log(` ${chalk.bold.green('Total:')} ${stats.total_extension_mb.toFixed(2)} MB`);
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
}
finally {
await client.disconnect();
}
});
// ============================================================================
// Installation & Server Management
// ============================================================================
program
.command('install')
.description('Install RuVector PostgreSQL (Docker or native)')
.option('-m, --method <type>', 'Installation method: docker, native, auto', 'auto')
.option('-p, --port <number>', 'PostgreSQL port', '5432')
.option('-u, --user <name>', 'Database user', 'ruvector')
.option('--password <pass>', 'Database password', 'ruvector')
.option('-d, --database <name>', 'Database name', 'ruvector')
.option('--data-dir <path>', 'Persistent data directory')
.option('--name <name>', 'Container name', 'ruvector-postgres')
.option('--version <version>', 'RuVector version', '0.2.3')
.action(async (options) => {
try {
await InstallCommands.install({
method: options.method,
port: parseInt(options.port),
user: options.user,
password: options.password,
database: options.database,
dataDir: options.dataDir,
name: options.name,
version: options.version,
});
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
process.exit(1);
}
});
program
.command('uninstall')
.description('Uninstall RuVector PostgreSQL')
.option('--name <name>', 'Container name', 'ruvector-postgres')
.option('--remove-data', 'Also remove data volumes')
.action(async (options) => {
try {
await InstallCommands.uninstall({
name: options.name,
removeData: options.removeData,
});
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
process.exit(1);
}
});
program
.command('status')
.description('Show RuVector PostgreSQL installation status')
.option('--name <name>', 'Container name', 'ruvector-postgres')
.action(async (options) => {
try {
await InstallCommands.printStatus({ name: options.name });
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
process.exit(1);
}
});
program
.command('start')
.description('Start RuVector PostgreSQL')
.option('--name <name>', 'Container name', 'ruvector-postgres')
.action(async (options) => {
try {
await InstallCommands.start({ name: options.name });
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
process.exit(1);
}
});
program
.command('stop')
.description('Stop RuVector PostgreSQL')
.option('--name <name>', 'Container name', 'ruvector-postgres')
.action(async (options) => {
try {
await InstallCommands.stop({ name: options.name });
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
process.exit(1);
}
});
program
.command('logs')
.description('Show RuVector PostgreSQL logs')
.option('--name <name>', 'Container name', 'ruvector-postgres')
.option('-f, --follow', 'Follow log output')
.option('-n, --tail <lines>', 'Number of lines to show', '100')
.action(async (options) => {
try {
await InstallCommands.logs({
name: options.name,
follow: options.follow,
tail: parseInt(options.tail),
});
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
process.exit(1);
}
});
program
.command('psql [command]')
.description('Connect to RuVector PostgreSQL or execute SQL')
.option('--name <name>', 'Container name', 'ruvector-postgres')
.action(async (command, options) => {
try {
await InstallCommands.psql({
name: options.name,
command: command,
});
}
catch (err) {
console.error(chalk.red('Error:'), err.message);
process.exit(1);
}
});
program.parse();