Skip to main content

Migrate from Mem0

Export your memories, bring them across and keep your Mem0 calls: tested, with real output.

6 min readUpdated November 2026

Switch one import

Method names, keyword arguments and return shapes follow Mem0 2.x. Pass a SQLite path, a store or a database URL, as for Memory.

app.pypython
# from mem0 import Memory
from llmslim.memory.compat import Mem0Memory as Memory

memory = Memory("memory.db")
memory.add([{"role": "user", "content": "I moved to Pune last month."}], user_id="asha")

hits = memory.search("Where does Asha live?", filters={"user_id": "asha"})
for hit in hits["results"]:
    print(hit["score"], hit["memory"])
print(hits["context"])  # the selected turns, ready for a prompt

What it does differently

The differences are deliberate, and they fail loudly rather than silently:

  • Nothing is extracted. add stores turns verbatim; every returned event is ADD.
  • search selects the turns that answer the question within a token budget (an LLMSlim extension, default 2,000), returns them ranked by a lexical overlap score, and also returns the prompt-ready text under "context". rerank=True uses LLMSlim's optional cross-encoder (llmslim[semantic]); threshold and explain are ignored.
  • Filters match user_id, agent_id and run_id exactly, and at least one is required. Any other filter key raises ValueError instead of being ignored. Scope ids go inside filters, as Mem0 2.x requires.
  • expiration_date and reference_date aren't supported and raise.
  • get_all returns the newest top_k (default 20) of a scope; history returns a turn's current state only, since there is no extraction history; reset raises, rather than deleting every user's memory.
  • from_config ignores LLM, embedder and vector-store settings; a {"store": path} entry selects a SQLite file.

Export your memories from Mem0

Mem0's get_all lists a scope's memories as {"results": [...]}; on the Platform it is paged, 200 at a time, with a next link while more remain. Write each memory as one JSON line. The Platform's memory export writes JSON shaped by a schema you define, which isn't this format.

export_mem0.pypython
import json

from mem0 import MemoryClient

client = MemoryClient()  # reads MEM0_API_KEY
user_ids = ["asha", "ravi"]  # the users whose memories you are moving

with open("mem0-export.jsonl", "w", encoding="utf-8") as out:
    for user_id in user_ids:
        page = 1
        while True:
            batch = client.get_all(filters={"user_id": user_id}, page=page, page_size=200)
            for memory in batch["results"]:
                memory.setdefault("user_id", user_id)
                out.write(json.dumps(memory, ensure_ascii=False) + "\n")
            if not batch.get("next"):
                break
            page += 1

# Open-source Mem0 returns every memory of a scope in one call:
#   Memory.from_config(config).get_all(filters={"user_id": user_id}, top_k=100_000)["results"]
mem0-export.jsonl (2 of 6 lines)json
{"id": "3f1c2a9e-5b7d-4e21-9a0c-1d2e3f4a5b01", "memory": "Is allergic to peanuts", "user_id": "asha", "metadata": null, "categories": ["health"], "created_at": "2026-03-02T09:14:11.482913-08:00", "updated_at": "2026-03-02T09:14:11.482931-08:00", "expiration_date": null}
{"id": "3f1c2a9e-5b7d-4e21-9a0c-1d2e3f4a5b05", "memory": "Works as a data engineer in Bengaluru", "user_id": "ravi", "agent_id": "career-coach", "metadata": null, "categories": ["professional_details"], "created_at": "2026-06-05T22:40:00.000000-07:00", "updated_at": "2026-06-05T22:40:00.000000-07:00", "expiration_date": null}

This script follows Mem0's SDK (mem0ai 2.2.1) but wasn't run against Mem0: we have no Mem0 account. Everything below was run on a sample export in this shape, six memories for two users, and the outputs are copied from that run.

Bring your memories

llmslim import mem0 reads JSON lines like these, what get_all returns ({"results": [...]}), a plain list of memories, or {"memories": [...]}. Each memory becomes one user turn under its own user_id, agent_id and run_id, at its original time, so the filters above find it where they did. Look first with --dry-run; importing again stores only what is new.

terminalbash
llmslim import mem0 mem0-export.jsonl --db memory.db --dry-run   # look first: stores nothing
llmslim import mem0 mem0-export.jsonl --db memory.db             # import
llmslim import mem0 mem0-export.jsonl --db memory.db             # again: only what is new
outputtext
  (no session): 4 turns, 4 new, 0 already there
  (no session): 2 turns, 2 new, 0 already there
Would import Mem0 history from mem0-export.jsonl
  into:        memory.db (user asha, ravi)
  files:       1
  sessions:    2
  turns:       6 to import, 0 already there, 0 skipped
  dry run: nothing was stored
  (no session): 4 turns, 4 new, 0 already there
  (no session): 2 turns, 2 new, 0 already there
Imported Mem0 history from mem0-export.jsonl
  into:        memory.db (user asha, ravi)
  files:       1
  sessions:    2
  turns:       6 imported, 0 already there, 0 skipped
  (no session): 4 turns, 0 new, 4 already there
  (no session): 2 turns, 0 new, 2 already there
Imported Mem0 history from mem0-export.jsonl
  into:        memory.db (user asha, ravi)
  files:       1
  sessions:    2
  turns:       0 imported, 6 already there, 0 skipped

Mem0's memories are facts its model extracted ("Is allergic to peanuts"), not what was said, and they are stored as they are. From then on, what you add is stored word for word. Each turn keeps the memory's created_at as its time and its id as source_id in its metadata; Mem0's categories and metadata aren't imported. See Import your chat history for the options.

Check it

Mem0Memory answers the calls your Mem0 code makes; Memory is LLMSlim's own API, with a token budget. This ran on 0.12.0 from PyPI and on main, with the same output. The token counts come from tiktoken (pip install "llmslim[fast-tokens]"); without it they are estimated at about 4 characters per token.

check.pypython
from llmslim.memory import Memory
from llmslim.memory.compat import Mem0Memory

# Code written for Mem0 keeps its calls: the same method names, filters and result shape.
mem0_style = Mem0Memory("memory.db")
hits = mem0_style.search("What is Asha allergic to?", filters={"user_id": "asha"})
for hit in hits["results"]:
    print(f'{hit["score"]:.3f}  {hit["memory"]}')
coach = mem0_style.get_all(filters={"user_id": "ravi", "agent_id": "career-coach"})
print(len(coach["results"]), "memories for ravi with career-coach")

# LLMSlim's own API: the turns that answer the question, within a token budget.
memory = Memory("memory.db")
for question in ["Where did Asha move to?", "Where does Asha live now?"]:
    context = memory.search(question, user_id="asha", budget=40)
    print(f"{question} ({context.tokens} of {context.history_tokens} tokens)\n  {context.text}")
output (the same on 0.12.0 and on main)text
0.642  Is allergic to peanuts
0.344  Is going to Jaipur in December for her sister's wedding
0.179  Moved to Pune in September 2026, to Koregaon Park
0.000  Prefers window seats on flights
2 memories for ravi with career-coach
Where did Asha move to? (31 of 107 tokens)
  [2026-09-21 (Mon) 15:30] user: Moved to Pune in September 2026, to Koregaon Park
Where does Asha live now? (21 of 107 tokens)
  [2026-03-02 (Mon) 17:14] user: Is allergic to peanuts

The last two lines show the limit of lexical relevance. Mem0's memories are short facts without their subject, so "Where did Asha move to?" shares the word "move" with "Moved to Pune" and finds it, while "Where does Asha live now?" shares no word with it, and the budget goes to the oldest memory instead. For questions worded unlike what was stored, use relevance="semantic" (pip install "llmslim[semantic]"), or a budget that holds the whole history.

When you're ready, use Memory itself

Mem0Memory wraps llmslim.memory.Memory, which has the rest: per-call budgets and time windows, sections for standing requests, preferences and conflicts, and the stores. See the Python quickstart.

Explanations

Mem0Memory.search ignores explain. Call Memory.search(..., explain=True) to see why each turn was kept: guide.

How they compare

Mem0 extracts facts with an LLM when you add; LLMSlim stores turns and selects from them when you search, with no model call. LLMSlim vs Mem0 has the benchmark results with their confidence intervals, where Mem0 is better, and both pricing pages. Coming from Zep instead? LLMSlim vs Zep shows the closest path, through JSONL. This guide is also in the repository, as docs/MIGRATE_FROM_MEM0.md.

Questions

Does it call an LLM when I add memories?

No. add stores each turn word for word and makes no model call, so infer, prompt and memory_type are accepted and ignored.

Does it cover Mem0's hosted client?

No. Mem0Memory follows the interface of Mem0's Memory class, the open-source library, in its 2.x shape. Your memories from the hosted Platform still come across: export them with MemoryClient.get_all, as above.

How do LLMSlim and Mem0 compare?

In round 3 (LLMSlim Core 0.9.0, read by Claude Haiku 5.5), the default LLMSlim and Mem0's open-source library were level on LoCoMo at about 2,500 tokens, 91.7% and 91.0%, at about a third of the cost per question. LLMSlim vs Mem0 has every number with its confidence interval, where Mem0 is better, and both pricing pages.