444 lines
11 KiB
Markdown
444 lines
11 KiB
Markdown
# Lance Vector Backend (RESP + JSON-RPC)
|
||
|
||
This document explains how to use HeroDB’s Lance-backed vector store. It is text-first: users provide text, and HeroDB computes embeddings server-side (no manual vectors). It includes copy-pasteable RESP (redis-cli) and JSON-RPC examples for:
|
||
|
||
- Creating a Lance database
|
||
- Embedding provider configuration (OpenAI, Azure OpenAI, or deterministic test provider)
|
||
- Dataset lifecycle: CREATE, LIST, INFO, DROP
|
||
- Ingestion: STORE text (+ optional metadata)
|
||
- Search: QUERY with K, optional FILTER and RETURN
|
||
- Delete by id
|
||
- Index creation (currently a placeholder/no-op)
|
||
|
||
References:
|
||
- Implementation: [src/lance_store.rs](src/lance_store.rs), [src/cmd.rs](src/cmd.rs), [src/rpc.rs](src/rpc.rs), [src/server.rs](src/server.rs), [src/embedding.rs](src/embedding.rs)
|
||
|
||
Notes:
|
||
- Admin DB 0 cannot be Lance (or Tantivy). Only databases with id >= 1 can use Lance.
|
||
- Permissions:
|
||
- Read operations (SEARCH, LIST, INFO) require read permission.
|
||
- Mutating operations (CREATE, STORE, CREATEINDEX, DEL, DROP, EMBEDDING CONFIG SET) require readwrite permission.
|
||
- Backend gating:
|
||
- If a DB is Lance, only LANCE.* and basic control commands (PING, ECHO, SELECT, INFO, CLIENT, etc.) are permitted.
|
||
- If a DB is not Lance, LANCE.* commands return an error.
|
||
|
||
Storage layout and schema:
|
||
- Files live at: <base_dir>/lance/<db_id>/<dataset>.lance
|
||
- Records schema:
|
||
- id: Utf8 (non-null)
|
||
- vector: FixedSizeList<Float32, dim> (non-null)
|
||
- text: Utf8 (nullable)
|
||
- meta: Utf8 JSON (nullable)
|
||
- Search is an L2 KNN brute-force scan for now (lower score = better). Index creation is a no-op placeholder to be implemented later.
|
||
|
||
Prerequisites:
|
||
- Start HeroDB with RPC enabled (for management calls):
|
||
- See [docs/basics.md](./basics.md) for flags. Example:
|
||
```bash
|
||
./target/release/herodb --dir /tmp/herodb --admin-secret mysecret --port 6379 --enable-rpc
|
||
```
|
||
|
||
|
||
## 0) Create a Lance-backed database (JSON-RPC)
|
||
|
||
Use the management API to create a database with backend "Lance". DB 0 is reserved for admin and cannot be Lance.
|
||
|
||
Request:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 1,
|
||
"method": "herodb_createDatabase",
|
||
"params": [
|
||
"Lance",
|
||
{ "name": "vectors-db", "storage_path": null, "max_size": null, "redis_version": null },
|
||
null
|
||
]
|
||
}
|
||
```
|
||
|
||
- Response contains the allocated db_id (>= 1). Use that id below (replace 1 with your actual id).
|
||
|
||
Select the database over RESP:
|
||
```bash
|
||
redis-cli -p 6379 SELECT 1
|
||
# → OK
|
||
```
|
||
|
||
|
||
## 1) Configure embedding provider (server-side embeddings)
|
||
|
||
HeroDB embeds text internally at STORE/SEARCH time using a per-dataset EmbeddingConfig sidecar. Configure provider before creating a dataset to choose dimensions and provider.
|
||
|
||
Supported providers:
|
||
- openai (standard OpenAI or Azure OpenAI)
|
||
- testhash (deterministic, CI-friendly; no network)
|
||
|
||
Environment variables for OpenAI:
|
||
- Standard OpenAI: export OPENAI_API_KEY=sk-...
|
||
- Azure OpenAI: export AZURE_OPENAI_API_KEY=...
|
||
|
||
RESP examples:
|
||
```bash
|
||
# Standard OpenAI with default dims (model-dependent, e.g. 1536)
|
||
redis-cli -p 6379 LANCE.EMBEDDING CONFIG SET myset PROVIDER openai MODEL text-embedding-3-small
|
||
|
||
# OpenAI with reduced output dimension (e.g., 512) when supported
|
||
redis-cli -p 6379 LANCE.EMBEDDING CONFIG SET myset PROVIDER openai MODEL text-embedding-3-small PARAM dim 512
|
||
|
||
# Azure OpenAI (set env: AZURE_OPENAI_API_KEY)
|
||
redis-cli -p 6379 LANCE.EMBEDDING CONFIG SET myset PROVIDER openai MODEL text-embedding-3-small \
|
||
PARAM use_azure true \
|
||
PARAM azure_endpoint https://myresource.openai.azure.com \
|
||
PARAM azure_deployment my-embed-deploy \
|
||
PARAM azure_api_version 2024-02-15 \
|
||
PARAM dim 512
|
||
|
||
# Deterministic test provider (no network, stable vectors)
|
||
redis-cli -p 6379 LANCE.EMBEDDING CONFIG SET myset PROVIDER testhash MODEL any
|
||
```
|
||
|
||
Read config:
|
||
```bash
|
||
redis-cli -p 6379 LANCE.EMBEDDING CONFIG GET myset
|
||
# → JSON blob describing provider/model/params
|
||
```
|
||
|
||
JSON-RPC examples:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 2,
|
||
"method": "herodb_lanceSetEmbeddingConfig",
|
||
"params": [
|
||
1,
|
||
"myset",
|
||
"openai",
|
||
"text-embedding-3-small",
|
||
{ "dim": "512" }
|
||
]
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 3,
|
||
"method": "herodb_lanceGetEmbeddingConfig",
|
||
"params": [1, "myset"]
|
||
}
|
||
```
|
||
|
||
|
||
## 2) Create a dataset
|
||
|
||
Choose a dimension that matches your embedding configuration. For OpenAI text-embedding-3-small without dimension override, typical dimension is 1536; when `dim` is set (e.g., 512), use that. The current API requires an explicit DIM.
|
||
|
||
RESP:
|
||
```bash
|
||
redis-cli -p 6379 LANCE.CREATE myset DIM 512
|
||
# → OK
|
||
```
|
||
|
||
JSON-RPC:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 4,
|
||
"method": "herodb_lanceCreate",
|
||
"params": [1, "myset", 512]
|
||
}
|
||
```
|
||
|
||
|
||
## 3) Store text documents (server-side embedding)
|
||
|
||
Provide your id, the text to embed, and optional META fields. The server computes the embedding using the configured provider and stores id/vector/text/meta in the Lance dataset. Upserts by id are supported via delete-then-append semantics.
|
||
|
||
RESP:
|
||
```bash
|
||
redis-cli -p 6379 LANCE.STORE myset ID doc-1 TEXT "Hello vector world" META title "Hello" category "demo"
|
||
# → OK
|
||
```
|
||
|
||
JSON-RPC:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 5,
|
||
"method": "herodb_lanceStoreText",
|
||
"params": [
|
||
1,
|
||
"myset",
|
||
"doc-1",
|
||
"Hello vector world",
|
||
{ "title": "Hello", "category": "demo" }
|
||
]
|
||
}
|
||
```
|
||
|
||
|
||
## 4) Search with a text query
|
||
|
||
Provide a query string; the server embeds it and performs KNN search. Optional: FILTER expression and RETURN subset of fields.
|
||
|
||
RESP:
|
||
```bash
|
||
# K nearest neighbors for the query text
|
||
redis-cli -p 6379 LANCE.SEARCH myset K 5 QUERY "greetings to vectors"
|
||
# → Array of hits: [id, score, [k,v, ...]] pairs, lower score = closer
|
||
|
||
# With a filter on meta fields and return only title
|
||
redis-cli -p 6379 LANCE.SEARCH myset K 3 QUERY "greetings to vectors" FILTER "category = 'demo'" RETURN 1 title
|
||
```
|
||
|
||
JSON-RPC:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 6,
|
||
"method": "herodb_lanceSearchText",
|
||
"params": [1, "myset", "greetings to vectors", 5, null, null]
|
||
}
|
||
```
|
||
|
||
With filter and selected fields:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 7,
|
||
"method": "herodb_lanceSearchText",
|
||
"params": [1, "myset", "greetings to vectors", 3, "category = 'demo'", ["title"]]
|
||
}
|
||
```
|
||
|
||
Response shape:
|
||
- RESP over redis-cli: an array of hits [id, score, [k, v, ...]].
|
||
- JSON-RPC returns an object containing the RESP-encoded wire format string or a structured result depending on implementation. See [src/rpc.rs](src/rpc.rs) for details.
|
||
|
||
|
||
## 5) Create an index (placeholder)
|
||
|
||
Index creation currently returns OK but is a no-op. It will integrate Lance vector indices in a future update.
|
||
|
||
RESP:
|
||
```bash
|
||
redis-cli -p 6379 LANCE.CREATEINDEX myset TYPE "ivf_pq" PARAM nlist 100 PARAM pq_m 16
|
||
# → OK (no-op for now)
|
||
```
|
||
|
||
JSON-RPC:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 8,
|
||
"method": "herodb_lanceCreateIndex",
|
||
"params": [1, "myset", "ivf_pq", { "nlist": "100", "pq_m": "16" }]
|
||
}
|
||
```
|
||
|
||
|
||
## 6) Inspect datasets
|
||
|
||
RESP:
|
||
```bash
|
||
# List datasets in current Lance DB
|
||
redis-cli -p 6379 LANCE.LIST
|
||
|
||
# Get dataset info
|
||
redis-cli -p 6379 LANCE.INFO myset
|
||
```
|
||
|
||
JSON-RPC:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 9,
|
||
"method": "herodb_lanceList",
|
||
"params": [1]
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 10,
|
||
"method": "herodb_lanceInfo",
|
||
"params": [1, "myset"]
|
||
}
|
||
```
|
||
|
||
|
||
## 7) Delete and drop
|
||
|
||
RESP:
|
||
```bash
|
||
# Delete by id
|
||
redis-cli -p 6379 LANCE.DEL myset doc-1
|
||
# → OK
|
||
|
||
# Drop the entire dataset
|
||
redis-cli -p 6379 LANCE.DROP myset
|
||
# → OK
|
||
```
|
||
|
||
JSON-RPC:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 11,
|
||
"method": "herodb_lanceDel",
|
||
"params": [1, "myset", "doc-1"]
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 12,
|
||
"method": "herodb_lanceDrop",
|
||
"params": [1, "myset"]
|
||
}
|
||
```
|
||
|
||
|
||
## 8) End-to-end example (RESP)
|
||
|
||
```bash
|
||
# 1. Select Lance DB (assume db_id=1 created via RPC)
|
||
redis-cli -p 6379 SELECT 1
|
||
|
||
# 2. Configure embedding provider (OpenAI small model at 512 dims)
|
||
redis-cli -p 6379 LANCE.EMBEDDING CONFIG SET myset PROVIDER openai MODEL text-embedding-3-small PARAM dim 512
|
||
|
||
# 3. Create dataset
|
||
redis-cli -p 6379 LANCE.CREATE myset DIM 512
|
||
|
||
# 4. Store documents
|
||
redis-cli -p 6379 LANCE.STORE myset ID doc-1 TEXT "The quick brown fox jumps over the lazy dog" META title "Fox" category "animal"
|
||
redis-cli -p 6379 LANCE.STORE myset ID doc-2 TEXT "A fast auburn fox vaulted a sleepy canine" META title "Fox paraphrase" category "animal"
|
||
|
||
# 5. Search
|
||
redis-cli -p 6379 LANCE.SEARCH myset K 2 QUERY "quick brown fox" RETURN 1 title
|
||
|
||
# 6. Dataset info and listing
|
||
redis-cli -p 6379 LANCE.INFO myset
|
||
redis-cli -p 6379 LANCE.LIST
|
||
|
||
# 7. Delete and drop
|
||
redis-cli -p 6379 LANCE.DEL myset doc-2
|
||
redis-cli -p 6379 LANCE.DROP myset
|
||
```
|
||
|
||
|
||
## 9) End-to-end example (JSON-RPC)
|
||
|
||
Assume RPC server on port 8080. Replace ids and ports as needed.
|
||
|
||
1) Create Lance DB:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 100,
|
||
"method": "herodb_createDatabase",
|
||
"params": ["Lance", { "name": "vectors-db", "storage_path": null, "max_size": null, "redis_version": null }, null]
|
||
}
|
||
```
|
||
|
||
2) Set embedding config:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 101,
|
||
"method": "herodb_lanceSetEmbeddingConfig",
|
||
"params": [1, "myset", "openai", "text-embedding-3-small", { "dim": "512" }]
|
||
}
|
||
```
|
||
|
||
3) Create dataset:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 102,
|
||
"method": "herodb_lanceCreate",
|
||
"params": [1, "myset", 512]
|
||
}
|
||
```
|
||
|
||
4) Store text:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 103,
|
||
"method": "herodb_lanceStoreText",
|
||
"params": [1, "myset", "doc-1", "The quick brown fox jumps over the lazy dog", { "title": "Fox", "category": "animal" }]
|
||
}
|
||
```
|
||
|
||
5) Search text:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 104,
|
||
"method": "herodb_lanceSearchText",
|
||
"params": [1, "myset", "quick brown fox", 2, null, ["title"]]
|
||
}
|
||
```
|
||
|
||
6) Info/list:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 105,
|
||
"method": "herodb_lanceInfo",
|
||
"params": [1, "myset"]
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 106,
|
||
"method": "herodb_lanceList",
|
||
"params": [1]
|
||
}
|
||
```
|
||
|
||
7) Delete/drop:
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 107,
|
||
"method": "herodb_lanceDel",
|
||
"params": [1, "myset", "doc-1"]
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": 108,
|
||
"method": "herodb_lanceDrop",
|
||
"params": [1, "myset"]
|
||
}
|
||
```
|
||
|
||
|
||
## 10) Operational notes and troubleshooting
|
||
|
||
- If using OpenAI and you see “missing API key env”, set:
|
||
- Standard: `export OPENAI_API_KEY=sk-...`
|
||
- Azure: `export AZURE_OPENAI_API_KEY=...` and pass `use_azure true`, `azure_endpoint`, `azure_deployment`, `azure_api_version`.
|
||
- Dimensions mismatch:
|
||
- Ensure the dataset DIM equals the provider’s embedding dim. For OpenAI text-embedding-3 models, set `PARAM dim 512` (or another supported size) and use that same DIM for `LANCE.CREATE`.
|
||
- DB 0 restriction:
|
||
- Lance is not allowed on DB 0. Use db_id >= 1.
|
||
- Permissions:
|
||
- Read operations (SEARCH, LIST, INFO) require read permission.
|
||
- Mutations (CREATE, STORE, CREATEINDEX, DEL, DROP, EMBEDDING CONFIG SET) require readwrite permission.
|
||
- Backend gating:
|
||
- On Lance DBs, only LANCE.* commands are accepted (plus basic control).
|
||
- Current index behavior:
|
||
- `LANCE.CREATEINDEX` returns OK but is a no-op. Future versions will integrate Lance vector indices.
|
||
- Implementation files for reference:
|
||
- [src/lance_store.rs](src/lance_store.rs), [src/cmd.rs](src/cmd.rs), [src/rpc.rs](src/rpc.rs), [src/server.rs](src/server.rs), [src/embedding.rs](src/embedding.rs) |