feat(stores): Adds Qdrant vector store backend

Signed-off-by: Anush008 <mail@anush.sh>

(cherry picked from commit 2f556b67f5)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Anush008 authored and Ettore Di Giacinto committed 2026-10-13 08:13:55 +00:00
1 parent dee4d8a399
commit cb97cb1ed9
23 files changed
+1025 -32

No files matched your search

+1 -1
View File
@@ -314,7 +314,7 @@ LocalAI supports various types of backends:
- **Vision & Detection Backends**: For object detection, segmentation, depth, and face/voice recognition (e.g., rf-detr.cpp, locate-anything.cpp, sam3.cpp, insightface)
- **Audio Processing Backends**: For voice activity detection and audio enhancement (e.g., Silero VAD, LocalVQE, [audio.cpp]({{%relref "features/audio-cpp" %}}))
- **Source Separation & Voice Conversion Backends**: For splitting a mix into named stems (vocals, drums, bass) and for converting speech or singing to a target voice (e.g., [audio.cpp]({{%relref "features/audio-cpp" %}}))
- **Utility Backends**: For reranking, PII/NER token classification, fine-tuning, quantization, and vector storage (e.g., rerankers, privacy-filter.cpp, TRL, local-store, valkey-store)
- **Utility Backends**: For reranking, PII/NER token classification, fine-tuning, quantization, and vector storage (e.g., rerankers, privacy-filter.cpp, TRL, local-store, valkey-store, qdrant-store)
See the [Backend & Model Compatibility Table]({{%relref "reference/compatibility-table" %}}) for the full catalog.
+94 -1
View File
@@ -51,12 +51,28 @@ but you can select a different store backend per request (see [Backends](#backen
## Backends
Each `/stores/*` request accepts an optional `backend` field selecting the store implementation.
Two backends ship with LocalAI:
Three backends ship with LocalAI:
| Backend | `backend` value | Persistence | Notes |
|---------|-----------------|-------------|-------|
| Local (default) | `local-store` (alias `embedded-store`) | In-memory, lost on restart | Exact cosine similarity, zero configuration. |
| Valkey Search | `valkey-store` (alias `valkey`) | Durable (Valkey RDB/AOF) | Backed by a Valkey Search (`FT.*`) server; survives restarts and supports opt-in HNSW. |
| Qdrant | `qdrant-store` (alias `qdrant`) | Durable (Qdrant storage) | Backed by a Qdrant server or cluster, or Qdrant Cloud; HNSW-indexed; on a single node a `find` right after a `set` sees the new vectors. |
Internal features that use stores (face and voice recognition, the router's KNN classifier and
embedding cache) don't send a `backend` field. To move the router's stores (`router-cache-<router>`
and `router-corpus-<router>`) to a durable backend, create a model config named after the store with
the `backend:` and `options:` shown below. Keep the default cosine `distance_metric` for them: they
compare similarities against cosine thresholds. Keep face and voice recognition on `local-store`
for now: their registries remember enrolled IDs only in memory, so after a restart entries in a
durable store could no longer be forgotten. With tracing enabled, the router's vector-store calls
appear in `/api/backend-traces` under the backend each store resolved to.
A `backend` field that names a different backend than the store's own (its model config's
`backend:`, or `local-store` when there is none) is served by a separate backend instance, so it
never lands on the store's usual backend. It gets the model config's `options:` only if the config
has no `backend:`; otherwise those options belong to the configured backend, and it gets its own
defaults.
### Valkey store backend
@@ -146,6 +162,83 @@ bundle; `tls_skip_verify:true` disables verification entirely and should only be
local testing.
{{% /notice %}}
### Qdrant store backend
The `qdrant-store` backend keeps vectors in a [Qdrant](https://qdrant.tech/) collection, so the
data survives a LocalAI restart. It works with a single Qdrant node, a Qdrant cluster and Qdrant
Cloud. It talks to Qdrant's **gRPC** port (`6334` by default), not the `6333` REST port.
Select it by passing `"backend": "qdrant-store"` (or the `"qdrant"` alias) on any `/stores/*` request:
```
curl -X POST http://localhost:8080/stores/set \
-H "Content-Type: application/json" \
-d '{"backend": "qdrant-store", "store": "my-vectors", "keys": [[0.1, 0.2], [0.3, 0.4]], "values": ["foo", "bar"]}'
```
As with Valkey, the connection and collection are configured through a **model config** named
after the store, with the settings in `options:` as `key:value` strings:
```yaml
name: my-vectors
backend: qdrant-store
options:
- addr:xyz-example.eu-central.aws.cloud.qdrant.io:6334
- tls:true
- api_key_env:QDRANT_API_KEY
- distance_metric:COSINE
```
When no config exists for a store, the backend connects to `localhost:6334` with the defaults
below.
Each store gets its own collection, named `localai_<store>-<hash>` (for example
`localai_my-vectors-1a2b3c4d`) unless you set `collection`. It is created on the first `set` with
the dimension of the first key, and Qdrant rejects keys and queries of any other dimension, even
after every key is deleted. To change the dimension (for example after switching embedding
models), delete the collection in Qdrant or set a new `collection`. The backend never deletes
collections itself, since with several writers that could lose another writer's data.
Keys are matched exactly, and a repeated key keeps the last value. A `set` is a single Qdrant
request, so a rejected `set` writes nothing. `NaN` and `Inf` components are rejected.
| Option | Default | Description |
|--------|---------|-------------|
| `addr` | `localhost:6334` | Qdrant gRPC address (`host:port`; the port defaults to `6334`). Not a URL — use `tls:true` for HTTPS endpoints. |
| `api_key` | *(empty)* | Qdrant API key (plaintext in config). |
| `api_key_env` | *(empty)* | Name of an env var that holds the API key. Preferred over `api_key` — keeps the secret out of the model YAML. |
| `tls` | `false` | Enable TLS (required by Qdrant Cloud). |
| `tls_ca_cert` | *(empty)* | Path to a PEM CA bundle used to verify the server certificate (self-signed / private CA). Turns on `tls`. |
| `tls_skip_verify` | `false` | Skip TLS certificate verification. Insecure — for testing only. Turns on `tls`. |
| `collection` | *(derived)* | Use this exact collection name instead of the derived one. |
| `distance_metric` | `COSINE` | `COSINE`, `EUCLID`, `DOT` or `MANHATTAN` (case-insensitive). |
| `request_timeout_ms` | `5000` | Per-request timeout in milliseconds. |
For `COSINE` the returned `similarities` follow the local store's convention (`1.0` = identical,
`-1.0` = opposite). For `DOT` they are dot products (larger is closer). For `EUCLID` and `MANHATTAN`
they are distances, so smaller is closer, which is the opposite ordering to `COSINE`. In every case
results come back nearest-first. If a collection already exists with a different distance than
`distance_metric`, loading the store (or, if the collection appeared later, the first `find`) fails
rather than returning scores with the wrong meaning.
{{% notice note %}}
The backend creates collections with Qdrant's default settings. For custom settings (sharding and
replication on a cluster, HNSW parameters, on-disk vectors, quantization), create the collection
in Qdrant yourself and point `collection` at it: an existing collection is used as long as it has a
single unnamed vector with the configured `distance_metric`.
{{% /notice %}}
{{% notice note %}}
Small collections are searched exactly. Once Qdrant builds the HNSW index (when a segment passes
its indexing threshold, 10 MB of vectors per segment by default, so typically tens of thousands of
embeddings), `find` is approximate, with recall set by the collection's HNSW settings.
{{% /notice %}}
{{% notice warning %}}
`tls` defaults to `false` (plaintext). Set `tls:true` whenever Qdrant is not on `localhost` or an
API key is configured, otherwise the key and the stored vectors travel the network unencrypted.
{{% /notice %}}
## Set
To set some keys you can do
@@ -129,6 +129,7 @@ All backends listed here can be installed on demand from the [Backend Gallery]({
| [privacy-filter.cpp](https://github.com/localai-org/privacy-filter.cpp) | Standalone GGML engine for the openai-privacy-filter PII/NER token-classification model family (powers LocalAI's PII redaction tier) | CPU, CUDA 13, Vulkan |
| [local-store](https://github.com/mudler/LocalAI) | Local-first vector database for embeddings | CPU, Metal |
| [valkey-store](https://github.com/mudler/LocalAI) | Durable vector store for embeddings backed by Valkey Search (FLAT or HNSW) | CPU, Metal |
| [qdrant-store](https://github.com/mudler/LocalAI) | Durable vector store for embeddings backed by Qdrant (HNSW; self-hosted, clustered or Qdrant Cloud) | CPU, Metal |
| [TRL](https://github.com/huggingface/trl) | Fine-tuning (SFT, DPO, GRPO, RLOO, KTO, ORPO) | CPU, CUDA 12/13 |
| [llama.cpp quantization](https://github.com/ggml-org/llama.cpp) | HuggingFace → GGUF model conversion and quantization | CPU, Metal |