A schema-first SQLite ORM with built-in vector embeddings and hybrid search
Project description
VecLite
Local‑first SQLite + vectors for agentic RAG — zero infra.
VecLite isn't a traditional ORM adapted for search — it's purpose‑built for retrieval. It combines relational filters, BM25, vectors, hybrid fusion, and optional reranking in one lightweight library that runs entirely on your machine.
Why VecLite exists:
- ⚡ Local‑first, zero infra — SQLite + vectors; no services to run
- 🧰 No plumbing — embeddings + indices managed automatically
- 🧩 Relational + views — enrich chunks without denormalization
- 🔎 Three search modes — BM25, vector, hybrid (+ optional rerank modifier)
- 📊 Multiple vector fields — embed different columns per table (title vs body)
- 🔗 Simple filters — eq/in_/between/JSON contains/ilike/regex
- 🗂️ No metadata limits — normal SQL columns/JSON, any shape
- 🧱 Vector + non‑vector tables — mix FTS‑only and regular tables alongside vectors
Comparison
| Capability | VecLite (SQLite + Vectors) | Typical Managed Vector DBs |
|---|---|---|
| Setup | Local‑first, zero infra | Hosted service / cluster |
| Data model | Relational tables + views + JSON | Vector‑centric, document/NoSQL |
| Multiple vector fields | Yes (title + body on same row) | No (1 vector per record) |
| Non‑vector queries | Yes (filters, ranges, regex, FTS) | Limited (metadata filters only) |
| Joins / views | Yes (via SQL views) | No |
| Keyword search | Yes (BM25 via FTS5) | Rare / not native |
| Vector search | Yes (cosine) | Yes |
| Hybrid search | Yes (vector + BM25) | Varies; often custom |
| Rerank | Optional modifier | External / varies |
| CRUD | Full insert/update/upsert/delete | Insert/update; deletes vary |
| Metadata limits | No (normal SQL columns/JSON) | Often constrained (field count/size) |
| Record size limits | No hard per‑row payload limit | Common (~40KB metadata payloads) |
| Non‑vector tables | Yes (regular/FTS‑only tables) | No (vector‑centric indexes) |
| Storage | One folder: sqlite.db + vectors/ | Remote indexes/storage |
| Best for | Local RAG, agents, notebooks | Production scale, multi‑tenant APIs |
VecLite is not a production, multi‑tenant vector service — it’s a compact, relational RAG database you can fully own and query normally.
Note: Many managed vector services impose strict metadata payload limits (e.g., ~40KB per record) and center the data model around vector indexes; VecLite lets you store any shape/size of metadata and create tables without vectors at all.
Use VecLite for: Local‑first RAG, agentic retrieval, Jupyter notebooks, desktop/edge apps Not for: High‑traffic multi‑tenant servers; drop‑in vector store adapters (integrate via custom retrievers instead)
Quick Start
Build a semantic search system in 5 lines:
pip install veclite[voyage] # Includes Voyage AI embeddings
export VOYAGE_API_KEY="your-key" # Get from https://www.voyageai.com
from veclite import Client, Schema
from veclite.schema import Table, Integer, Text
# Define schema
class Document(Table):
__tablename__ = "documents"
id = Integer(primary_key=True)
content = Text(vector=True, fts=True)
# Create database
schema = Schema()
schema.add_table(Document)
client = Client.create(schema, "rag_db")
# Insert data
client.table("documents").insert([
{"content": "Python is a programming language"},
{"content": "Machine learning uses neural networks"},
{"content": "The Solar System has 8 planets"},
]).execute()
# Search by meaning
results = client.table("documents").vector_search(
query="AI and deep learning",
topk=5
).execute()
That's it. No embedding pipelines, no vector databases, no infrastructure.
Search Modes (+ optional rerank modifier)
VecLite provides the complete retrieval stack - perfect for agentic RAG systems that need different search strategies:
1. 🔍 Keyword Search (BM25)
Traditional full-text search with production-grade BM25 ranking:
results = client.table("docs").keyword_search(
query="machine learning transformers",
topk=10
).execute()
Use when: Exact term matching matters (product codes, names, technical terms)
2. 🎯 Vector Search (Semantic)
Find by meaning, not just keywords:
results = client.table("docs").vector_search(
query="AI tutorials for beginners", # Matches "ML guides for newcomers"
topk=10
).execute()
Use when: Semantic similarity matters more than exact terms
3. 🚀 Hybrid Search (Best of Both)
Combines keyword + vector with Reciprocal Rank Fusion:
results = client.table("docs").hybrid_search(
query="transformer architecture",
alpha=0.7, # 70% semantic, 30% keyword
topk=10
).execute()
Use when: You want both precision (keywords) and recall (semantics) Perfect for: General-purpose RAG retrieval
🎖️ Rerank Modifier (optional)
Post-retrieval modifier to refine candidates:
# Chain rerank directly on query builder
results = client.table("docs") \
.hybrid_search("quantum computing", topk=100) \
.rerank(query="quantum computing applications", content_column="content", topk=10) \
.execute()
# Retrieves 100 candidates → reranks → returns best 10
Use when: Quality > speed (2-stage retrieval)
Filtered Search
Combine search with SQL-like filters:
results = client.table("papers") \
.hybrid_search("climate impacts", alpha=0.6, topk=20) \
.eq("category", "science") \
.gt("year", 2020) \
.is_not_null("peer_reviewed") \
.execute()
Available filters: eq, neq, gt, gte, lt, lte, in_, between, is_null, is_not_null, like, ilike, regex
Multiple Vector Fields Per Row
Unlike most vector DBs (1 vector per record), VecLite lets you embed multiple fields:
class SupremeCourtCase(Table):
__tablename__ = "cases"
id = Integer(primary_key=True)
case_name = Text()
holding = Text(vector=True, fts=True)
facts = Text(vector=True, fts=True)
year = Integer()
# Search by holding
results = client.table("cases").vector_search(
query="privacy rights in digital age",
column="holding",
topk=5
).execute()
# Or search by facts
results = client.table("cases").vector_search(
query="government surveillance of emails",
column="facts",
topk=5
).execute()
Use cases: Legal research, academic papers (title vs abstract), products (name vs description)
Recipe: SEC Filings (Relational + FTS + Vectors)
Keep filings in one DB, pages as FTS‑only, and chunks with vectors. Let an agent both retrieve semantically and read exact page ranges.
from veclite import Client, Schema
from veclite.schema import Table, Integer, Text, Boolean
class Filings(Table):
__tablename__ = "filings"
id = Integer(primary_key=True)
ticker = Text(index=True)
form_type = Text(index=True)
filing_date = Text(index=True)
class FilingPages(Table):
__tablename__ = "filing_pages"
id = Integer(primary_key=True)
filing_id = Integer(index=True)
page_number = Integer(index=True)
content = Text(fts=True) # FTS only
class FilingChunks(Table):
__tablename__ = "filing_chunks"
id = Integer(primary_key=True)
filing_id = Integer(index=True)
page = Integer(index=True)
content = Text(vector=True, fts=True) # vectors + FTS
has_table = Boolean(default=False)
schema = Schema()
schema.add_table(Filings)
schema.add_table(FilingPages)
schema.add_table(FilingChunks)
client = Client.create(schema, "sec_db") # ./sec_db/{sqlite.db, vectors/}
# Hybrid retrieval on chunks within a filing
q = "Apple risk factors and competitive challenges"
hits = client.table("filing_chunks").hybrid_search(q, topk=10, alpha=0.7) \
.eq("filing_id", 12345).execute()
# Read a page window around best hit
best = hits.data[0]
page = best["page"]
filing_id = best["filing_id"]
pages = client.table("filing_pages").select("*") \
.eq("filing_id", filing_id) \
.between("page_number", page - 1, page + 1) \
.order("page_number") \
.execute()
This three‑table pattern (filings, filing_pages, filing_chunks) gives agents precision (page ranges) and recall (semantic + keyword).
Automatic Embeddings
No manual embedding pipeline needed - VecLite handles everything:
# Mark field for auto-embeddings
class Paper(Table):
__tablename__ = "papers"
id = Integer(primary_key=True)
title = Text()
abstract = Text(vector=True, fts=True) # Auto-embed on insert/update
year = Integer()
What happens automatically:
- ✅ Embeddings generated on insert/update
- ✅ Batching for efficiency
- ✅ LMDB caching (avoid re-embedding)
- ✅ Vector storage alongside SQLite
Embedding options:
vector=True→ voyage-3.5-lite (512D, default)vector=VectorConfig.voyage_3()→ voyage-3 (1024D)vector=VectorConfig.voyage_large()→ voyage-3.5-large (1536D)vector=True, contextualized=True→ voyage-context-3 (512D, better for long documents)
Installation
# Core (SQLite + local vectors)
pip install veclite
# With Voyage AI embeddings (recommended)
pip install veclite[voyage]
# With embedding cache (LMDB)
pip install veclite[cache]
# Everything
pip install veclite[all]
Requirements:
- Python 3.9+
- SQLite 3.35+ (included with Python)
- NumPy
Sync vs Async
Choose the right API for your use case:
Sync - Notebooks, scripts, simple applications:
from veclite import Client
client = Client.create(schema, "db.db")
results = client.table("docs").hybrid_search("query", topk=10).execute()
Async - Web apps, concurrent workloads:
from veclite import AsyncClient
client = AsyncClient.create(schema, "db.db")
results = await client.table("docs").hybrid_search("query", topk=10).execute()
When to Use VecLite
✅ Perfect For
- RAG Systems - Complete standalone retrieval solution
- Agentic RAG - Agents that choose retrieval strategies dynamically
- Semantic Search - Find documents by meaning, not just keywords
- Jupyter Notebooks - Interactive development and analysis
- Desktop Applications - Local-first semantic search
- Edge/IoT Devices - On-device retrieval without external APIs
❌ NOT For
- Production web servers - Use Qdrant, Pinecone, Weaviate instead
- Multi-tenant SaaS - VecLite is single-tenant by design
- High concurrency - SQLite write limitations
Documentation
- Installation
- Quickstart Guide
- Vector Search
- Keyword Search
- Hybrid Search
- Schema Definition
- API Reference
Testing
# Run all 120 tests
pytest tests/
# Run specific test suite
pytest tests/test_vector_search_sync.py
pytest tests/test_hybrid_search_async.py
Contributing
Contributions welcome! VecLite is designed to be simple and focused on RAG use cases.
git clone https://github.com/lucasastorian/veclite.git
cd veclite
pip install -e ".[dev]"
pytest tests/
License
MIT License - see LICENSE file for details.
Acknowledgments
- Built on SQLite's FTS5 for BM25 keyword search
- Inspired by Supabase's fluent query API
- Optimized for RAG and local-first applications
- Voyage AI for state-of-the-art embeddings
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file veclite-0.1.1.tar.gz.
File metadata
- Download URL: veclite-0.1.1.tar.gz
- Upload date:
- Size: 80.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c5d65e45a08cd2e012daf337f4b2feb2782cb650132279af39ca6794f1b0978
|
|
| MD5 |
c5e8b725d06f4e834938ac459ac62b41
|
|
| BLAKE2b-256 |
5e32701312df957c58aedd34e13eb3cedffb12d337db9433ded26693d6d35a2f
|
File details
Details for the file veclite-0.1.1-py3-none-any.whl.
File metadata
- Download URL: veclite-0.1.1-py3-none-any.whl
- Upload date:
- Size: 103.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4500c1959a7b816e97de1487f685945a32e2e7245840c39abeac0daa5c9d4c59
|
|
| MD5 |
f66959bdea7af13d4bd9941cc7b378ef
|
|
| BLAKE2b-256 |
96e58c30380d67516e04f8aec4335fe133169f5f536691a2cc57866db2bb7ea3
|