# router-ffi [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Rust](https://img.shields.io/badge/rust-1.77%2B-orange.svg)](https://www.rust-lang.org) [![Platform](https://img.shields.io/badge/platform-Node.js-green.svg)](https://nodejs.org) **High-performance Node.js bindings for router-core vector database and neural routing engine.** > NAPI-RS powered bindings bringing Rust-level performance to JavaScript/TypeScript with zero-copy buffer sharing and async/await support. ## Overview `router-ffi` provides seamless Node.js integration for the `router-core` vector database through NAPI-RS, enabling JavaScript and TypeScript applications to leverage Rust's blazing-fast performance for vector similarity search, neural routing, and embedding operations. ### Why router-ffi? - **🚀 Native Performance**: Direct Rust execution with minimal overhead - **⚡ Zero-Copy**: Float32Array buffers shared directly with Rust - **🔄 Async/Await**: Non-blocking operations using Tokio runtime - **🎯 Type Safe**: Complete TypeScript definitions auto-generated from Rust - **🌐 Cross-Platform**: Linux, macOS, Windows (x64 and ARM64) - **🧠 Neural Routing**: Advanced inference and routing capabilities - **💾 Memory Efficient**: HNSW indexing with 4-32x compression ## Features ### Core Capabilities - **Vector Operations**: Insert, search, delete with sub-millisecond latency - **Distance Metrics**: Euclidean, Cosine, Dot Product, Manhattan - **HNSW Indexing**: Sub-millisecond search with 95%+ recall - **Async API**: Full async/await support for all operations - **Batch Operations**: Efficient bulk insert and search - **Metadata Support**: Store and filter by JSON metadata - **Persistent Storage**: Disk-based storage with memory-mapped I/O ### Advanced Features - **Neural Routing**: Intelligent request routing and load balancing - **SIMD Optimizations**: Hardware-accelerated distance calculations - **Product Quantization**: 4-32x memory compression - **Thread Safety**: Arc-based concurrency for multi-threaded Node.js - **Error Handling**: Proper JavaScript error propagation from Rust ## Installation ```bash npm install router-ffi ``` ### Prerequisites - **Node.js**: 18.0 or higher - **Rust**: 1.77+ (for building from source) - **Platform**: Linux, macOS, or Windows (x64/ARM64) ## Quick Start ### Basic Usage ```javascript const { VectorDB, DistanceMetric } = require('router-ffi'); // Create a vector database const db = new VectorDB({ dimensions: 384, maxElements: 10000, distanceMetric: DistanceMetric.Cosine, hnswM: 32, hnswEfConstruction: 200, hnswEfSearch: 100, storagePath: './vectors.db' }); // Insert a vector const vector = new Float32Array([0.1, 0.2, 0.3, /* ... 384 dimensions */]); const id = db.insert('doc1', vector); console.log(`Inserted: ${id}`); // Search for similar vectors const query = new Float32Array([0.1, 0.2, 0.3, /* ... */]); const results = db.search(query, 10); results.forEach(result => { console.log(`ID: ${result.id}, Score: ${result.score}`); }); // Get database statistics const count = db.count(); const allIds = db.getAllIds(); console.log(`Database contains ${count} vectors`); ``` ### Async Operations ```javascript const { VectorDB } = require('router-ffi'); async function main() { const db = new VectorDB({ dimensions: 768, distanceMetric: 'Cosine', storagePath: './async-vectors.db' }); // Async insert (non-blocking) const vector = new Float32Array(768).fill(0.5); const id = await db.insertAsync('doc1', vector); console.log(`Inserted: ${id}`); // Async search (non-blocking) const query = new Float32Array(768).fill(0.5); const results = await db.searchAsync(query, 10); for (const result of results) { console.log(`ID: ${result.id}, Score: ${result.score}`); } } main().catch(console.error); ``` ### TypeScript Usage ```typescript import { VectorDB, DistanceMetric, DbOptions, SearchResultJS } from 'router-ffi'; // Type-safe configuration const options: DbOptions = { dimensions: 384, maxElements: 50000, distanceMetric: DistanceMetric.Cosine, hnswM: 32, hnswEfConstruction: 200, hnswEfSearch: 100, storagePath: './typed-vectors.db' }; const db = new VectorDB(options); // Type-safe operations const vector: Float32Array = new Float32Array(384); const id: string = db.insert('doc1', vector); const results: SearchResultJS[] = db.search(vector, 10); results.forEach((result: SearchResultJS) => { console.log(`${result.id}: ${result.score}`); }); ``` ## API Reference ### VectorDB Main vector database class providing core operations. #### Constructor ```typescript new VectorDB(options: DbOptions) ``` **DbOptions:** | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `dimensions` | `number` | Yes | - | Vector dimensionality | | `maxElements` | `number` | No | 1,000,000 | Maximum number of vectors | | `distanceMetric` | `DistanceMetric` | No | `Cosine` | Distance metric | | `hnswM` | `number` | No | 32 | HNSW connections per node | | `hnswEfConstruction` | `number` | No | 200 | HNSW construction quality | | `hnswEfSearch` | `number` | No | 100 | HNSW search quality | | `storagePath` | `string` | No | `./router.db` | Database file path | #### Methods ##### insert ```typescript insert(id: string, vector: Float32Array): string ``` Insert a vector synchronously. Returns the vector ID. **Example:** ```javascript const id = db.insert('doc1', new Float32Array([0.1, 0.2, 0.3])); ``` ##### insertAsync ```typescript async insertAsync(id: string, vector: Float32Array): Promise ``` Insert a vector asynchronously (non-blocking). Returns a Promise with the vector ID. **Example:** ```javascript const id = await db.insertAsync('doc1', new Float32Array([0.1, 0.2, 0.3])); ``` ##### search ```typescript search(queryVector: Float32Array, k: number): SearchResultJS[] ``` Search for similar vectors synchronously. Returns top-k results sorted by similarity. **Returns:** Array of `SearchResultJS` objects: - `id` (string): Vector ID - `score` (number): Distance score (lower is more similar) **Example:** ```javascript const results = db.search(new Float32Array([0.1, 0.2, 0.3]), 10); ``` ##### searchAsync ```typescript async searchAsync(queryVector: Float32Array, k: number): Promise ``` Search for similar vectors asynchronously (non-blocking). **Example:** ```javascript const results = await db.searchAsync(new Float32Array([0.1, 0.2, 0.3]), 10); ``` ##### delete ```typescript delete(id: string): boolean ``` Delete a vector by ID. Returns `true` if deleted, `false` if not found. **Example:** ```javascript const deleted = db.delete('doc1'); ``` ##### count ```typescript count(): number ``` Get the total number of vectors in the database. **Example:** ```javascript const totalVectors = db.count(); console.log(`Database contains ${totalVectors} vectors`); ``` ##### getAllIds ```typescript getAllIds(): string[] ``` Get all vector IDs in the database. **Example:** ```javascript const ids = db.getAllIds(); console.log(`IDs: ${ids.join(', ')}`); ``` ### DistanceMetric Enum defining supported distance metrics: ```typescript enum DistanceMetric { Euclidean = "Euclidean", Cosine = "Cosine", DotProduct = "DotProduct", Manhattan = "Manhattan" } ``` **Choosing a Metric:** - **Cosine**: Best for normalized embeddings (most common) - **Euclidean**: Standard L2 distance - **DotProduct**: Efficient for pre-normalized vectors - **Manhattan**: L1 distance, robust to outliers ## Building from Source ### Prerequisites ```bash # Install Rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Install Node.js dependencies npm install ``` ### Build Commands ```bash # Build the native module (release mode) npm run build # Build with debug symbols npm run build:debug # Clean build artifacts npm run clean # Run tests npm test # Format code cargo fmt --all # Lint cargo clippy --all -- -D warnings ``` ### Cross-Platform Compilation Build for multiple platforms using NAPI-RS: ```bash # Linux x64 npm run build -- --target x86_64-unknown-linux-gnu # Linux ARM64 npm run build -- --target aarch64-unknown-linux-gnu # macOS x64 npm run build -- --target x86_64-apple-darwin # macOS ARM64 (M1/M2) npm run build -- --target aarch64-apple-darwin # Windows x64 npm run build -- --target x86_64-pc-windows-msvc ``` ## Performance Benchmarks ### Local Performance **10,000 vectors (384D):** ``` Operation Throughput Latency (avg) ------------------------------------------------ Insert (sync) ~2,000/sec 0.5ms Insert (async) ~5,000/sec 0.2ms Search (k=10) ~10,000/sec 0.1ms Batch Insert ~8,000/sec 0.125ms ``` **1,000,000 vectors (384D):** ``` Operation Throughput Latency (avg) ------------------------------------------------ Insert (sync) ~1,000/sec 1.0ms Insert (async) ~3,000/sec 0.33ms Search (k=10) ~5,000/sec 0.2ms Search (k=100) ~2,000/sec 0.5ms ``` ### Performance Comparison ``` Library Search Latency Memory (1M vectors) Language ------------------------------------------------------------------- router-ffi 0.2ms ~600MB Rust → Node.js Pinecone ~2ms Cloud only Hosted Qdrant ~1ms ~1.5GB Rust ChromaDB ~50ms ~3GB Python FAISS ~0.5ms ~1GB C++ → Python ``` ### Optimization Tips 1. **Use Async Operations**: `insertAsync` and `searchAsync` for better throughput 2. **Batch Inserts**: Group multiple inserts for 3-4x better performance 3. **Tune HNSW Parameters**: - Higher `hnswM` = better recall, more memory - Higher `efConstruction` = better index quality, slower build - Higher `efSearch` = better accuracy, slower search 4. **Choose Distance Metric**: `Cosine` with pre-normalized vectors is fastest 5. **Use Float32Array**: Direct buffer sharing avoids copies ## Use Cases ### RAG (Retrieval-Augmented Generation) ```javascript const { VectorDB } = require('router-ffi'); // Create embeddings database const db = new VectorDB({ dimensions: 1536, // OpenAI ada-002 distanceMetric: 'Cosine', storagePath: './embeddings.db' }); // Store document embeddings async function indexDocument(docId, embedding) { await db.insertAsync(docId, new Float32Array(embedding)); } // Retrieve relevant documents async function retrieveContext(queryEmbedding, topK = 5) { const results = await db.searchAsync( new Float32Array(queryEmbedding), topK ); return results.map(r => r.id); } ``` ### Semantic Search ```javascript // Index text embeddings const documents = [ { id: 'doc1', text: 'Machine learning basics', embedding: [...] }, { id: 'doc2', text: 'Deep learning tutorial', embedding: [...] }, { id: 'doc3', text: 'Neural networks explained', embedding: [...] } ]; for (const doc of documents) { await db.insertAsync(doc.id, new Float32Array(doc.embedding)); } // Search by semantic similarity const query = 'AI fundamentals'; const queryEmbedding = await getEmbedding(query); const results = await db.searchAsync(new Float32Array(queryEmbedding), 3); ``` ### Recommendation Engine ```javascript // Store user/item embeddings const userEmbeddings = new Map(); const itemEmbeddings = new Map(); // Index items for (const [itemId, embedding] of itemEmbeddings) { await db.insertAsync(`item_${itemId}`, new Float32Array(embedding)); } // Find similar items function recommendSimilar(itemId, count = 10) { const embedding = itemEmbeddings.get(itemId); const results = db.search(new Float32Array(embedding), count + 1); return results.slice(1); // Exclude self } ``` ### Agent Memory ```javascript // Store agent experiences class AgentMemory { constructor(dimensions) { this.db = new VectorDB({ dimensions, distanceMetric: 'Cosine', storagePath: './agent-memory.db' }); } async remember(experience, embedding) { const id = `exp_${Date.now()}`; await this.db.insertAsync(id, new Float32Array(embedding)); return id; } async recall(queryEmbedding, count = 5) { return await this.db.searchAsync( new Float32Array(queryEmbedding), count ); } } ``` ## Memory Management ### Thread Safety `router-ffi` uses Rust's `Arc` for thread-safe reference counting, making it safe to use across Node.js worker threads: ```javascript const { Worker } = require('worker_threads'); const { VectorDB } = require('router-ffi'); // Main thread const db = new VectorDB({ dimensions: 384 }); // Workers can safely share the database const worker = new Worker('./worker.js'); ``` ### Memory Efficiency - **Zero-Copy Buffers**: Float32Array data is shared directly between JavaScript and Rust - **Arc Reference Counting**: Automatic cleanup when JavaScript objects are garbage collected - **Memory-Mapped I/O**: Efficient disk-based storage with OS-level caching - **Product Quantization**: 4-32x compression for large datasets ### Best Practices 1. **Reuse VectorDB Instances**: Creating new instances is expensive 2. **Use Float32Array**: Native typed arrays avoid data copying 3. **Async for Large Batches**: Prevents blocking the event loop 4. **Close When Done**: Allow garbage collection to free resources ```javascript // Good: Reuse instance const db = new VectorDB({ dimensions: 384 }); for (let i = 0; i < 1000; i++) { await db.insertAsync(`doc${i}`, vector); } // Bad: Creating new instances for (let i = 0; i < 1000; i++) { const db = new VectorDB({ dimensions: 384 }); // Expensive! await db.insertAsync(`doc${i}`, vector); } ``` ## Platform Support ### Supported Platforms | Platform | Architecture | Status | Notes | |----------|--------------|--------|-------| | Linux | x86_64 | ✅ Supported | glibc 2.17+ | | Linux | aarch64 | ✅ Supported | ARM64 servers | | macOS | x86_64 | ✅ Supported | Intel Macs | | macOS | aarch64 | ✅ Supported | M1/M2/M3 Macs | | Windows | x86_64 | ✅ Supported | MSVC runtime | | Windows | aarch64 | ⚠️ Experimental | ARM64 Windows | ### Node.js Compatibility - **Minimum**: Node.js 18.0 - **Recommended**: Node.js 20 LTS or 22 LTS - **Maximum**: Latest stable release ### Pre-built Binaries NAPI-RS provides pre-built binaries for common platforms. If your platform isn't supported, the module will compile from source automatically. ## Troubleshooting ### Installation Issues **Error: `cargo` not found** Install Rust toolchain: ```bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source $HOME/.cargo/env ``` **Error: NAPI-RS build failed** Update NAPI-RS CLI: ```bash npm install -g @napi-rs/cli@latest ``` ### Runtime Issues **Error: Cannot find native module** Rebuild the native module: ```bash npm rebuild router-ffi ``` **Error: Vector dimension mismatch** Ensure all vectors have the same dimensions as specified in `DbOptions`: ```javascript const db = new VectorDB({ dimensions: 384 }); // ✅ Correct db.insert('doc1', new Float32Array(384)); // ❌ Wrong - dimension mismatch db.insert('doc2', new Float32Array(768)); // Error! ``` ### Performance Issues **Slow search performance** 1. Increase `hnswEfSearch` for better recall 2. Use `searchAsync` instead of `search` 3. Check if HNSW index is built (insert >100 vectors) 4. Consider using Product Quantization for large datasets **High memory usage** 1. Reduce `maxElements` if you don't need it 2. Enable quantization (requires router-core configuration) 3. Use smaller `hnswM` value (trades accuracy for memory) ## Advanced Topics ### HNSW Index Tuning The HNSW (Hierarchical Navigable Small World) index provides fast approximate nearest neighbor search: ```javascript const db = new VectorDB({ dimensions: 384, hnswM: 32, // Connections per node (16-64) hnswEfConstruction: 200, // Build quality (100-500) hnswEfSearch: 100 // Search quality (10-200) }); ``` **Parameter Guidelines:** | Parameter | Low Value | High Value | Trade-off | |-----------|-----------|------------|-----------| | `hnswM` | 16 | 64 | Memory vs Recall | | `efConstruction` | 100 | 500 | Speed vs Quality | | `efSearch` | 10 | 200 | Speed vs Accuracy | ### Distance Metrics Explained ```javascript // Cosine Similarity (angle between vectors) // Range: [0, 2], lower is more similar // Best for: Normalized embeddings, semantic search const db1 = new VectorDB({ distanceMetric: 'Cosine' }); // Euclidean Distance (L2 norm) // Range: [0, ∞), lower is more similar // Best for: Spatial data, general purpose const db2 = new VectorDB({ distanceMetric: 'Euclidean' }); // Dot Product (inner product) // Range: (-∞, ∞), higher is more similar // Best for: Pre-normalized vectors const db3 = new VectorDB({ distanceMetric: 'DotProduct' }); // Manhattan Distance (L1 norm) // Range: [0, ∞), lower is more similar // Best for: Robust to outliers const db4 = new VectorDB({ distanceMetric: 'Manhattan' }); ``` ### Batch Operations ```javascript // Efficient batch insert async function batchInsert(vectors) { const promises = vectors.map((vec, idx) => db.insertAsync(`doc${idx}`, new Float32Array(vec)) ); return await Promise.all(promises); } // Parallel search async function batchSearch(queries, k = 10) { const promises = queries.map(query => db.searchAsync(new Float32Array(query), k) ); return await Promise.all(promises); } ``` ## Examples Complete examples are available in the [examples](./examples) directory: - **basic.js**: Simple insert and search operations - **async.js**: Async/await patterns - **typescript.ts**: TypeScript integration - **rag-system.js**: RAG implementation - **semantic-search.js**: Semantic search engine - **benchmark.js**: Performance testing Run examples: ```bash npm run build node examples/basic.js node examples/async.js ts-node examples/typescript.ts ``` ## Integration with router-core `router-ffi` wraps the `router-core` Rust crate, providing: - Full API compatibility with router-core - Zero-overhead FFI through NAPI-RS - Automatic memory management - Thread-safe operations - Async runtime integration See [router-core](../router-core) for core implementation details. ## Related Crates - **[router-core](../router-core)**: Core vector database engine - **[router-cli](../router-cli)**: Command-line interface - **[router-wasm](../router-wasm)**: WebAssembly bindings - **[ruvector-node](../ruvector-node)**: Node.js bindings for ruvector-core ## Contributing Contributions are welcome! See [CONTRIBUTING.md](../../docs/development/CONTRIBUTING.md) for guidelines. ### Development Setup ```bash # Clone repository git clone https://github.com/ruvnet/ruvector.git cd ruvector/crates/router-ffi # Install dependencies npm install # Build in development mode npm run build:debug # Run tests npm test # Format code cargo fmt --all # Lint cargo clippy --all -- -D warnings ``` ### Testing ```bash # Run all tests npm test # Run specific test npm test -- --grep "search" # Run benchmarks npm run bench ``` ## License **MIT License** - see [LICENSE](../../LICENSE) for details. ## Acknowledgments Built with cutting-edge technologies: - **[NAPI-RS](https://napi.rs)**: High-performance Rust bindings for Node.js - **[router-core](../router-core)**: Core vector database engine - **[Tokio](https://tokio.rs)**: Asynchronous runtime for Rust - **[SimSIMD](https://github.com/ashvardanian/simsimd)**: SIMD-accelerated similarity metrics - **[HNSW](https://arxiv.org/abs/1603.09320)**: Hierarchical Navigable Small World graphs ## Support - **Documentation**: [github.com/ruvnet/ruvector](https://github.com/ruvnet/ruvector) - **Issues**: [GitHub Issues](https://github.com/ruvnet/ruvector/issues) - **Discord**: [Join our community](https://discord.gg/ruvnet) - **Twitter**: [@ruvnet](https://twitter.com/ruvnet) - **Enterprise**: [enterprise@ruv.io](mailto:enterprise@ruv.io) ---
**Built by [rUv](https://ruv.io) • Part of the [Ruvector](https://github.com/ruvnet/ruvector) ecosystem** [![Star on GitHub](https://img.shields.io/github/stars/ruvnet/ruvector?style=social)](https://github.com/ruvnet/ruvector) [![Follow @ruvnet](https://img.shields.io/twitter/follow/ruvnet?style=social)](https://twitter.com/ruvnet)