What ships with Docmancer
A base install gives you SQLite FTS5 (lexical) and the MCP runtime. To enable dense and sparse vectors, install the [vector] extra:
pip install 'docmancer[vector]'
That pulls in the Qdrant client and the sqlite-vec fallback. The Qdrant binary itself is downloaded automatically on first ingest from the pinned v1.14.1 GitHub release.
Managed Qdrant lifecycle
docmancer qdrant owns the local Qdrant process:
docmancer qdrant up
docmancer qdrant down
docmancer qdrant status
docmancer qdrant upgrade
docmancer qdrant logs
| Command | What it does |
|---|---|
up | Starts the pinned Qdrant binary. Downloads on first run. Port selection guarded by filelock. |
down | Stops the running Docmancer-owned Qdrant cleanly. |
status | Reports running state, port, storage path, and version. |
upgrade | Pulls the binary for the currently pinned release version. |
logs | Tails the Qdrant log file at ~/.docmancer/qdrant/logs/. |
Telemetry is disabled. PID and runtime files live under ~/.docmancer/qdrant/.
Foreign collection protection
QdrantStore.ensure_collection refuses to claim a pre-existing Qdrant collection that lacks the Docmancer ownership sentinel. If you point vector_store.url at a shared cluster or a collection that already exists, you'll get a clear PermissionError instead of silently writing into it.
delete_collection will only operate on collections that carry the sentinel. This makes accidental deletion of foreign data impossible.
Configuring the vector store
Add a vector_store: block to your YAML to opt into hybrid retrieval. The dispatcher auto-flips retrieval.default_mode to hybrid when this block is present and non-empty.
vector_store:
provider: qdrant
collection: docmancer
managed: true
Override URL or use an existing Qdrant cluster (the ownership sentinel still applies):
vector_store:
provider: qdrant
url: http://localhost:6333
collection: docmancer
managed: false
Or set the env var:
export DOCMANCER_QDRANT_URL=http://localhost:6333
SqliteVecStore fallback
When your platform has no matching Qdrant binary, Docmancer transparently falls back to sqlite-vec — a vector extension that lives inside the same SQLite database as FTS5. You can also force this backend:
vector_store:
provider: sqlite_vec
Tradeoffs: dense queries work, sparse (SPLADE) is unavailable, performance is slower than Qdrant for large corpora. Good enough for small-to-medium projects.
Embeddings providers
| Provider | Extra | Models | Notes |
|---|---|---|---|
| FastEmbed (default) | none | BAAI/bge-base-en-v1.5 dense + prithivida/Splade_PP_en_v1 sparse | Local, no API key, ~500 MB model download on first run |
| OpenAI | [embeddings-openai] | text-embedding-3-small, text-embedding-3-large | Requires OPENAI_API_KEY |
| Voyage | [embeddings-voyage] | voyage-3, voyage-3-lite | Requires VOYAGE_API_KEY |
| Cohere | [embeddings-cohere] | embed-english-v3.0 | Requires COHERE_API_KEY |
Cloud providers retry on 429 / 5xx with bounded exponential backoff. If a configured cloud provider has no API key in env, ingest falls back to FTS5-only with a warning rather than aborting.
Configure in YAML:
embeddings:
provider: openai
dense_model: text-embedding-3-large
Content-hash cache
Every section is hashed by content. The cache at ~/.docmancer/embeddings-cache/ records (hash, model, vector). On re-ingest, sections whose hash matches the cache skip the embedding call entirely.
This means:
- Re-ingesting an unchanged source is fast.
- Swapping embeddings providers re-embeds everything (different model = different cache key).
ingest --recreateblows away SQLite and re-runs embeddings from scratch — but cache hits still apply if content hashes match.
Stale vector cleanup
sync_vector_store reconciles Qdrant against SQLite on every ingest:
- New sections are embedded and upserted.
- Sections whose content hash matches
embedding_upsertsbookkeeping skip the embedding pass. - Points whose chunk ids have vanished from SQLite are pruned (reported as
pruned=Nin the agent log).
This means ingest --recreate cannot leave stale dense hits behind, and removing a source via docmancer remove actually removes its vector points too.
Inspecting drift
docmancer inspect
Section counts are grouped by format and warnings flag any divergence between SQLite section counts and Qdrant point counts. Re-run ingest to reconcile.
Disabling vectors
To keep everything lexical-only:
docmancer ingest ./docs --no-vectors
Or globally:
export DOCMANCER_AUTO_VECTORS=0
A bare YAML without vector_store: also keeps the index lexical-only. The managed Qdrant lifecycle code only runs when explicitly opted in.