UNPKG

@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

214 lines (210 loc) 9.86 kB
/** * Sparse Vector Commands * CLI commands for sparse vector operations including BM25, sparsification, and distance calculations */ import chalk from 'chalk'; import ora from 'ora'; export class SparseCommands { static async create(client, options) { const spinner = ora('Creating sparse vector...').start(); try { await client.connect(); const indices = JSON.parse(options.indices); const values = JSON.parse(options.values); const dim = parseInt(options.dim); const result = await client.createSparseVector(indices, values, dim); spinner.succeed(chalk.green('Sparse vector created successfully')); console.log(chalk.bold.blue('\nSparse Vector Details:')); console.log(chalk.gray('-'.repeat(40))); console.log(` ${chalk.green('Indices:')} ${indices.length}`); console.log(` ${chalk.green('Non-zero elements:')} ${values.length}`); console.log(` ${chalk.green('Dimension:')} ${dim}`); console.log(` ${chalk.green('Sparsity:')} ${((1 - values.length / dim) * 100).toFixed(2)}%`); } catch (err) { spinner.fail(chalk.red('Failed to create sparse vector')); console.error(chalk.red(err.message)); } finally { await client.disconnect(); } } static async distance(client, options) { const spinner = ora(`Computing sparse ${options.metric} distance...`).start(); try { await client.connect(); const result = await client.sparseDistance(options.a, options.b, options.metric); spinner.succeed(chalk.green(`Sparse ${options.metric} distance computed`)); console.log(chalk.bold.blue('\nDistance Result:')); console.log(chalk.gray('-'.repeat(40))); console.log(` ${chalk.green('Metric:')} ${options.metric}`); console.log(` ${chalk.green('Distance:')} ${result.toFixed(6)}`); } catch (err) { spinner.fail(chalk.red('Distance computation failed')); console.error(chalk.red(err.message)); } finally { await client.disconnect(); } } static async bm25(client, options) { const spinner = ora('Computing BM25 score...').start(); try { await client.connect(); const k1 = options.k1 ? parseFloat(options.k1) : 1.2; const b = options.b ? parseFloat(options.b) : 0.75; const score = await client.sparseBM25(options.query, options.doc, parseFloat(options.docLen), parseFloat(options.avgDocLen), k1, b); spinner.succeed(chalk.green('BM25 score computed')); console.log(chalk.bold.blue('\nBM25 Result:')); console.log(chalk.gray('-'.repeat(40))); console.log(` ${chalk.green('Score:')} ${score.toFixed(6)}`); console.log(` ${chalk.green('k1:')} ${k1}`); console.log(` ${chalk.green('b:')} ${b}`); console.log(` ${chalk.green('Document Length:')} ${options.docLen}`); console.log(` ${chalk.green('Avg Doc Length:')} ${options.avgDocLen}`); } catch (err) { spinner.fail(chalk.red('BM25 computation failed')); console.error(chalk.red(err.message)); } finally { await client.disconnect(); } } static async topK(client, options) { const spinner = ora('Computing top-k sparse elements...').start(); try { await client.connect(); const result = await client.sparseTopK(options.sparse, parseInt(options.k)); spinner.succeed(chalk.green('Top-k elements computed')); console.log(chalk.bold.blue('\nTop-K Result:')); console.log(chalk.gray('-'.repeat(40))); console.log(` ${chalk.green('Original NNZ:')} ${result.originalNnz}`); console.log(` ${chalk.green('After Top-K:')} ${result.newNnz}`); console.log(` ${chalk.green('Sparse Vector:')} ${result.vector}`); } catch (err) { spinner.fail(chalk.red('Top-k computation failed')); console.error(chalk.red(err.message)); } finally { await client.disconnect(); } } static async prune(client, options) { const spinner = ora('Pruning sparse vector...').start(); try { await client.connect(); const result = await client.sparsePrune(options.sparse, parseFloat(options.threshold)); spinner.succeed(chalk.green('Sparse vector pruned')); console.log(chalk.bold.blue('\nPrune Result:')); console.log(chalk.gray('-'.repeat(40))); console.log(` ${chalk.green('Threshold:')} ${options.threshold}`); console.log(` ${chalk.green('Original NNZ:')} ${result.originalNnz ?? 'N/A'}`); console.log(` ${chalk.green('After Pruning:')} ${result.newNnz ?? 'N/A'}`); console.log(` ${chalk.green('Elements Removed:')} ${(result.originalNnz ?? 0) - (result.newNnz ?? 0)}`); } catch (err) { spinner.fail(chalk.red('Pruning failed')); console.error(chalk.red(err.message)); } finally { await client.disconnect(); } } static async denseToSparse(client, options) { const spinner = ora('Converting dense to sparse...').start(); try { await client.connect(); const dense = JSON.parse(options.dense); const result = await client.denseToSparse(dense); spinner.succeed(chalk.green('Conversion completed')); console.log(chalk.bold.blue('\nConversion Result:')); console.log(chalk.gray('-'.repeat(40))); console.log(` ${chalk.green('Dense Dimension:')} ${dense.length}`); console.log(` ${chalk.green('Non-zero Elements:')} ${result.nnz}`); console.log(` ${chalk.green('Sparsity:')} ${((1 - result.nnz / dense.length) * 100).toFixed(2)}%`); console.log(` ${chalk.green('Sparse Vector:')} ${result.vector}`); } catch (err) { spinner.fail(chalk.red('Conversion failed')); console.error(chalk.red(err.message)); } finally { await client.disconnect(); } } static async sparseToDense(client, sparse) { const spinner = ora('Converting sparse to dense...').start(); try { await client.connect(); const result = await client.sparseToDense(sparse); spinner.succeed(chalk.green('Conversion completed')); console.log(chalk.bold.blue('\nConversion Result:')); console.log(chalk.gray('-'.repeat(40))); console.log(` ${chalk.green('Dense Dimension:')} ${result.length}`); console.log(` ${chalk.green('Non-zero Elements:')} ${result.filter((v) => v !== 0).length}`); // Show first 10 elements const preview = result.slice(0, 10).map((v) => v.toFixed(4)).join(', '); console.log(` ${chalk.green('Preview:')} [${preview}${result.length > 10 ? ', ...' : ''}]`); } catch (err) { spinner.fail(chalk.red('Conversion failed')); console.error(chalk.red(err.message)); } finally { await client.disconnect(); } } static async info(client, sparse) { const spinner = ora('Getting sparse vector info...').start(); try { await client.connect(); const info = await client.sparseInfo(sparse); spinner.stop(); console.log(chalk.bold.blue('\nSparse Vector Info:')); console.log(chalk.gray('-'.repeat(40))); console.log(` ${chalk.green('Dimension:')} ${info.dim}`); console.log(` ${chalk.green('Non-zero Elements (NNZ):')} ${info.nnz}`); console.log(` ${chalk.green('Sparsity:')} ${info.sparsity.toFixed(2)}%`); console.log(` ${chalk.green('L2 Norm:')} ${info.norm.toFixed(6)}`); } catch (err) { spinner.fail(chalk.red('Failed to get info')); console.error(chalk.red(err.message)); } finally { await client.disconnect(); } } static showHelp() { console.log(chalk.bold.blue('\nSparse Vector Operations:')); console.log(chalk.gray('-'.repeat(60))); console.log(` ${chalk.yellow('Format:')} Sparse vectors use the format: '{index:value, index:value, ...}' Example: '{0:0.5, 10:0.3, 100:0.8}' ${chalk.yellow('Distance Metrics:')} ${chalk.green('dot')} - Dot product (inner product) ${chalk.green('cosine')} - Cosine similarity ${chalk.green('euclidean')} - L2 distance ${chalk.green('manhattan')} - L1 distance ${chalk.yellow('BM25 Scoring:')} Used for text search relevance ranking. Parameters: ${chalk.green('k1')} - Term frequency saturation (default: 1.2) ${chalk.green('b')} - Length normalization (default: 0.75) ${chalk.yellow('Commands:')} ${chalk.green('sparse create')} - Create sparse vector from indices/values ${chalk.green('sparse distance')} - Compute distance between sparse vectors ${chalk.green('sparse bm25')} - Compute BM25 relevance score ${chalk.green('sparse top-k')} - Keep only top-k elements by value ${chalk.green('sparse prune')} - Remove elements below threshold ${chalk.green('sparse dense-to-sparse')} - Convert dense to sparse ${chalk.green('sparse sparse-to-dense')} - Convert sparse to dense ${chalk.green('sparse info')} - Get sparse vector statistics `); } } export default SparseCommands;