cuvs-node
v0.0.6
Published
Opensource Node.js bindings for NVIDIA cuVS - GPU-accelerated vector search and clustering
Maintainers
Readme
cuvs-node
Opensource Node.js bindings for NVIDIA cuVS - GPU-accelerated vector search and clustering.
Build, search, and serialize high-performance vector indexes directly from Node.js using NVIDIA GPUs.
NEW in v0.0.5 — Prebuilt binaries.
npm install cuvs-nodenow downloads a prebuilt addon plus cuVS, CUDA runtime, cuBLAS, cuSOLVER, cuSPARSE, cuRAND, NCCL, and OpenMP. No conda, no separate cuVS install, no compile step. The binary (~1.8 GB compressed) is fetched once from GitHub Releases at install time. See Install below.
Install
General Requirements
- Linux x86_64
- NVIDIA GPU with CUDA 12.x driver installed (
nvidia-smiworks)
2 Ways to install: Prebuilt Binaries OR From Source
Installation method 1: Prebuilt Binaries (recommended)
In addition to the requirements above, you need a recent libstdc++ on your system.
- Ubuntu 24.04, Debian 13, RHEL/Rocky 9, or newer → built-in, nothing to do.
OR
- Ubuntu 22.04, Debian 12 → run
sudo apt install -y libstdc++6
(recent point releases include the needed symbols).
Verify if you have it:
strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep -E 'GLIBCXX_3\.4\.3[1-9]' | head -1
- should print at least
GLIBCXX_3.4.31.
If you do, then proceed to install:
Then:
npm install cuvs-nodeDuring install, npm downloads ~1.8 GB of bundled binaries from this repo's GitHub Releases (cuVS, CUDA runtime, cuBLAS, cuSOLVER, cuSPARSE, cuRAND, NCCL, OpenMP). One-time download per install. No conda, no separate cuVS install, no compilation.
Smoke test:
node -e "const { Resources } = require('cuvs-node'); const r = new Resources(); r.dispose(); console.log('ok')"You should see ok.
### Installation method 2: Build from source
Use this only if the prebuilt binary won't work on your machine — older libstdc++, a build pipeline that disallows binary downloads, or you're hacking on the C++.
Step 1: Install Node.js
ARCH=$(uname -m)
if [ "$ARCH" = "aarch64" ]; then
NODE_ARCH="arm64"
else
NODE_ARCH="x64"
fi
curl -fsSL https://nodejs.org/dist/v20.19.0/node-v20.19.0-linux-${NODE_ARCH}.tar.xz | tar -xJ -C /usr/local --strip-components=1 2>/dev/null || \
curl -fsSL https://nodejs.org/dist/v20.19.0/node-v20.19.0-linux-${NODE_ARCH}.tar.xz | sudo tar -xJ -C /usr/local --strip-components=1
npm install -g node-gyp 2>/dev/null || sudo npm install -g node-gypStep 2: Clone this repo
cd /workspace
git clone https://github.com/638Labs/cuvs-node.git
cd cuvs-nodeStep 3: Run setup
chmod +x scripts/*.sh
./scripts/setup.shThis installs conda, cuVS, builds the native addon, and runs the full test suite. Takes a couple minutes on a typical GPU instance.
When it finishes, you should see:
=================================
Status: ALL TESTS PASSEDSource-build dev workflow
Activate the environment:
source ~/miniforge3/etc/profile.d/conda.sh
conda activate cuvs-dev
export LD_LIBRARY_PATH="$CONDA_PREFIX/lib:$LD_LIBRARY_PATH"Edit code, then build and test:
npm run build
./scripts/verify.shWhen ready to commit, deactivate conda first (conda conflicts with git's SSH):
conda deactivate && conda deactivate
unset LD_LIBRARY_PATH
git add -A && git commit -m "your message" && git pushQuick Example
This example builds a GPU-accelerated nearest neighbor index from 10,000 random vectors with 128 dimensions, searches it with 3 query vectors to find the 10 closest matches for each, then saves and reloads the index from disk.
See examples/basic.js for a runnable example. Run it with:
node examples/basic.jsRebuilding and Re-verifying
After making code changes:
npm run build
./scripts/verify.shAPI
Resources
Wraps GPU handles, CUDA streams, and memory pools. Create one per process, reuse across operations, dispose when done.
new Resources()- allocate cuVS resources on the default GPU.resources.dispose()- free the underlying handles. Idempotent.
CagraIndex
GPU-accelerated approximate nearest neighbor index using the CAGRA (Cuda Anns GRAph) algorithm. Graph-based; best general-purpose ANN on GPU.
CagraIndex.build(resources, dataset, { rows, cols })- build an index from aFloat32Arrayofrows * colsvalues laid out row-major. Returns aCagraIndex.index.search(resources, queries, { rows, cols, k })- search the index with aFloat32Arrayof query vectors. Returns{ indices: Uint32Array, distances: Float32Array }, each of lengthrows * k. Results are sorted ascending by distance per query.index.serialize(resources, path)- write the index to disk.CagraIndex.deserialize(resources, path)- load a previously serialized index. Returns aCagraIndex.index.toHnsw(resources)- convert this CAGRA graph to anHnswIndexfor CPU-side search. Returns anHnswIndex.
IvfFlatIndex
Inverted-file index with flat (uncompressed) lists. Fast to build, exact distances within probed lists, higher memory than IVF-PQ.
IvfFlatIndex.build(resources, dataset, { rows, cols })- build an index from aFloat32Arrayofrows * colsvalues laid out row-major. Returns anIvfFlatIndex.index.search(resources, queries, { rows, cols, k })- search the index with aFloat32Arrayof query vectors. Returns{ indices: Uint32Array, distances: Float32Array }, each of lengthrows * k. Results are sorted ascending by distance per query.index.serialize(resources, path)- write the index to disk.IvfFlatIndex.deserialize(resources, path)- load a previously serialized index. Returns anIvfFlatIndex.
IvfPqIndex
Inverted-file index with product quantization. Lower memory than IVF-Flat, approximate distances via PQ codes, good for very large datasets.
IvfPqIndex.build(resources, dataset, { rows, cols })- build an index from aFloat32Arrayofrows * colsvalues laid out row-major. Returns anIvfPqIndex.index.search(resources, queries, { rows, cols, k })- search the index with aFloat32Arrayof query vectors. Returns{ indices: Uint32Array, distances: Float32Array }, each of lengthrows * k. Results are sorted ascending by distance per query.index.serialize(resources, path)- write the index to disk.IvfPqIndex.deserialize(resources, path)- load a previously serialized index. Returns anIvfPqIndex.
BruteForceIndex
Exact nearest neighbor search via brute-force pairwise distance. Ground-truth baseline and the right choice for small datasets.
BruteForceIndex.build(resources, dataset, { rows, cols })- build an index from aFloat32Arrayofrows * colsvalues laid out row-major. Returns aBruteForceIndex.index.search(resources, queries, { rows, cols, k })- search the index with aFloat32Arrayof query vectors. Returns{ indices: Uint32Array, distances: Float32Array }, each of lengthrows * k. Results are sorted ascending by distance per query.index.serialize(resources, path)- write the index to disk.BruteForceIndex.deserialize(resources, path)- load a previously serialized index. Returns aBruteForceIndex.
HnswIndex
CPU-side HNSW graph, produced by converting a GPU-built CagraIndex. Build on GPU for speed, serve on CPU for cheap deployment.
HnswIndexinstances are created viacagraIndex.toHnsw(resources)orHnswIndex.deserialize(resources, path); there is no standalonebuildmethod.index.search(resources, queries, { rows, cols, k })- search the index with aFloat32Arrayof query vectors. Returns{ indices: Uint32Array, distances: Float32Array }, each of lengthrows * k. Results are sorted ascending by distance per query.index.serialize(resources, path)- write the index to disk.HnswIndex.deserialize(resources, path)- load a previously serialized index. Returns anHnswIndex.
Important: Git and Conda
Conda modifies library paths that can break git's SSH. Always deactivate conda before git operations:
conda deactivate && conda deactivate
unset LD_LIBRARY_PATH
git add -A && git commit -m "your message" && git pushThen reactivate when you need to build or test again:
source ~/miniforge3/etc/profile.d/conda.sh
conda activate cuvs-dev
export LD_LIBRARY_PATH="$CONDA_PREFIX/lib:$LD_LIBRARY_PATH"License
cuvs-node is opensourced under the terms of the Apache-2.0 license
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/638labs/cuvs-node.
Considering the break neck speed of AI (and how some ppl now contribute using AI, so they get to overwhelm us peasants who type with our fingers code and README files the old fashioned way), the evolution of AI projects and opinionated ways to approach data/vectors/search, please remain civil and professional in all interactions.
This project is intended to foster innovation while being a safe, welcoming space for collaboration; contributors are expected to adhere to the Contributor Covenant code of conduct.
