Clean up release highlights copy and removing emdash from the docs (#5984)

This commit is contained in:
Kartik
2026-06-29 21:27:53 +05:30
committed by GitHub
parent 754034abbc
commit b2ff3aeda5
73 changed files with 572 additions and 557 deletions
+28 -28
View File
@@ -17,7 +17,7 @@ The new Mem0 release redesigns both extraction and retrieval, and cleans up the
- **Retrieval**: Multi-signal hybrid search (semantic + BM25 keyword + entity matching)
- **Entity linking**: Automatic entity extraction and cross-memory linking
- **SDK cleanup**: Deprecated parameters removed, naming conventions standardized
- **API surface aligned with Platform**: Entity IDs now follow the same convention across OSS and Platform — top-level kwargs for `add()` / `delete_all()`, inside `filters` for `search()` / `get_all()`
- **API surface aligned with Platform**: Entity IDs now follow the same convention across OSS and Platform: top-level kwargs for `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.
@@ -27,13 +27,13 @@ These changes produce a **+20 point improvement on LoCoMo** (71.4 → 91.6) and
| 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` |
| `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 |
| `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 |
@@ -50,8 +50,8 @@ These changes produce a **+20 point improvement on LoCoMo** (71.4 → 91.6) and
| `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. |
| `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 store support has been removed entirely |
| Default graph config | Neo4j default config applied | No default graph config | Graph store config is no longer used |
@@ -62,7 +62,7 @@ These changes produce a **+20 point improvement on LoCoMo** (71.4 → 91.6) and
|---|---|---|---|
| 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` | — | Remove from all calls |
| 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 |
### TypeScript Client SDK
@@ -70,7 +70,7 @@ These changes produce a **+20 point improvement on LoCoMo** (71.4 → 91.6) and
|---|---|---|---|
| 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` | — | Remove from all calls |
| 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 |
@@ -108,7 +108,7 @@ The Python `[nlp]` extra installs [spaCy](https://spacy.io/) for entity extracti
</Info>
<Warning>
**Qdrant users — install `fastembed` to enable BM25 keyword search.** The Qdrant backend uses [fastembed](https://github.com/qdrant/fastembed) to encode sparse (BM25) vectors alongside dense vectors in the same collection. Without it, BM25 is silently disabled and search falls back to semantic-only — you'll see a log warning `"fastembed not installed — BM25 keyword search disabled"` on the first search call. Other vector stores use their native full-text capabilities and don't need `fastembed`.
**Qdrant users: install `fastembed` to enable BM25 keyword search.** The Qdrant backend uses [fastembed](https://github.com/qdrant/fastembed) to encode sparse (BM25) vectors alongside dense vectors in the same collection. Without it, BM25 is silently disabled and search falls back to semantic-only. The first search call logs a warning that BM25 keyword search is disabled. Other vector stores use their native full-text capabilities and don't need `fastembed`.
```bash
pip install fastembed
@@ -134,8 +134,8 @@ 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 store support has been removed
# custom_update_memory_prompt removed: use custom_instructions
# enable_graph and graph_store removed: graph store support has been removed
}
```
</Tab>
@@ -154,7 +154,7 @@ pip install fastembed
// After
const config = {
customInstructions: "Focus on user preferences", // [OK] New name
// enableGraph and graphStore removed — graph store support has been removed
// enableGraph and graphStore removed: graph store support has been removed
};
```
</Tab>
@@ -165,7 +165,7 @@ pip install fastembed
<Tabs>
<Tab title="Python OSS">
```python
# Before — entity IDs as top-level kwargs
# Before: entity IDs as top-level kwargs
results = m.search(
"what meetings did I attend?",
user_id="alice",
@@ -174,7 +174,7 @@ pip install fastembed
for r in results:
print(r["score"]) # Was raw cosine similarity
# After — entity IDs go inside `filters` (matches Platform API)
# 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]
@@ -192,13 +192,13 @@ pip install fastembed
</Tab>
<Tab title="TypeScript OSS">
```typescript
// Before — entity IDs as top-level options
// Before: entity IDs as top-level options
const results = await m.search("what meetings did I attend?", {
userId: "alice",
limit: 20 // [REMOVED] Renamed to 'topK' for consistency
});
// After — entity IDs go inside `filters` (matches Platform API)
// 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'
@@ -254,7 +254,7 @@ pip install fastembed
<Tabs>
<Tab title="Python OSS">
```python
# Before — could return ADD, UPDATE, DELETE events
# Before: could return ADD, UPDATE, DELETE events
result = m.add("I love hiking and my dog's name is Max", user_id="alice")
for item in result["results"]:
if item["event"] == "ADD":
@@ -264,7 +264,7 @@ pip install fastembed
elif item["event"] == "DELETE":
print("Deleted:", item["memory"]) # [REMOVED] No longer returned
# After — only ADD events
# 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
@@ -277,7 +277,7 @@ pip install fastembed
# 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)
# After: async_mode and output_format removed (async by default, v1.1 always)
client.add(
messages,
options=AddMemoryOptions(user_id="alice")
@@ -305,7 +305,7 @@ pip install fastembed
</Tabs>
<Tip>
The ADD-only model means memories accumulate over time. When information changes, the new fact is stored alongside the old one. Retrieval handles ranking — the most relevant, current information surfaces first.
The ADD-only model means memories accumulate over time. When information changes, the new fact is stored alongside the old one. Retrieval handles ranking: the most relevant, current information surfaces first.
</Tip>
### 5. Update Vector Store Dependencies
@@ -322,7 +322,7 @@ pip install "upstash-vector>=0.6.0"
### 6. Entity Store Setup
The new algorithm automatically creates a parallel entity store collection named `{your_collection}_entities`. No manual setup is required — it's created on first use.
The new algorithm automatically creates a parallel entity store collection named `{your_collection}_entities`. No manual setup is required: it's created on first use.
<Warning>
Make sure your vector store user/credentials have permission to create new collections. If you're using a managed vector database with restricted permissions, pre-create the `{collection_name}_entities` collection with the same embedding dimensions as your main collection.
@@ -343,7 +343,7 @@ Mem0 now builds the graph itself. It extracts entities (proper nouns, quoted tex
**Migration:**
- Remove `enable_graph` / `enableGraph` from your config
- Remove the `graph_store` / `graphStore` block — it is no longer read
- Remove the `graph_store` / `graphStore` block: it is no longer read
- Uninstall external graph drivers (neo4j, memgraph, etc.) if you were using them only for Mem0
- No data migration is required. Built-in graph memory activates automatically on the next `add()` call.
@@ -365,7 +365,7 @@ Input conversation
→ Entity extraction + linking
```
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.
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.
### Retrieval: Multi-Signal Hybrid Search
@@ -381,7 +381,7 @@ Query
**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.
**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.
## Vector Store Compatibility
@@ -417,7 +417,7 @@ You always get semantic search. Hybrid search features layer on top when availab
These parameters have been removed across all SDKs. Remove them from your code:
### Python Client SDK — Removed parameters
### Python Client SDK: Removed parameters
**Constructor:** `org_id`, `project_id`
@@ -431,7 +431,7 @@ These parameters have been removed across all SDKs. Remove them from your code:
**project.update():** `enable_graph`
### TypeScript Client SDK — Removed parameters
### TypeScript Client SDK: Removed parameters
**Constructor:** `organizationId`, `projectId`, `organizationName`, `projectName`
@@ -443,7 +443,7 @@ These parameters have been removed across all SDKs. Remove them from your code:
**get_all():** `enable_graph` / `enableGraph`
### Python OSS — Removed/renamed parameters
### Python OSS: Removed/renamed parameters
**Config:** `custom_fact_extraction_prompt` → renamed to `custom_instructions`
@@ -451,7 +451,7 @@ These parameters have been removed across all SDKs. Remove them from your code:
**Config:** `enable_graph` + `graph_store` → removed (graph store support removed entirely)
### TypeScript OSS — Removed/renamed parameters
### TypeScript OSS: Removed/renamed parameters
**Config:** `customPrompt` → renamed to `customInstructions`
@@ -527,7 +527,7 @@ The entity store tries to create a `{collection_name}_entities` collection autom
### Score values are different from before
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.
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(...)`.
+17 -17
View File
@@ -11,12 +11,12 @@ iconType: "solid"
## Overview
The new Mem0 memory algorithm is a ground-up redesign of how memories are extracted, stored, and retrieved. It scores **91.6 on LoCoMo** and **93.4 on LongMemEval** — a +20 and +26 point improvement over the previous algorithm — while cutting extraction latency roughly in half.
The new Mem0 memory algorithm is a ground-up redesign of how memories are extracted, stored, and retrieved. It scores **91.6 on LoCoMo** and **93.4 on LongMemEval**: a +20 and +26 point improvement over the previous algorithm: while cutting extraction latency roughly in half.
| What Changed | Before | After |
|---|---|---|
| **Extraction** | Two LLM passes (extract + merge) | Single-pass ADD-only (one LLM call) |
| **Memory mutations** | ADD, UPDATE, DELETE | ADD only — nothing is overwritten or deleted |
| **Memory mutations** | ADD, UPDATE, DELETE | ADD only: nothing is overwritten or deleted |
| **Agent-generated facts** | Often ignored | First-class, stored with equal weight |
| **Graph memory** | External graph store (Neo4j, etc.) + manual setup | Built-in and automatic; entities extracted and linked across memories natively, no external store |
| **Retrieval** | Semantic (vector) only | Hybrid retrieval combining multiple signals |
@@ -27,9 +27,9 @@ The new Mem0 memory algorithm is a ground-up redesign of how memories are extrac
The previous algorithm could UPDATE or DELETE existing memories during extraction. The new algorithm only adds new facts. When information changes (e.g., a user moves from New York to San Francisco), both facts are preserved with temporal context. This means:
- **More memories over time** — your memory count will grow rather than plateau
- **Better temporal reasoning** — the system can distinguish "used to live in New York" from "now lives in San Francisco"
- **No information loss** — facts that seemed contradictory but were actually complementary are preserved
- **More memories over time**: your memory count will grow rather than plateau
- **Better temporal reasoning**: the system can distinguish "used to live in New York" from "now lives in San Francisco"
- **No information loss**: facts that seemed contradictory but were actually complementary are preserved
<Tip>
If your application previously relied on UPDATE/DELETE behavior to keep memory counts low, the new algorithm handles this at retrieval time instead. Multi-signal retrieval ranks the most relevant, current information higher without destroying historical context.
@@ -41,7 +41,7 @@ Previously, when an agent said something like "I've booked your flight for March
### Retrieval is hybrid now
Search now uses hybrid retrieval, which improves ranking quality — especially for queries involving exact keywords, proper nouns, entities that appear across multiple memories, and time-aware queries (via Temporal Reasoning). The response shape is unchanged:
Search now uses hybrid retrieval, which improves ranking quality: especially for queries involving exact keywords, proper nouns, entities that appear across multiple memories, and time-aware queries (via Temporal Reasoning). The response shape is unchanged:
```json
{
@@ -57,7 +57,7 @@ Search now uses hybrid retrieval, which improves ranking quality — especially
}
```
The top-level `score` remains a `[0, 1]` value. Relative ranking between results stays comparable to v2, but absolute numbers shift since the scoring method changed — retune any hard thresholds in your app against representative queries. Temporal signals are applied internally during ranking and are not returned as extra client-facing fields.
The top-level `score` remains a `[0, 1]` value. Relative ranking between results stays comparable to v2, but absolute numbers shift since the scoring method changed: retune any hard thresholds in your app against representative queries. Temporal signals are applied internally during ranking and are not returned as extra client-facing fields.
## API Changes
@@ -142,7 +142,7 @@ curl -X POST 'https://api.mem0.ai/v3/memories/?page=1&page_size=50' \
### Response Format
**Add response** — asynchronous, returns an `event_id` for polling:
**Add response**: asynchronous, returns an `event_id` for polling:
```json
{
@@ -152,9 +152,9 @@ curl -X POST 'https://api.mem0.ai/v3/memories/?page=1&page_size=50' \
}
```
Poll status via `GET /v1/event/{event_id}/` — status will be `SUCCEEDED` or `FAILED`.
Poll status via `GET /v1/event/{event_id}/`: status will be `SUCCEEDED` or `FAILED`.
**Search response** — combined multi-signal score per result:
**Search response**: combined multi-signal score per result:
```json
{
@@ -172,7 +172,7 @@ Poll status via `GET /v1/event/{event_id}/` — status will be `SUCCEEDED` or `F
}
```
**List response** — paginated envelope (new in V3):
**List response**: paginated envelope (new in V3):
```json
{
@@ -266,16 +266,16 @@ If your application previously read graph relations from the API response (`rela
<Steps>
<Step title="Review your search thresholds">
The default `threshold` is now `0.1` (previously no threshold). If your application was relying on unfiltered results, explicitly pass `threshold=0.0` in your search calls to preserve the old behavior. In most cases, the new default is better — it filters out low-relevance noise.
The default `threshold` is now `0.1` (previously no threshold). If your application was relying on unfiltered results, explicitly pass `threshold=0.0` in your search calls to preserve the old behavior. In most cases, the new default is better: it filters out low-relevance noise.
</Step>
<Step title="Review reranking usage">
Reranking is now `false` by default. If your application depended on reranked results, add `rerank=True` to your search calls. Note that reranking adds latency (~200-400ms) but can improve ordering quality for complex queries.
</Step>
<Step title="Update score handling (optional)">
The top-level `score` field continues to work as before. It is now a combined multi-signal score (semantic + keyword + entity) rather than pure cosine similarity, so the absolute numbers will differ. Relative ranking remains comparable — if you have threshold-based filtering in your app, retune on a representative query set.
The top-level `score` field continues to work as before. It is now a combined multi-signal score (semantic + keyword + entity) rather than pure cosine similarity, so the absolute numbers will differ. Relative ranking remains comparable: if you have threshold-based filtering in your app, retune on a representative query set.
</Step>
<Step title="Adjust memory count expectations">
With ADD-only extraction, memory counts will grow over time rather than being consolidated. This is by design — retrieval handles relevance ranking. If you have hard limits on memory count, consider using the memory expiration feature or periodic cleanup.
With ADD-only extraction, memory counts will grow over time rather than being consolidated. This is by design: retrieval handles relevance ranking. If you have hard limits on memory count, consider using the memory expiration feature or periodic cleanup.
</Step>
<Step title="Test with representative queries">
The biggest improvements are in temporal reasoning (+29.6 on LoCoMo), multi-hop queries (+23.1), and assistant memory recall (+53.6 on LongMemEval). Test queries in these categories to see the improvement.
@@ -297,9 +297,9 @@ If your application previously read graph relations from the API response (`rela
| **LoCoMo Overall** | 71.4 | **91.6** (+20.2) |
| **LongMemEval Overall** | 67.8 | **93.4** (+25.6) |
| **Extraction latency (p50)** | ~2.0s | **~1.0s** |
| **Mean tokens per query** | — | 6.8-7.0K (top200) |
| **Mean tokens per query** | N/A | 6.8-7.0K (top200) |
All benchmarks were run on a production-representative stack — deliberately avoiding frontier models to keep numbers representative of real production workloads.
All benchmarks were run on a production-representative stack: deliberately avoiding frontier models to keep numbers representative of real production workloads.
## FAQ
@@ -308,7 +308,7 @@ All benchmarks were run on a production-representative stack — deliberately av
No. Existing memories remain as-is. New memories added after the rollout will use the new extraction algorithm. Both old and new memories are searchable through the same retrieval pipeline.
</Accordion>
<Accordion title="Will my memory count increase faster now?">
Yes. The ADD-only approach means memories accumulate rather than being consolidated. This is intentional — the retrieval system handles ranking and relevance. If you need to manage memory volume, use the expiration date feature or the delete API.
Yes. The ADD-only approach means memories accumulate rather than being consolidated. This is intentional: the retrieval system handles ranking and relevance. If you need to manage memory volume, use the expiration date feature or the delete API.
</Accordion>
<Accordion title="Can I opt out of the new algorithm?">
The new algorithm is the default for all platform users. If you have a specific need to use the previous extraction behavior, contact support.
+6 -6
View File
@@ -34,7 +34,7 @@ No migration is needed. Copy the example env file, set your password, and start
```bash
cd server
cp .env.example .env
# Edit .env — set POSTGRES_PASSWORD (required) and OPENAI_API_KEY at minimum
# Edit .env: set POSTGRES_PASSWORD (required) and OPENAI_API_KEY at minimum
make up
```
@@ -77,17 +77,17 @@ POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_DB=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=<your-password> # required — compose will refuse to start without it
POSTGRES_PASSWORD=<your-password> # required: compose will refuse to start without it
POSTGRES_COLLECTION_NAME=memories
```
<Info>
`POSTGRES_PASSWORD` is **required** — `docker compose up` will refuse to start without it. If you previously relied on the hardcoded default, set `POSTGRES_PASSWORD=postgres`.
`POSTGRES_PASSWORD` is **required**: `docker compose up` will refuse to start without it. If you previously relied on the hardcoded default, set `POSTGRES_PASSWORD=postgres`.
</Info>
### 4. Start Only Postgres
Start **only** the Postgres container first — do **not** start the mem0 API yet.
Start **only** the Postgres container first: do **not** start the mem0 API yet.
The API runs `alembic upgrade head` on startup, which creates empty tables that
would conflict with the restore.
@@ -107,11 +107,11 @@ docker compose exec -T postgres pg_isready -q && echo "ready" || echo "not ready
docker compose exec -T postgres psql -U postgres < mem0_backup.sql
```
You may see notices like `role "postgres" already exists` — these are safe to ignore.
You may see notices like `role "postgres" already exists`: these are safe to ignore.
<Warning>
You must restore **before** starting the mem0 API container. The API runs
database migrations on startup which create empty tables — restoring after
database migrations on startup which create empty tables: restoring after
that would fail with duplicate-key errors and lose your API keys and settings.
</Warning>