How OmniRoute Memory Handles Persistent Conversational Storage Using FTS5 and Qdrant

OmniRoute implements a hybrid persistent memory system that stores conversational data in SQLite FTS5 for fast keyword retrieval and Qdrant for semantic vector search, orchestrated through a unified manager that enables both exact-match and similarity-based recall across chat history.

The OmniRoute open-source routing layer (diegosouzapw/OmniRoute) provides persistent conversational memory by combining two specialized storage backends. This architecture ensures that chat contexts survive server restarts while supporting both precise keyword lookups and intelligent semantic retrieval.

Dual-Backend Architecture for Conversational Memory

OmniRoute splits conversational storage across complementary backends optimized for different retrieval strategies. This dual approach balances the speed of exact text matching with the flexibility of embedding-based similarity search.

The system uses SQLite FTS5 (Full-Text Search version 5) to power fast keyword-based lookup of recent chats and user-provided notes. According to src/lib/db/migrations/022_add_memory_fts5.sql, the migration creates an FTS5 virtual table that indexes the content column of the memory table.

The SQLite-based backend (src/lib/memory/sqliteBackend.ts) implements CRUD operations including upsert, search, and delete. These operations write to the regular memory and memory_vec tables while automatically updating the FTS5 index. Retrieval uses the SQLite MATCH operator, returning rows sorted by the built-in BM25 relevance score.

Data persists in the local filesystem under ~/.omniroute/, ensuring conversational history survives process restarts without external dependencies.

Qdrant for Semantic Vector Storage

For semantic similarity search, OmniRoute integrates with Qdrant, a remote vector database. The configuration logic in src/lib/memory/qdrant.ts builds connection parameters from the Settings table or environment variables (QDRANT_HOST, QDRANT_PORT).

When storing memory, the upsertSemanticMemoryPoint function first calls the embedding service (createEmbeddingResponse) to generate a dense vector, then stores the vector with payload metadata via the /collections/<collection>/points endpoint. Searches execute through searchSemanticMemory, which embeds the query text and issues a /points/search request, supporting filtering by API-key or session ID.

The system supports optional quantization (none, int8, binary) via buildQuantizationConfig and searchQuantizationParams, reducing RAM usage while enabling fast rescoring for quantized collections.

Memory Manager Orchestration

The Memory Manager (src/lib/memory/manager.ts) unifies both backends behind a single API. It exposes retrieveMemory, upsertMemory, and cleanupMemory methods that internally route requests based on the type flag—directing keyword queries to SQLite and semantic queries to Qdrant.

For hybrid retrieval, the manager can execute a combined strategy: first performing a semantic top-K search in Qdrant, then refining results with FTS5 keyword filters. This delivers both broad similarity matching and precise relevance filtering.

Implementation Details and Source Code

Database Schema and Migrations

The FTS5 integration begins with the migration file src/lib/db/migrations/022_add_memory_fts5.sql, which establishes the virtual table structure. This virtual table indexes content from the primary memory storage, enabling efficient text search without duplicating the underlying data store.

The Qdrant collection is created lazily on first use through the ensureCollection function, which verifies the collection exists before attempting vector operations.

Automatic Cleanup and Retention

The system includes automatic cleanup via cleanupSemanticMemoryPoints, which removes expired or stale entries based on configurable retention policies. This keeps storage size bounded across both SQLite files and Qdrant collections.

Practical Usage Examples

CLI Memory Commands

You can add memory entries directly from the command line using the CLI implementation in bin/cli/commands/memory.mjs:

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

Programmatic Memory Operations

Upserting semantic memory via the Qdrant backend (src/lib/memory/qdrant.ts):

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,
});

Keyword search using FTS5 (src/lib/memory/sqliteBackend.ts):

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

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

Semantic search using Qdrant:

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

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

Hybrid retrieval via the Memory Manager (src/lib/memory/manager.ts):

import { retrieveMemory } from "@/lib/memory/manager";

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

Chat Core Integration

The memory system integrates with OmniRoute's chat processing pipeline through dedicated handlers. The open-sse/handlers/chatCore/memoryExtraction.ts module extracts relevant memories from incoming messages, while open-sse/handlers/chatCore/memorySkillsInjection.ts injects retrieved context into the chat flow before routing to language models.

Summary

  • OmniRoute uses SQLite FTS5 for persistent full-text search with BM25 scoring, storing data locally in ~/.omniroute/.
  • Qdrant provides scalable semantic vector storage with configurable quantization (int8, binary, or none) and session-based filtering.
  • The Memory Manager (src/lib/memory/manager.ts) unifies both backends, supporting hybrid retrieval strategies that combine semantic similarity with keyword precision.
  • Automatic cleanup through cleanupSemanticMemoryPoints enforces retention policies across both storage systems.
  • All operations are accessible via REST API, CLI (bin/cli/commands/memory.mjs), or direct programmatic integration.

Frequently Asked Questions

How does OmniRoute decide between FTS5 and Qdrant for a memory query?

The Memory Manager (src/lib/memory/manager.ts) routes requests based on the type flag passed to retrieveMemory. Keyword-based queries execute against the SQLite FTS5 backend for exact matches, while semantic queries route to Qdrant for vector similarity search. For hybrid retrieval, the system can execute both strategies sequentially—first retrieving semantic candidates from Qdrant, then filtering through the FTS5 index for keyword relevance.

What quantization options does OmniRoute support for Qdrant vectors?

According to src/lib/memory/qdrant.ts, the system supports three quantization levels via buildQuantizationConfig: none (full precision), int8 (8-bit integers), and binary. These settings reduce RAM usage and enable fast rescoring during similarity searches, configured through searchQuantizationParams when executing queries.

Where is conversational data stored when Qdrant is not configured?

When Qdrant is unavailable, OmniRoute maintains full functionality using SQLite FTS5 alone. The SQLite backend (src/lib/memory/sqliteBackend.ts) persists all conversational data in local database files under ~/.omniroute/, ensuring durable storage for keyword-based retrieval even without external vector database dependencies.

How does OmniRoute handle memory cleanup and retention?

The system implements automatic cleanup through the cleanupSemanticMemoryPoints function (Qdrant) and corresponding SQLite maintenance routines. These remove expired entries based on configurable retention policies defined in the expiresAt field (for Qdrant) or similar timestamp logic, preventing unlimited storage growth while maintaining recent conversation history.

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 →