How OmniRoute Uses FTS5 and Qdrant for Persistent Conversational Memory

OmniRoute implements a dual-backend memory system combining SQLite FTS5 for fast keyword search and Qdrant for semantic vector retrieval, orchestrated through a unified manager that provides persistent, searchable conversational memory across restarts.

The OmniRoute routing and conversation platform stores conversational snippets in two complementary backends that serve different retrieval strategies. This hybrid architecture enables both precise keyword matching and powerful semantic similarity search, ensuring developers can retrieve relevant context from large conversation histories efficiently. According to the OmniRoute source code, the system persists data to disk on SQLite and Qdrant, making memory durable across server restarts.

FTS5 Full-Text Search: Fast Keyword-Based Retrieval

OmniRoute uses SQLite FTS5 as its lightweight, embedded full-text search engine for quick lookup of recent chats and user-provided notes.

FTS5 Schema and Migration

The FTS5 virtual table is created via migration in src/lib/db/migrations/022_add_memory_fts5.sql. This indexes the content column of each memory entry, enabling BM25-ranked relevance scoring out of the box:

-- Creates virtual FTS5 table linked to memory content
CREATE VIRTUAL TABLE memory_vec USING fts5(
  content,
  content_rowid=rowid
);

SQLite Backend Operations

The src/lib/memory/sqliteBackend.ts file implements CRUD operations that automatically synchronize the regular memory table with the FTS5 index:

Method Purpose
upsert Inserts or updates memory entries in both tables
search Executes FTS5 MATCH queries with BM25 ranking
delete Removes entries and maintains index consistency

Retrieval uses SQLite's built-in MATCH operator. The BM25 relevance score ranks results automatically without additional configuration.

import { searchFullTextMemory } from "@/lib/memory/sqliteBackend";

const results = await searchFullTextMemory("roadmap Q4");

The SQLite database resides under ~/.omniroute/ and survives process restarts, providing zero-configuration persistence for deployments without external dependencies.

Qdrant Vector Search: Semantic Similarity at Scale

For nearest-neighbor lookup based on embeddings, OmniRoute integrates with Qdrant, a remote vector database that enables similarity-based recall across entire conversation histories.

Qdrant Configuration

The src/lib/memory/qdrant.ts module builds Qdrant configuration from the Settings table or environment variables:

// Environment-driven configuration
QDRANT_HOST    // Server hostname
QDRANT_PORT    // HTTP port
QDRANT_API_KEY // Authentication (optional)

Semantic Memory Operations

Upserting semantic memory (upsertSemanticMemoryPoint) follows a two-step pipeline:

  1. Call createEmbeddingResponse to generate a dense vector from content
  2. Store the vector with payload metadata to /collections/<collection>/points
import { upsertSemanticMemoryPoint } from "@/lib/memory/qdrant";

await upsertSemanticMemoryPoint({
  id: "mem-123",
  apiKeyId: "user-abc",
  sessionId: "sess-456",
  key: "project-xyz-notes",
  content: "Discussed roadmap for Q4, decided on feature-flags.",
  metadata: { tags: ["roadmap", "q4"] },
  createdAt: new Date().toISOString(),
  expiresAt: null,
});

Semantic search (searchSemanticMemory) embeds query text and issues /points/search requests:

import { searchSemanticMemory } from "@/lib/memory/qdrant";

const { ok, results } = await searchSemanticMemory("feature-flags plan", 5);
if (ok) console.log(results);

Results filter by apiKey or session when scoping is required, ensuring multi-tenant isolation.

Quantization for Performance

Qdrant collections support optional quantization configured via buildQuantizationConfig:

Mode RAM Reduction Use Case
none None Maximum accuracy, smaller datasets
int8 ~4x Balanced speed/quality for medium scale
binary ~32x Maximum throughput, acceptable precision loss

The searchQuantizationParams helper applies rescore settings when quantized collections need refinement.

Collections are created lazily on first use via ensureCollection, eliminating deployment-time setup.

Memory Manager: Unified Hybrid Retrieval

The src/lib/memory/manager.ts module exposes a single API that orchestrates both backends based on request characteristics:

Method Backend Selection Logic
upsertMemory Routes to FTS5, Qdrant, or both based on type flag
retrieveMemory Chooses semantic, keyword, or hybrid strategy
cleanupMemory Applies retention policies to both stores

Hybrid Retrieval Pattern

UI components can issue combined requests that merge strengths of both systems:

  1. Execute semantic top-K search in Qdrant for conceptual relevance
  2. Refine results with keyword filtering via FTS5 for precision
import { retrieveMemory } from "@/lib/memory/manager";

const { ok, results } = await retrieveMemory({
  query: "roadmap",
  topK: 10,
  scope: { apiKeyId: "user-abc" },
});

The manager handles backend fallbacks transparently—if Qdrant is unavailable, queries degrade gracefully to FTS5 where possible.

Automatic Cleanup and Retention

Storage growth is bounded via cleanupSemanticMemoryPoints, which removes expired or stale entries based on configurable retention policies. This runs against both backends to maintain consistency.

CLI and Integration Examples

Command-Line Memory Management

omniroute memory add \
  --key "project-xyz-notes" \
  --content "Discussed roadmap for Q4, decided on feature-flags." \
  --metadata '{"tags":["roadmap","q4"]}'

Chat-Core Integration

Memory extraction and injection hooks connect the persistence layer to active conversations:

Component File
Extraction from incoming messages open-sse/handlers/chatCore/memoryExtraction.ts
Injection into context windows open-sse/handlers/chatCore/memorySkillsInjection.ts

Summary

  • SQLite FTS5 provides cheap, exact-match keyword search with BM25 ranking—ideal for recent notes and tagged content
  • Qdrant delivers scalable semantic similarity via embeddings, with quantization options for performance tuning
  • Memory manager in src/lib/memory/manager.ts unifies both backends behind a single API with hybrid retrieval support
  • Both systems persist to disk: SQLite in ~/.omniroute/, Qdrant as external container with disk-backed collections
  • Automatic cleanup prevents unbounded growth across both stores

Frequently Asked Questions

What is the difference between FTS5 and Qdrant in OmniRoute's memory system?

FTS5 handles fast keyword search with exact and prefix matching, ideal for finding specific terms, tags, or recent conversations. Qdrant enables semantic search by comparing vector embeddings, which finds conceptually related content even without keyword overlap. The system uses both: FTS5 for precision and low latency, Qdrant for conceptual recall across large histories.

How does OmniRoute ensure memory persists across server restarts?

SQLite databases are stored in the local data directory (~/.omniroute/) as ordinary files. Qdrant runs as a separate service with its own disk-backed storage—the collection is created on first use via ensureCollection. Both mechanisms survive process termination and server reboots without data loss.

When should I use hybrid retrieval versus single-backend queries?

Use hybrid retrieval when you need both conceptual breadth and keyword precision—first retrieving semantically similar candidates with Qdrant, then filtering with FTS5. Use single-backend queries for specialized cases: pure FTS5 for exact tag lookups or compliance auditing, pure Qdrant for open-ended "find similar ideas" exploration without keyword constraints.

How does quantization in Qdrant affect memory search quality?

Quantization reduces RAM usage significantly—int8 cuts memory by ~4× with minimal accuracy loss, while binary achieves ~32× reduction with faster scoring but potential precision degradation. OmniRoute's searchQuantizationParams enables rescoring to recover quality on quantized collections, making binary viable for high-throughput scenarios with acceptable recall trade-offs.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →