How to Implement the OmniRoute Memory System for Persistent Conversational Context
OmniRoute implements persistent conversational context through a modular pipeline that extracts message chunks, converts them to vector embeddings, stores them in Qdrant or SQLite, and retrieves relevant snippets to inject into subsequent prompts.
OmniRoute provides a production-ready memory subsystem that retains conversation context across multiple requests and client sessions. The system processes incoming messages through a six-stage pipeline that ensures horizontal scalability while maintaining persistent state in a durable vector store. By decoupling the memory logic from request handlers, OmniRoute enables stateless deployment architectures without sacrificing conversational continuity.
Architectural Overview
The memory system follows a stateless design where all persistence resides in the vector store, allowing horizontal scaling without session affinity. The pipeline consists of six core components:
1. Extraction – src/lib/memory/extraction.ts parses incoming request payloads including messages, tool results, and files into searchable chunks with metadata.
2. Summarization – src/lib/memory/summarization.ts optionally condenses large chunks using the configured LLM before persistence to reduce storage costs.
3. Vector Storage – src/lib/memory/qdrant.ts (Qdrant) or src/lib/memory/store.ts (SQLite) handles embedding generation via the vectorize() service and stores vectors with metadata including conversationId, timestamp, and sourceMessageId.
4. Retrieval – src/lib/memory/retrieval.ts embeds the current user prompt and performs nearest-neighbor searches to return the most relevant historical chunks.
5. Injection – src/lib/memory/injection.ts merges retrieved chunks into the prompt as system messages or prepended context before forwarding to the target model.
6. Cache and Re-index – src/lib/memory/cache.ts provides an LRU cache for embeddings to avoid redundant API calls, while src/lib/memory/reindex.ts runs background jobs to keep the vector database synchronized after schema changes.
Configuration and validation are handled by src/lib/memory/settings.ts and src/lib/memory/verify.ts, which manage runtime toggles like MEMORY_ENABLED and MEMORY_MAX_TOKENS using Zod schemas.
Enabling Memory via the Settings API
The memory system is controlled through src/app/api/settings/memory/route.ts, which exposes REST endpoints for runtime configuration.
Enable memory and configure limits:
POST /api/settings/memory
Content-Type: application/json
{
"enabled": true,
"maxTokens": 2048,
"topK": 5,
"provider": "qdrant"
}
Behind the scenes, src/lib/memory/verify.ts validates the payload against Zod schemas, while src/lib/db/settings.ts persists the configuration. The MEMORY_ENABLED toggle determines whether the pipeline runs during request processing.
Implementing the Storage and Retrieval Flow
Storing Conversation Context
When processing a new message, use the storeChunk function from src/lib/memory/store.ts to persist context:
import { storeChunk } from "@/lib/memory/store";
// Store a user message with conversation metadata
await storeChunk({
conversationId: convId,
messageId: msg.id,
content: msg.content,
metadata: { role: "user", timestamp: Date.now() },
});
This function orchestrates extraction.ts to chunk the text, calls the embeddings service to generate a 1536-dimensional vector, and persists the result via qdrant.ts or your configured adapter.
Retrieving Relevant Context
On subsequent requests, retrieve historical context using retrieveRelevantChunks from src/lib/memory/retrieval.ts:
import { retrieveRelevantChunks } from "@/lib/memory/retrieval";
const context = await retrieveRelevantChunks({
conversationId: currentConvId,
prompt: newUserPrompt,
topK: 5,
});
// Inject context into the prompt
const enrichedPrompt = `${context.join("\n---\n")}\n\n${newUserPrompt}`;
The retrieval process embeds the incoming prompt, queries the vector store for nearest neighbors, and returns plaintext snippets ready for injection.
Full Request Integration
The runtime endpoint src/app/api/memory/route.ts provides handleMemory to orchestrate the complete pipeline:
import { handleMemory } from "@/app/api/memory/route";
export async function handler(req: Request) {
// Execute full pipeline: extract → store → retrieve → inject
const enrichedReq = await handleMemory(req);
// Forward to LLM executor
const response = await executor.execute(enrichedReq);
return response;
}
This approach ensures that every request automatically maintains conversational continuity without manual intervention.
Configuring Edge Cases and Fault Tolerance
The memory system includes several safeguards for production deployments:
Token Budget Management – src/lib/memory/settings.ts enforces MEMORY_MAX_TOKENS limits on injected context. When retrieved chunks exceed this budget, the system drops the oldest chunks first to maintain the most recent relevance.
Privacy and Isolation – Memory data can be scoped per API key, ensuring conversation isolation between users. Delete specific conversation histories via the settings endpoint:
DELETE /api/settings/memory
Content-Type: application/json
{
"conversationId": "conv-123",
"apiKey": "sk-..."
}
Fault Tolerance – If the vector store becomes unavailable, src/lib/memory/verify.ts implements a fallback mechanism that automatically degrades to no-memory mode, allowing requests to proceed without historical context rather than failing entirely.
Extending the Memory System
OmniRoute’s modular architecture supports custom implementations:
Vector Database Replacement – Replace src/lib/memory/qdrant.ts by implementing the VectorStore interface with your preferred provider (e.g., Pinecone, Weaviate). The system automatically uses your adapter when specified in the provider configuration.
Custom Chunking Strategies – Modify src/lib/memory/extraction.ts to implement domain-specific chunking logic, such as semantic paragraph boundaries or code-block-aware splitting.
Alternative Summarization – Provide custom summarization logic in src/lib/memory/summarization.ts to implement RAG-style summaries or extractive summarization before vectorization.
Summary
- OmniRoute implements persistent conversational memory through a six-stage pipeline (extraction, summarization, storage, retrieval, injection, caching) that maintains state in Qdrant or SQLite.
- The system is stateless from the request handler perspective, storing all context in durable vector stores to enable horizontal scaling.
- Configure memory behavior via
src/app/api/settings/memory/route.tsusing toggles likeMEMORY_ENABLEDandMEMORY_MAX_TOKENS. - Store chunks using
storeChunkfromsrc/lib/memory/store.tsand retrieve context viaretrieveRelevantChunksfromsrc/lib/memory/retrieval.ts. - Built-in fault tolerance automatically falls back to no-memory mode when the vector store fails, ensuring request continuity.
Frequently Asked Questions
How does OmniRoute handle vector store failures?
If the vector store becomes unavailable, the system implemented in src/lib/memory/verify.ts automatically falls back to a no-memory mode. This ensures that requests continue to process without historical context rather than throwing errors, maintaining service availability while logging the degradation.
Can I use a different vector database instead of Qdrant?
Yes. You can replace Qdrant by implementing the VectorStore interface in a new adapter file and updating the provider configuration. The system treats src/lib/memory/qdrant.ts as a pluggable adapter, allowing you to substitute Pinecone, Weaviate, or any other vector store that supports similarity search.
How does the memory system respect token limits?
The src/lib/memory/settings.ts module enforces the MEMORY_MAX_TOKENS configuration, which caps the total token count of injected context. When retrieved chunks exceed this limit, the system drops the oldest chunks first, ensuring the most recent and relevant context remains within the budget.
Is conversation data isolated between different API keys?
Yes. The memory system supports per-API-key scoping, ensuring that conversations remain isolated between users. You can delete specific conversation histories or configure memory settings per key via the src/app/api/settings/memory/route.ts endpoint, which validates ownership before executing deletions.
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 →