A C++17 implementation of the mem0 memory layer, built on Qt. Provides long-term fact-level memory for LLM applications — extract, deduplicate, store, and retrieve user context across conversations.
mem0-cpp reimplements the core architecture of mem0ai/mem0 (Apache 2.0) in C++ with Qt as the runtime framework. It is not a wrapper or binding — all core logic (fact extraction pipeline, hybrid retrieval, entity linking, storage) is a from-scratch C++ implementation designed for desktop and embedded environments where Python is unavailable.
| Aspect | mem0 (Python) | mem0-cpp |
|---|---|---|
| Language | Python | C++17 |
| Runtime | Python + spaCy + OpenAI/Ollama | Qt 5/6 |
| Storage | Qdrant / ChromaDB / etc. | SQLite (WAL, FTS5) |
| NER | spaCy statistical models | Rule-based (regex + heuristics) |
| Parallelism | asyncio | QtConcurrent |
| Embedding | OpenAI / Ollama HTTP | Abstract IEmbeddingClient (inject any backend) |
| LLM | OpenAI / Ollama HTTP | Abstract ILLMClient (inject any backend) |
| Prompts | Ported from mem0/configs/prompts.py |
Ported verbatim |
| Scoring | Graph-based + BM25 | Cosine + sigmoid-normalized BM25 + entity boost |
The extraction prompts are direct ports of mem0's ADDITIVE_EXTRACTION_PROMPT (v3) and DEFAULT_UPDATE_MEMORY_PROMPT (v1). The scoring pipeline preserves the original's three-signal fusion (semantic + keyword + entity) with C++ implementations of sigmoid BM25 normalization and cosine similarity.
flowchart LR
subgraph Input
M[Messages]
end
subgraph mem0-cpp
direction TB
E[LLM Fact Extraction] --> D[SHA-256 Dedup]
D --> S[SQLite Storage]
S --> R[Hybrid Retrieval]
R --> CS[Cosine Similarity]
R --> BM25[FTS5 BM25]
R --> EN[Entity Boost]
CS --> F[Score Fusion]
BM25 --> F
EN --> F
EL[EntityLinker] --> EN
end
subgraph External - inject via interfaces
LLM[ILLMClient]
EMB[IEmbeddingClient]
end
M --> E
LLM --> E
EMB --> S
EMB --> EL
| Component | File | Description |
|---|---|---|
Mem0 |
src/mem0.h/.cpp |
Main orchestrator: extract → embed → dedup → store → retrieve |
Storage |
src/storage.h/.cpp |
SQLite unified storage (embeddings, FTS5, entities, history) |
EntityLinker |
src/entitylinker.h/.cpp |
Rule-based NER + entity-memory linking |
Scoring |
src/scoring.h/.cpp |
Sigmoid BM25 normalization, cosine similarity |
Prompts |
src/prompts.h/.cpp |
Fact extraction prompts (ported from mem0) |
Utils |
src/utils.h/.cpp |
SHA-256 hashing, vector serialization, text utilities |
Interfaces |
src/interfaces.h |
ILLMClient / IEmbeddingClient abstract adapters |
# Prerequisites: Qt 5.15+ or Qt 6, CMake 3.16+
mkdir build && cd build
cmake .. -DCMAKE_PREFIX_PATH=/path/to/Qt
make -j$(nproc)#include <mem0.h>
#include <interfaces.h>
// 1. Implement abstract interfaces
class MyLLMClient : public mem0::ILLMClient {
QString generate(const QList<QPair<QString, QString>> &messages,
bool jsonMode) override;
};
class MyEmbeddingClient : public mem0::IEmbeddingClient {
QVector<float> embed(const QString &text) override;
QList<QVector<float>> embedBatch(const QStringList &texts) override;
int dimension() const override;
};
// 2. Create Mem0 instance
mem0::Mem0::Config config;
config.dbPath = "/path/to/mem0.db";
mem0::Mem0 m(new MyLLMClient(), new MyEmbeddingClient(), config); // tables created in constructor
// 3. Add memories from a conversation
QList<mem0::Message> messages = {{"user", "I work at UnionTech"}, {"assistant", "Great!"}};
auto results = m.add(messages, "user_42");
// results: [{id: "uuid", memory: "User works at UnionTech", event: "ADD"}]
// 4. Search with hybrid retrieval
auto searchResults = m.search("What does the user do for a living?", "user_42");
// returns ranked memories with fused scores# In your project's CMakeLists.txt
add_subdirectory(path/to/mem0-cpp)
target_link_libraries(your_target mem0)Outputs a static library libmem0.a (or mem0.lib on Windows).
| Method | Description |
|---|---|
add(messages, userId) |
Extract facts from messages, dedup, store |
add(text, userId) |
Convenience overload for single text |
addBatch(batch, userId) |
Parallel extraction across multiple conversations |
search(query, userId, topK, threshold) |
Hybrid retrieval: semantic + BM25 + entity boost |
getAll(userId, topK) |
List all memories for a user |
update(memoryId, text) |
Update a memory's text and re-embed |
remove(memoryId) |
Delete a single memory |
removeAll(userId) |
Delete all memories for a user |
history(memoryId) |
Get change history for a memory |
| Method | Description |
|---|---|
generate(messages, jsonMode) |
Send chat messages, return text response |
| Method | Description |
|---|---|
embed(text) |
Single text → vector |
embedBatch(texts) |
Multiple texts → vectors |
dimension() |
Vector dimension |
cd build
make
./test_mem0 # 28 tests, ~200ms
ctest --output-on-failureTests use mock LLM/embedding clients (no external services required).
- SQLite-only storage: No external vector database dependency. Embeddings are stored as BLOBs in SQLite with WAL mode. FTS5 handles keyword retrieval. Suitable for single-user desktop applications with moderate memory counts.
- Dependency injection via abstract interfaces:
ILLMClientandIEmbeddingClientare pure virtual. Wire in any backend — local models, HTTP APIs, D-Bus services. - Rule-based NER instead of spaCy: Desktop applications typically cannot ship spaCy models. The
EntityLinkeruses regex-based extraction for four entity types (PROPER, QUOTED, TOPIC, IDENTIFIER) with embedding-based semantic matching for disambiguation. - SHA-256 for dedup: Text content is hashed with SHA-256 before storage. Existing hashes are queried via lightweight SQL to avoid full record loading.
- Qt 5.15+ or Qt 6.x (Core, Sql, Concurrent modules)
- CMake 3.16+
- C++17 compiler
Optional: Qt Network module (for the built-in LLMClient HTTP implementation)
Apache License 2.0 — see LICENSE.
This project reuses extraction prompts from mem0ai/mem0 (Apache 2.0). The C++ implementation is an independent reimplementation, not derived from the Python source code.