# How to Set Up the Memory System with Qdrant Backend in OmniRoute

> Learn how to set up the memory system with Qdrant backend in OmniRoute. Enable Qdrant, configure details, and mirror SQLite operations for semantic search while maintaining SQLite as your data source.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: how-to-guide
- Published: 2026-08-07

---

**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`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/schemas/qdrant.ts) and stored in the `settings` table.

Send a PUT request to the settings endpoint:

```typescript
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 `memoryVectorStore` from `"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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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:

```bash
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`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/memory/qdrant.ts) (lines 78-86). This pings the `/readyz` endpoint and returns latency metrics:

```typescript
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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/memory/qdrant.ts) (lines 73-93) handles:

1. Generating embeddings via the configured model
2. Posting the vector and payload to `/points?wait=true`
3. Failing open—SQLite write succeeds even if Qdrant is unreachable

```typescript
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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`:

```typescript
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:**

```typescript
import { deleteSemanticMemoryPoint } from "@/lib/memory/qdrant";

await deleteSemanticMemoryPoint("msg-123");

```

**Periodic cleanup (e.g., nightly job):**

```typescript
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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/memory/store.ts) (lines 286-306) orchestrates a **best-effort** synchronization strategy:

1. **SQLite write first** — metadata and content persist to the local database
2. **Qdrant mirror attempt** — vector upsert runs asynchronously
3. **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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/schemas/qdrant.ts) | Zod schemas for settings validation |
| [`src/lib/memory/qdrant.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/memory/qdrant.ts) | Core client: config, health, CRUD, search |
| [`src/lib/memory/store.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/memory/store.ts) | High-level memory store with sync coordination |
| [`src/lib/memory/retrieval.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/memory/retrieval.ts) | Query routing between Qdrant and SQLite-vec |
| [`tests/integration/qdrant-routes.test.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/tests/integration/qdrant-routes.test.ts) | API and health endpoint tests |
| [`tests/e2e/memory-qdrant-routes.spec.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/tests/e2e/memory-qdrant-routes.spec.ts) | Playwright tests for Engine UI |

## Summary

- **Enable Qdrant** via PUT to `/api/settings/qdrant` with 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`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/memory/qdrant.ts) for runtime search tuning. For production, deploy Qdrant as a dedicated cluster rather than localhost.