The SQLite of Vector Search & Episodic Memory for AI Agents in ~120KB. Pure C99, AVX2+FMA, ARM NEON, FASM x64, zero dependencies.
1
stars
15
commits
C
primary language
Sep 11, 2026
updated
Bare-metal C99 · AVX2+FMA · ARM NEON · FASM x64 · Zero Dependencies · ~120 KB
Quickstart • Google Colab • Why NanoVector? • Benchmarks • Architecture • Python API • Ecosystem
Modern AI agents and local LLM pipelines are plagued by vector database bloat:
torch, onnxruntime, pydantic, fastapi, duckdb).NanoVector solves this by delivering exact, sub-millisecond, brute-force SIMD search directly in CPU cache with zero external dependencies.
| Feature | NanoVector ⚡ | ChromaDB 🐢 | FAISS ⚖️ |
|---|---|---|---|
| Distribution Wheel Size | 38 KB (~120 KB unpacked) | ~120 MB+ | ~50 MB+ |
| External Dependencies | 0 (Zero) | 35+ packages | OpenMP, BLAS |
| Python Cold Import Overhead | < 1 ms (3,000x faster) | ~1,850 ms | ~120 ms |
| Search Latency (N=2,000, 384D) | 0.13 ms (7,478 QPS) | 8.2 ms | 0.22 ms |
| Batch Ingestion Throughput | 1,414,000 vectors/sec | ~25,000 vectors/sec | ~400,000 vectors/sec |
| Storage Format | Single file (.nvec) | SQLite + DuckDB dirs | Custom binary |
| Zero-Copy NumPy | Yes (Buffer Protocol) | No (copies memory) | Partial |
| GIL Release during Search | Yes (Py_BEGIN_ALLOW_THREADS) | Partial | Partial |
Install the zero-dependency pre-compiled binary wheel in under 1 second:
pip install nanovector
import nanovector
import numpy as np
# 1. Initialize an index (dim=384 for all-MiniLM-L6-v2, 768 for BERT, 1536 for OpenAI)
index = nanovector.Index(dim=384, metric="cosine")
# 2. Add single embeddings with optional metadata strings
vec = np.random.randn(384).astype(np.float32)
index.add("doc_1", vec, metadata='{"author": "eminsk", "tag": "ai"}')
# 3. Batch addition (Zero-Copy directly from 2D NumPy array)
batch_vecs = np.random.randn(5000, 384).astype(np.float32)
batch_ids = [f"turn_{i}" for i in range(5000)]
batch_metas = [f'{{"turn_id": {i}, "role": "agent"}}' for i in range(5000)]
index.add_batch(batch_ids, batch_vecs, metadatas=batch_metas)
# 4. Search top-k nearest neighbors (returns in ~0.15 ms)
query = np.random.randn(384).astype(np.float32)
results = index.search(query, top_k=5)
for r in results:
print(f"[{r.id}] Score: {r.score:.4f} | Metadata: {r.metadata}")
# 5. Single-file instant persistence (.nvec)
index.save("agent_memory.nvec")
# 6. Instant reload from disk
loaded_index = nanovector.load("agent_memory.nvec")
print(f"Reloaded {len(loaded_index)} vectors in {loaded_index.dim}D")
Give your LLM agents lightning-fast, persistent long-term memory:
import nanovector
import numpy as np
class AgentEpisodicMemory:
def __init__(self, filepath="agent_brain.nvec", dim=384):
self.filepath = filepath
try:
self.index = nanovector.load(filepath)
except Exception:
self.index = nanovector.Index(dim=dim, metric="cosine")
def remember(self, fact_id: str, embedding: np.ndarray, fact_text: str):
self.index.add(fact_id, embedding, metadata=fact_text)
self.index.save(self.filepath)
def recall(self, query_embedding: np.ndarray, top_k=3):
return self.index.search(query_embedding, top_k=top_k)
# Usage in Agent Loop
memory = AgentEpisodicMemory(filepath="agent_brain.nvec")
# Store facts if brain is empty
if len(memory.index) == 0:
memory.remember("mem_1", np.random.randn(384).astype(np.float32), "User prefers Python, C, and FASM.")
memory.remember("mem_2", np.random.randn(384).astype(np.float32), "NanoVector achieves sub-millisecond search.")
memory.remember("mem_3", np.random.randn(384).astype(np.float32), "Episodic memory saves state in single .nvec file.")
query_vec = np.random.randn(384).astype(np.float32)
recalled_facts = memory.recall(query_vec, top_k=3)
for match in recalled_facts:
print(f"Score: {match.score:.4f} -> Memory: {match.metadata}")
Run NanoVector interactively in your browser with zero local setup:
The Interactive Colab Notebook demonstrates:
sentence-transformers embeddings (all-MiniLM-L6-v2)..nvec Brain Persistence: Instant binary save and zero-overhead reload.Real-world benchmarks measured on Intel/AMD x86_64 CPU (AVX2+FMA) using standard 384-dimensional sentence embeddings (all-MiniLM-L6-v2) against NumPy 2.x / OpenBLAS:
| Dataset Size ($N$) | Metric | NanoVector Latency | NanoVector QPS | NumPy Baseline | Speedup |
|---|---|---|---|---|---|
| 500 vectors | Cosine | 0.0347 ms (34.7 µs) | 28,854 QPS | 0.0828 ms | 2.39x faster |
| 2,000 vectors | Cosine | 0.1337 ms (133.7 µs) | 7,478 QPS | 0.1876 ms | 1.40x faster |
| 10,000 vectors | Cosine | 1.4021 ms | 713 QPS | 1.1617 ms | Comparable (1 thread vs multi-core OpenBLAS) |
| 50,000 vectors | Cosine | 6.7479 ms | 148 QPS | 4.8132 ms | Exact 100% Recall |
.nvec file).NanoVector is written in standard C99 with a multi-tiered hardware acceleration pipeline:
┌───────────────────────────────┐
│ Python C-API │
│ (Buffer Protocol / No-GIL) │
└───────────────┬───────────────┘
│
┌───────────────▼───────────────┐
│ NanoVector C99 Core │
│ Top-K In-Place Heap $O(N\log K)$ │
└───────────────┬───────────────┘
│
┌────────────────────────┼────────────────────────┐
│ │ │
┌────────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
│ x86_64 AVX2 │ │ ARM64 NEON │ │ FASM x64 │
│ 256-bit FMA │ │ 128-bit FMA │ │ Bare-Metal ASM │
│ (32 floats/iter)│ │ (16 floats/iter)│ │ (Windows x64) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
src/nanovector_avx2.c):
_mm256_fmadd_ps) eliminates intermediate register spills.src/nanovector_neon.c):
vfmaq_f32 and vaddvq_f32.src/asm/nanovector_x64.asm):
ymm0..ymm5, shadow store handling)..nvec Binary Specification:
NVEC\x01.nanovector.Index(dim: int, metric: str = "cosine", normalize: bool = False)Initializes an embedded vector index.
dim (int): Vector dimensionality (e.g. 384, 768, 1536).metric (str): Distance metric:
"cosine": Cosine similarity ($\frac{u \cdot v}{|u| |v|}$), higher is closer. Range $[-1.0, 1.0]$."dot" or "ip": Inner Product ($u \cdot v$), higher is closer."l2" or "euclidean": Squared Euclidean distance ($\sum (u_i - v_i)^2$), lower is closer.normalize (bool): If True, vectors are automatically L2-normalized upon insertion and search.| Method | Description |
|---|---|
add(id: str, vector: Any, metadata: Optional[str] = None) | Adds a single 1D vector (NumPy array, list, or buffer) with unique ID and optional metadata string. |
add_batch(ids: List[str], vectors: Any, metadatas: Optional[List[str]] = None) | Adds multiple vectors in batch directly from 2D numpy.ndarray (Zero-Copy). Releases GIL. |
search(query: Any, top_k: int = 10) -> List[Match] | Searches Top-$K$ nearest neighbors for query vector. Releases GIL during search. |
save(filepath: str) -> None | Serializes the entire index to a single .nvec binary file on disk. |
load(filepath: str) -> Index | Classmethod / function loading an index from a .nvec file in sub-millisecond time. |
index.dim (int): Dimensionality of indexed vectors.index.count (int) or len(index): Total number of indexed vectors.index.metric (str): Active distance metric.nanovector.version() (str): Library version string (e.g. "0.1.0").nanovector.simd_backend() (str): Active hardware acceleration backend ("AVX2+FMA (x86_64)", "ARM NEON", etc.).nanovector is developed by @eminsk as part of an open-source performance ecosystem:
pip install nanogemm).pip install yfinance-ta-patterns).MIT License. See LICENSE for details.
15 commits
C
64.5%
Python
17.3%
Jupyter Notebook
12.9%
Assembly
5.4%
The SQLite of Vector Search & Episodic Memory for AI Agents in ~120KB. Pure C99, AVX2+FMA, ARM NEON, FASM x64, zero dependencies.
1
stars
15
commits
C
primary language
Sep 11, 2026
updated
Bare-metal C99 · AVX2+FMA · ARM NEON · FASM x64 · Zero Dependencies · ~120 KB
Quickstart • Google Colab • Why NanoVector? • Benchmarks • Architecture • Python API • Ecosystem
Modern AI agents and local LLM pipelines are plagued by vector database bloat:
torch, onnxruntime, pydantic, fastapi, duckdb).NanoVector solves this by delivering exact, sub-millisecond, brute-force SIMD search directly in CPU cache with zero external dependencies.
| Feature | NanoVector ⚡ | ChromaDB 🐢 | FAISS ⚖️ |
|---|---|---|---|
| Distribution Wheel Size | 38 KB (~120 KB unpacked) | ~120 MB+ | ~50 MB+ |
| External Dependencies | 0 (Zero) | 35+ packages | OpenMP, BLAS |
| Python Cold Import Overhead | < 1 ms (3,000x faster) | ~1,850 ms | ~120 ms |
| Search Latency (N=2,000, 384D) | 0.13 ms (7,478 QPS) | 8.2 ms | 0.22 ms |
| Batch Ingestion Throughput | 1,414,000 vectors/sec | ~25,000 vectors/sec | ~400,000 vectors/sec |
| Storage Format | Single file (.nvec) | SQLite + DuckDB dirs | Custom binary |
| Zero-Copy NumPy | Yes (Buffer Protocol) | No (copies memory) | Partial |
| GIL Release during Search | Yes (Py_BEGIN_ALLOW_THREADS) | Partial | Partial |
Install the zero-dependency pre-compiled binary wheel in under 1 second:
pip install nanovector
import nanovector
import numpy as np
# 1. Initialize an index (dim=384 for all-MiniLM-L6-v2, 768 for BERT, 1536 for OpenAI)
index = nanovector.Index(dim=384, metric="cosine")
# 2. Add single embeddings with optional metadata strings
vec = np.random.randn(384).astype(np.float32)
index.add("doc_1", vec, metadata='{"author": "eminsk", "tag": "ai"}')
# 3. Batch addition (Zero-Copy directly from 2D NumPy array)
batch_vecs = np.random.randn(5000, 384).astype(np.float32)
batch_ids = [f"turn_{i}" for i in range(5000)]
batch_metas = [f'{{"turn_id": {i}, "role": "agent"}}' for i in range(5000)]
index.add_batch(batch_ids, batch_vecs, metadatas=batch_metas)
# 4. Search top-k nearest neighbors (returns in ~0.15 ms)
query = np.random.randn(384).astype(np.float32)
results = index.search(query, top_k=5)
for r in results:
print(f"[{r.id}] Score: {r.score:.4f} | Metadata: {r.metadata}")
# 5. Single-file instant persistence (.nvec)
index.save("agent_memory.nvec")
# 6. Instant reload from disk
loaded_index = nanovector.load("agent_memory.nvec")
print(f"Reloaded {len(loaded_index)} vectors in {loaded_index.dim}D")
Give your LLM agents lightning-fast, persistent long-term memory:
import nanovector
import numpy as np
class AgentEpisodicMemory:
def __init__(self, filepath="agent_brain.nvec", dim=384):
self.filepath = filepath
try:
self.index = nanovector.load(filepath)
except Exception:
self.index = nanovector.Index(dim=dim, metric="cosine")
def remember(self, fact_id: str, embedding: np.ndarray, fact_text: str):
self.index.add(fact_id, embedding, metadata=fact_text)
self.index.save(self.filepath)
def recall(self, query_embedding: np.ndarray, top_k=3):
return self.index.search(query_embedding, top_k=top_k)
# Usage in Agent Loop
memory = AgentEpisodicMemory(filepath="agent_brain.nvec")
# Store facts if brain is empty
if len(memory.index) == 0:
memory.remember("mem_1", np.random.randn(384).astype(np.float32), "User prefers Python, C, and FASM.")
memory.remember("mem_2", np.random.randn(384).astype(np.float32), "NanoVector achieves sub-millisecond search.")
memory.remember("mem_3", np.random.randn(384).astype(np.float32), "Episodic memory saves state in single .nvec file.")
query_vec = np.random.randn(384).astype(np.float32)
recalled_facts = memory.recall(query_vec, top_k=3)
for match in recalled_facts:
print(f"Score: {match.score:.4f} -> Memory: {match.metadata}")
Run NanoVector interactively in your browser with zero local setup:
The Interactive Colab Notebook demonstrates:
sentence-transformers embeddings (all-MiniLM-L6-v2)..nvec Brain Persistence: Instant binary save and zero-overhead reload.Real-world benchmarks measured on Intel/AMD x86_64 CPU (AVX2+FMA) using standard 384-dimensional sentence embeddings (all-MiniLM-L6-v2) against NumPy 2.x / OpenBLAS:
| Dataset Size ($N$) | Metric | NanoVector Latency | NanoVector QPS | NumPy Baseline | Speedup |
|---|---|---|---|---|---|
| 500 vectors | Cosine | 0.0347 ms (34.7 µs) | 28,854 QPS | 0.0828 ms | 2.39x faster |
| 2,000 vectors | Cosine | 0.1337 ms (133.7 µs) | 7,478 QPS | 0.1876 ms | 1.40x faster |
| 10,000 vectors | Cosine | 1.4021 ms | 713 QPS | 1.1617 ms | Comparable (1 thread vs multi-core OpenBLAS) |
| 50,000 vectors | Cosine | 6.7479 ms | 148 QPS | 4.8132 ms | Exact 100% Recall |
.nvec file).NanoVector is written in standard C99 with a multi-tiered hardware acceleration pipeline:
┌───────────────────────────────┐
│ Python C-API │
│ (Buffer Protocol / No-GIL) │
└───────────────┬───────────────┘
│
┌───────────────▼───────────────┐
│ NanoVector C99 Core │
│ Top-K In-Place Heap $O(N\log K)$ │
└───────────────┬───────────────┘
│
┌────────────────────────┼────────────────────────┐
│ │ │
┌────────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
│ x86_64 AVX2 │ │ ARM64 NEON │ │ FASM x64 │
│ 256-bit FMA │ │ 128-bit FMA │ │ Bare-Metal ASM │
│ (32 floats/iter)│ │ (16 floats/iter)│ │ (Windows x64) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
src/nanovector_avx2.c):
_mm256_fmadd_ps) eliminates intermediate register spills.src/nanovector_neon.c):
vfmaq_f32 and vaddvq_f32.src/asm/nanovector_x64.asm):
ymm0..ymm5, shadow store handling)..nvec Binary Specification:
NVEC\x01.nanovector.Index(dim: int, metric: str = "cosine", normalize: bool = False)Initializes an embedded vector index.
dim (int): Vector dimensionality (e.g. 384, 768, 1536).metric (str): Distance metric:
"cosine": Cosine similarity ($\frac{u \cdot v}{|u| |v|}$), higher is closer. Range $[-1.0, 1.0]$."dot" or "ip": Inner Product ($u \cdot v$), higher is closer."l2" or "euclidean": Squared Euclidean distance ($\sum (u_i - v_i)^2$), lower is closer.normalize (bool): If True, vectors are automatically L2-normalized upon insertion and search.| Method | Description |
|---|---|
add(id: str, vector: Any, metadata: Optional[str] = None) | Adds a single 1D vector (NumPy array, list, or buffer) with unique ID and optional metadata string. |
add_batch(ids: List[str], vectors: Any, metadatas: Optional[List[str]] = None) | Adds multiple vectors in batch directly from 2D numpy.ndarray (Zero-Copy). Releases GIL. |
search(query: Any, top_k: int = 10) -> List[Match] | Searches Top-$K$ nearest neighbors for query vector. Releases GIL during search. |
save(filepath: str) -> None | Serializes the entire index to a single .nvec binary file on disk. |
load(filepath: str) -> Index | Classmethod / function loading an index from a .nvec file in sub-millisecond time. |
index.dim (int): Dimensionality of indexed vectors.index.count (int) or len(index): Total number of indexed vectors.index.metric (str): Active distance metric.nanovector.version() (str): Library version string (e.g. "0.1.0").nanovector.simd_backend() (str): Active hardware acceleration backend ("AVX2+FMA (x86_64)", "ARM NEON", etc.).nanovector is developed by @eminsk as part of an open-source performance ecosystem:
pip install nanogemm).pip install yfinance-ta-patterns).MIT License. See LICENSE for details.
15 commits
C
64.5%
Python
17.3%
Jupyter Notebook
12.9%
Assembly
5.4%