docs/migration/oss-v2-to-v3.mdx
The new Mem0 release redesigns both extraction and retrieval, and cleans up the SDK surface across Python and TypeScript:
add() / delete_all(), inside filters for search() / get_all()These changes produce a +20 point improvement on LoCoMo (71.4 → 91.6) and +26 point improvement on LongMemEval (67.8 → 93.4), while cutting extraction latency roughly in half.
| Change | Old | New | Migration |
|---|---|---|---|
search() / get_all() entity IDs | Top-level kwargs (user_id="...") | Inside filters dict | m.search("q", filters={"user_id": "..."}): top-level kwargs now raise ValueError |
top_k default | 100 | 20 | Pass top_k=100 explicitly to restore |
threshold default | None (no filtering) | 0.1 (filters low-relevance) | Pass threshold=0.0 for old behavior |
threshold validation | Any float | Must be in [0, 1] | Out-of-range values now raise ValueError |
rerank default | True | False | Pass rerank=True to restore |
| Entity ID validation | Accepted any string | Trimmed; empty / whitespace-only rejected (ValueError) | Pass a non-empty identifier without internal spaces |
messages in add() | Could be None | Must be str / dict / list[dict]: other types raise Mem0ValidationError (code VALIDATION_003) | Always pass a string, dict, or list of messages |
add() events | Returns ADD, UPDATE, DELETE | Returns ADD only | Update code expecting UPDATE/DELETE |
| Custom extraction prompt | custom_fact_extraction_prompt | custom_instructions | Rename in config |
| Custom update prompt | custom_update_memory_prompt | Deprecated | Use custom_instructions instead |
| Graph memory | enable_graph + graph_store in config | Removed | Graph memory is removed from OSS. It's a built-in, always-on Mem0 Platform feature |
| Qdrant client | >=1.9.1 | >=1.12.0 | Update dependency |
| Upstash client | >=0.1.0 | >=0.6.0 | Update dependency |
| Change | Old | New | Migration |
|---|---|---|---|
| Search parameter | search(query, { limit: 10 }) | search(query, { topK: 10 }) | Rename limit → topK |
topK default | 100 | 20 | Pass topK: 100 explicitly to restore |
search() / getAll() entity IDs | Top-level options (userId: "...") | Inside filters object | m.search("q", { filters: { userId: "..." } }) |
threshold validation | Any number | Must be in [0, 1] | Out-of-range values now throw |
| Entity ID validation | Any string | Trimmed; empty / whitespace-only rejected | Pass non-empty identifiers without internal spaces |
messages in add() | Could be null / undefined | Required: throws on null/undefined | Always pass a string or array |
| Payload key for lemmatized text | text_lemmatized (snake_case) | textLemmatized (camelCase) | TS-only internal field. If you share a vector store collection between Python and TS SDKs, lemma-based BM25 will not resolve across languages: keep collections language-scoped. |
| Custom prompt | customPrompt | customInstructions | Rename in config |
| Graph memory | enableGraph + graphStore in config | Removed | Graph memory is removed from OSS. It's a built-in, always-on Mem0 Platform feature |
| Default graph config | Neo4j default config applied | No default graph config | Graph store config is no longer read; OSS has no built-in graph config to fall back to |
| Change | Old | New | Migration |
|---|---|---|---|
| Constructor | MemoryClient(api_key, org_id, project_id) | MemoryClient(api_key) | Remove org_id, project_id from constructor |
| Method options | client.add(messages, **kwargs) | client.add(messages, options=AddMemoryOptions(...)) | Use typed option classes (or **kwargs still works) |
| Removed params | api_version, output_format, async_mode, filter_memories, keyword_search, force_add_only, batch_size, immutable, includes, excludes, enable_graph, org_name, project_name | N/A | Remove from all calls |
| Change | Old | New | Migration |
|---|---|---|---|
| Constructor | new MemoryClient({ apiKey, organizationId, projectId }) | new MemoryClient({ apiKey }) | Remove organizationId, projectId, organizationName, projectName |
| All params | snake_case: user_id, agent_id, top_k | camelCase: userId, agentId, topK | Rename all params to camelCase |
| Removed params | api_version, output_format, async_mode, enable_graph, org_id, project_id, org_name, project_name, filter_memories, batch_size, force_add_only, immutable, includes, excludes, keyword_search | N/A | Remove from all calls |
| Output format enum | OutputFormat.V1, OutputFormat.V1_1 | Removed | v1.1 is now always used |
| API version enum | API_VERSION.V1, API_VERSION.V2 | Removed | Handled internally |
# For hybrid search + entity extraction (recommended)
pip install --upgrade "mem0ai[nlp]"
python -m spacy download en_core_web_sm
# Qdrant users: also install fastembed to enable BM25 keyword search
pip install fastembed
```
<Info>
**Supported Python versions for `[nlp]` extras: 3.10 – 3.12.** spaCy and its `blis` / `thinc` dependencies do not yet ship prebuilt wheels for Python 3.13, so installs on 3.13 will fail at build time. Use Python 3.12 (or older) for the `[nlp]` extras until upstream support lands. The base `mem0ai` package works on all supported Python versions; only the NLP extras are constrained.
</Info>
pip install fastembed
# After
config = {
"custom_instructions": "Focus on user preferences", # [OK] New name
# custom_update_memory_prompt removed: use custom_instructions
# enable_graph and graph_store removed: graph memory is now a Mem0 Platform feature
}
```
// After
const config = {
customInstructions: "Focus on user preferences", // [OK] New name
// enableGraph and graphStore removed: graph memory is now a Mem0 Platform feature
};
```
# After: entity IDs go inside `filters` (matches Platform API)
results = m.search(
"what meetings did I attend?",
filters={"user_id": "alice"}, # [REMOVED top-level kwarg, use filters]
top_k=20, # New default is 20 (was 100)
threshold=0.1, # New default (pass 0.0 to disable)
rerank=False # New default (pass True to restore)
)
for r in results:
print(r["score"])
```
<Warning>
Passing `user_id`, `agent_id`, or `run_id` as a top-level kwarg to `search()` or `get_all()` now raises `ValueError`. They must be inside the `filters` dict. The change aligns the OSS SDK with the Platform API contract.
</Warning>
// After: entity IDs go inside `filters` (matches Platform API)
const results = await m.search("what meetings did I attend?", {
filters: { userId: "alice" }, // [REMOVED top-level option, use filters]
topK: 20 // [OK] Renamed from 'limit'
});
```
# Before
client = MemoryClient(api_key="...", org_id="org-1", project_id="proj-1")
results = client.search("query", user_id="alice", top_k=20, enable_graph=True)
# After
client = MemoryClient(api_key="...") # org_id, project_id removed
results = client.search(
"query",
options=SearchMemoryOptions(
filters={"user_id": "alice"},
top_k=20
)
)
```
// After
const client = new MemoryClient({ apiKey: "..." });
const results = await client.search("query", {
filters: { userId: "alice" },
topK: 20
});
```
# After: only ADD events
result = m.add("I love hiking and my dog's name is Max", user_id="alice")
for item in result["results"]:
print("New memory:", item["memory"]) # Only ADD events
```
# Before
client.add(messages, user_id="alice", async_mode=True, output_format="v1.1")
# After: async_mode and output_format removed (async by default, v1.1 always)
client.add(
messages,
options=AddMemoryOptions(user_id="alice")
)
# Or using **kwargs
client.add(messages, user_id="alice")
```
// After
await client.add(messages, {
userId: "alice" // [OK] camelCase
});
```
If you're using Qdrant or Upstash, update your client libraries:
# Qdrant users
pip install "qdrant-client>=1.12.0"
# Upstash users
pip install "upstash-vector>=0.6.0"
The new algorithm automatically creates a parallel collection named {your_collection}_entities to power entity matching, the third signal in hybrid search. No manual setup is required: it's created on first use. This is separate from and unrelated to graph memory, which is a Mem0 Platform feature.
Graph memory is removed from the open-source SDK. It is not being replaced by an OSS equivalent: graph memory is a Mem0 Platform feature, built in and always on, with no external graph database required. See Graph Memory for what it does on Platform.
What was removed from OSS:
enable_graph / enableGraph config flaggraph_store / graphStore configuration blockMigration:
enable_graph / enableGraph from your configgraph_store / graphStore block: it is no longer readInput conversation
→ Retrieve top-10 related existing memories (for deduplication context)
→ Single LLM call: extract all distinct new facts
→ Batch embed extracted memories
→ Hash-based deduplication (MD5, prevents exact duplicates)
→ Batch insert into vector store
→ Entity extraction (for entity matching)
The previous algorithm used two LLM calls: one to extract candidate facts, one to decide ADD/UPDATE/DELETE actions against existing memories. The new algorithm collapses this into a single call that only adds. The model spends its capacity on understanding the input rather than diffing against existing state.
Query
→ Preprocess (lemmatize keywords, extract entities)
→ Parallel scoring:
1. Semantic search (vector similarity)
2. BM25 keyword search (normalized term matching)
3. Entity matching (entity overlap boost)
→ Score fusion → Top-K selection
Scoring: The three signals are normalized and fused into a single combined score per result. The fusion adapts based on which signals are available at runtime (semantic-only, semantic + BM25, or all three when spaCy + the entity store are active).
BM25 is a boost signal, not a recall expander. Only semantic search results are candidates: BM25 and entity scores boost ranking but don't add new candidates.
All 15 supported vector stores have been enhanced with two new capabilities:
| Capability | Purpose | Fallback if Unsupported |
|---|---|---|
keyword_search() | BM25/full-text keyword matching | Falls back to semantic-only search |
search_batch() | Batch search for entity matching | Falls back to sequential search |
Qdrant-specific changes:
fastembed library for BM25 encoding (lazy-loaded, gracefully degrades)pip install fastembedAll other vector stores:
keyword_search() methods using their native full-text capabilitiesThe new features degrade gracefully when optional dependencies are missing:
| Missing Dependency | Impact | Search Still Works? |
|---|---|---|
spaCy (mem0ai[nlp]) | No entity extraction, no BM25 lemmatization | Yes (semantic-only) |
fastembed (Qdrant) | No BM25 keyword search | Yes (semantic + entity matching) |
| Entity matching store unavailable | No entity matching boost | Yes (semantic + BM25) |
You always get semantic search. Hybrid search features layer on top when available.
These parameters have been removed across all SDKs. Remove them from your code:
Constructor: org_id, project_id
All methods: api_version, output_format, async_mode, org_name, project_name, org_id, project_id
add(): enable_graph, immutable, filter_memories, batch_size, force_add_only, includes, excludes, keyword_search
search(): enable_graph
get_all(): enable_graph
project.update(): enable_graph
Constructor: organizationId, projectId, organizationName, projectName
All methods: OutputFormat enum, API_VERSION enum
add(): enable_graph / enableGraph, async_mode / asyncMode, output_format / outputFormat, immutable, filter_memories / filterMemories, batch_size / batchSize, force_add_only / forceAddOnly, includes, excludes, keyword_search / keywordSearch
search(): enable_graph / enableGraph
get_all(): enable_graph / enableGraph
Config: custom_fact_extraction_prompt → renamed to custom_instructions
Config: custom_update_memory_prompt → deprecated, use custom_instructions
Config: enable_graph + graph_store → removed (graph memory is now a Mem0 Platform feature)
Config: customPrompt → renamed to customInstructions
Config: enableGraph + graphStore → removed (graph memory is now a Mem0 Platform feature)
search(): limit → renamed to topK
limit is not a valid parameterThe limit parameter has been renamed to topK in the TypeScript OSS:
// Before
const results = await m.search("query", { userId: "alice", limit: 20 });
// After
const results = await m.search("query", { filters: { userId: "alice" }, topK: 20 });
All TypeScript Client SDK parameters now use camelCase. The SDK handles conversion to/from the API automatically:
// Before
await client.search("query", { user_id: "alice", top_k: 20 });
// After
await client.search("query", { filters: { userId: "alice" }, topK: 20 });
ValueError: Top-level entity parameters not supported in search() / get_all()search() and get_all() now require entity IDs inside filters. Top-level kwargs raise ValueError. This aligns the OSS SDK with the Platform API.
# Before
results = m.search("query", user_id="alice", top_k=20)
# After
results = m.search("query", filters={"user_id": "alice"}, top_k=20)
add() and delete_all() continue to accept entity IDs as top-level kwargs.
The default threshold changed from None to 0.1. Low-relevance results that were previously included are now filtered out. To restore the old behavior:
results = m.search("query", filters={"user_id": "alice"}, threshold=0.0)
If you see errors about missing spaCy models, download the required model:
python -m spacy download en_core_web_sm
If spaCy is not installed at all, install the NLP extras:
pip install "mem0ai[nlp]"
The entity matching store tries to create a {collection_name}_entities collection automatically. If your vector database has restricted permissions, pre-create this collection with the same embedding dimensions as your main collection.
The top-level score still ranges [0, 1], but it is computed differently in v3. Relative ranking between results stays comparable, but absolute numbers shift: retune any hard thresholds in your app against representative queries.
If you need the raw cosine similarity for a specific use case, run an unboosted vector query directly against your vector store via vector_store.search(...).