How to Set Up the Memory System with Qdrant Backend in OmniRoute
Enable Qdrant in OmniRoute by toggling qdrantEnabled in the settings API, configure host/port/collection details, and the system automatically mirrors SQLite memory operations to Qdrant for semantic search while keeping SQLite as the durable source of truth.
OmniRoute supports two layers for conversational memory: a SQLite/FTS5 primary store and an optional Qdrant vector backend. When you set up the memory system with Qdrant backend in OmniRoute, the system provides fast semantic search on embeddings while maintaining SQLite as the fallback for metadata and durability. This guide walks through configuration, health verification, and operational patterns using the actual source code from diegosouzapw/OmniRoute.
Configuration: Enabling Qdrant via Settings API
The first step to set up the memory system with Qdrant backend in OmniRoute is persisting your connection parameters. The settings are validated against QdrantSettingsSchema in src/shared/schemas/qdrant.ts and stored in the settings table.
Send a PUT request to the settings endpoint:
await fetch("/api/settings/qdrant", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
enabled: true,
host: "localhost",
port: 6333,
collection: "omniroute_memory",
embeddingModel: "openai/text-embedding-3-small",
quantization: "none", // options: "none", "int8", "binary"
vectorSize: 1536,
hnswEfConstruct: 128,
}),
});
Key parameters explained:
- enabled — flips
memoryVectorStorefrom"auto"(SQLite-vec) to"qdrant" - quantization — reduces memory footprint;
"int8"or"binary"for production workloads - vectorSize — must match your embedding model (1536 for
text-embedding-3-small)
Config Normalization and Environment Fallbacks
The function getQdrantConfig() in src/lib/memory/qdrant.ts (lines 47-55) reads raw settings and produces a typed QdrantConfig object. It applies environment variable fallbacks and defaults, ensuring the system degrades gracefully if UI settings are incomplete.
This normalization layer means you can also configure Qdrant via environment variables:
QDRANT_HOST=qdrant.example.com
QDRANT_PORT=6333
QDRANT_COLLECTION=omniroute_memory
Health Check: Verifying Qdrant Connectivity
Before trusting the connection, call checkQdrantHealth() from src/lib/memory/qdrant.ts (lines 78-86). This pings the /readyz endpoint and returns latency metrics:
import { checkQdrantHealth } from "@/lib/memory/qdrant";
const health = await checkQdrantHealth();
if (health.ok) {
console.log(`Qdrant reachable in ${health.latencyMs} ms`);
} else {
console.warn("Qdrant not reachable:", health.error);
}
The UI displays "connected" status only when this check returns { ok: true }.
Collection Management and Quantization
On first upsert or search, OmniRoute automatically creates or verifies the Qdrant collection with the correct vector size and optional quantization. The buildQuantizationConfig helper in src/lib/memory/qdrant.ts (lines 6-22) constructs the collection configuration based on your quantization setting.
No manual collection creation is required—the ORM layer handles this automatically.
Upserting Memory Points
When you store a memory point, upsertSemanticMemoryPoint() in src/lib/memory/qdrant.ts (lines 73-93) handles:
- Generating embeddings via the configured model
- Posting the vector and payload to
/points?wait=true - Failing open—SQLite write succeeds even if Qdrant is unreachable
import { upsertSemanticMemoryPoint } from "@/lib/memory/qdrant";
await upsertSemanticMemoryPoint({
id: "msg-123",
apiKeyId: "key-1",
sessionId: "sess-abc",
key: "user_question",
content: "How do I set up Qdrant?",
metadata: { source: "chat" },
createdAt: new Date().toISOString(),
expiresAt: null,
});
The id field uniquely identifies the point across both SQLite and Qdrant.
Semantic Search Against Qdrant
The searchSemanticMemory() function in src/lib/memory/qdrant.ts (lines 42-81) converts your query to an embedding, builds filters, applies quantization-specific search parameters, and calls /points/search:
import { searchSemanticMemory } from "@/lib/memory/qdrant";
const result = await searchSemanticMemory("setup Qdrant", 5, {
apiKeyId: "key-1",
sessionId: "sess-abc",
});
if (result.ok) {
console.log("Top hits:", result.results);
} else {
console.error("Search failed:", result.error);
}
Results merge into the standard memory response format, allowing seamless fallback to SQLite-vec if Qdrant is disabled.
Deleting and Cleaning Up Memory Points
Best-effort deletion operations ensure SQLite consistency even when Qdrant is temporarily unavailable:
Delete single point:
import { deleteSemanticMemoryPoint } from "@/lib/memory/qdrant";
await deleteSemanticMemoryPoint("msg-123");
Periodic cleanup (e.g., nightly job):
import { cleanupSemanticMemoryPoints } from "@/lib/memory/qdrant";
const cleanup = await cleanupSemanticMemoryPoints({ retentionDays: 30 });
console.log(`Deleted ${cleanup.deletedCount} stale points from Qdrant`);
Both functions in src/lib/memory/qdrant.ts (lines 90-121) log errors but do not block the SQLite transaction.
How the Sync Layer Works
The high-level memory store in src/lib/memory/store.ts (lines 286-306) orchestrates a best-effort synchronization strategy:
- SQLite write first — metadata and content persist to the local database
- Qdrant mirror attempt — vector upsert runs asynchronously
- Error isolation — Qdrant failures are logged but don't abort the transaction
This design guarantees durability and availability even with intermittent Qdrant connectivity.
Retrieval Routing: When Qdrant Gets Queried
The retrieval layer in src/lib/memory/retrieval.ts (lines 11-20) inspects memoryVectorStore settings. When set to "qdrant", semantic search routes to Qdrant; otherwise it falls back to SQLite-vec. All /api/memory/... endpoints flow through this router, making the backend choice transparent to API consumers.
File Reference Guide
| File | Purpose |
|---|---|
src/shared/schemas/qdrant.ts |
Zod schemas for settings validation |
src/lib/memory/qdrant.ts |
Core client: config, health, CRUD, search |
src/lib/memory/store.ts |
High-level memory store with sync coordination |
src/lib/memory/retrieval.ts |
Query routing between Qdrant and SQLite-vec |
tests/integration/qdrant-routes.test.ts |
API and health endpoint tests |
tests/e2e/memory-qdrant-routes.spec.ts |
Playwright tests for Engine UI |
Summary
- Enable Qdrant via PUT to
/api/settings/qdrantwith host, port, and collection details - Verify connectivity using
checkQdrantHealth()before production traffic - Automatic collection setup on first use—no manual Qdrant configuration needed
- Best-effort sync writes SQLite first, mirrors to Qdrant asynchronously
- Fail-open design ensures SQLite durability when Qdrant is unreachable
- Semantic search routes through
searchSemanticMemory()with automatic embedding generation
Frequently Asked Questions
What happens if Qdrant becomes unreachable during operation?
SQLite remains the source of truth. Writes to SQLite succeed regardless of Qdrant availability. The system logs Qdrant errors but continues serving requests with potentially degraded semantic search quality. Once Qdrant recovers, new memory points resume syncing automatically.
Can I switch from Qdrant back to SQLite-vec without data loss?
Yes. Disable Qdrant via the settings API (enabled: false), and the system reverts to SQLite-vec for semantic search. Your historical data remains in SQLite. Vector data in Qdrant is not automatically migrated back, but new embeddings generate locally.
Which embedding models does OmniRoute support with Qdrant?
Any model returning fixed-size vectors works. The default openai/text-embedding-3-small produces 1536-dimensional vectors. Configure vectorSize to match your chosen model, and ensure the same model is used consistently for queries and storage to maintain search accuracy.
How do I tune Qdrant performance for high-throughput workloads?
Set quantization to "int8" or "binary" to reduce memory and improve search speed. Increase hnswEfConstruct for better recall at build time, and monitor searchQuantizationParams in src/lib/memory/qdrant.ts for runtime search tuning. For production, deploy Qdrant as a dedicated cluster rather than localhost.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →