GitHub

Vector Backends

The vector backend is where semantic search's cosine KNN actually runs. RCE Code ships two, both behind one VectorIndex protocol: numpy, the portable zero-dependency default, and sqlite-vec, which runs the same exact search inside SQLite for speed and lower memory at scale.

Both backends compute exact cosine KNN — a brute-force scan over every stored vector, not an approximate index. Because all embeddings are L2-normalized, cosine similarity is a plain dot product, and the two backends return the same neighbours. The choice is purely about where and how that scan executes, not about accuracy. See Embedders for the normalization guarantee.

NumpyVectorIndex (the default)

The default backend stores each embedding as a float32 BLOB in the chunk_vectors table. At query time it loads the vectors for the active model into a numpy matrix and computes matrix @ query_vec, then takes the top scores. It depends on nothing beyond numpy, which RCE Code already requires, so it works everywhere with zero setup.

The trade-off is that the scan happens in Python and materializes every vector in memory per query. For small and medium repositories that is perfectly fast; for very large corpora the per-query allocation and copy start to dominate.

SqliteVecVectorIndex

The sqlite-vec backend stores vectors in a vec0 virtual table and runs the cosine KNN in C, inside SQLite, via a MATCH query. It never materializes every vector in Python, so it is faster and lower-memory at scale. It is still an exact brute-force scan — same neighbours as numpy, just computed closer to the data.

The cost is a dependency: it needs a sqlite3 build that can load extensions, plus the embeddings-sqlite-vec extra. Construction enables the loadable extension and creates the vec0 table on first use; if the extension can't load, it raises rather than silently falling back.

install the sqlite-vec extra
pip install 'rce-code[embeddings-sqlite-vec]'

Choosing a backend

numpy (default)sqlite-vec
DependencyNone beyond numpy (already required)Extension-capable sqlite3 + embeddings-sqlite-vec extra
Where the KNN runsPython / numpy, in-processIn C, inside SQLite
Search semanticsExact cosine KNNExact cosine KNN (identical results)
Memory at query timeLoads all active vectors into a numpy matrixScans in SQLite; no full materialization in Python
When to pick itPortability, simplicity, small/medium reposLarge corpora where speed and memory matter

Selecting the backend

The backend is a single field on CodeConfig, vector_backend, which defaults to "numpy". Set it to "sqlite-vec" and pass the config into CodeEngine.open_or_create.

sqlite_vec_backend.py
from rce.code import CodeEngine
from rce.code.config import CodeConfig
cfg = CodeConfig(
repo_path="/path/to/repo",
vector_backend="sqlite-vec", # default is "numpy"
)
engine = CodeEngine.open_or_create("/path/to/repo", config=cfg)
engine.index()
engine.embed()
for hit in engine.search_semantic("rate limiter", top_k=5):
print(round(hit.score, 3), hit.chunk.file_path, hit.chunk.name)

Only "numpy" and "sqlite-vec" are valid; any other value raises a clear error when the index is opened.

Dimension is fixed at table creation

For the sqlite-vec backend, the embedding dimension is baked into the vec0 table when it is created. If you later switch to an embedder with a different dim, the table is dropped and rebuilt — a destructive re-embed.

Switching backends re-embeds

The two backends store vectors in different tables, so changing vector_backend on an existing index leaves the new backend empty until you re-embed. The next engine.embed() repopulates it from your chunks. Plan a backend switch as a one-time re-embed, not a hot swap.

Related

Embeddings have to exist before either backend can search them — see Embedders for the embedder protocol and the built-in adapters, and the Indexing Pipeline for how chunks (the things you embed) are produced.