Mem0
Bring your extracted facts; gain provenance and Reflexes.
Read the guide →Migration guides from popular memory and RAG stacks. Each guide preserves your existing ids for dual-write, maps your schema onto the Contexta graph, and ends with the first Reflex you could not author before.
Bring your extracted facts; gain provenance and Reflexes.
Read the guide →Keep the temporal graph; gain Reflexes and surgical forget.
Read the guide →Promote agent-local memory blocks to a shared context layer.
Read the guide →Keep the embeddings; add graph, time, and provenance.
Read the guide →Trade flat memory namespaces for a typed graph.
Read the guide →Mem0 stores extracted facts and supports multi-level memory across user, agent, and session. A Mem0 migration is mostly a mapping exercise: each Mem0 memory becomes a node in Contexta with the original Mem0 id preserved as an `external_ref` for round-tripping. Mem0 metadata (user_id, agent_id, run_id) maps onto Contexta namespaces and tags, so existing slicing keeps working. Once nodes land, the optional second pass extracts edges — relationships Mem0 stored implicitly through fact phrasing. The third pass attaches provenance: source URI, ingestion time, confidence. With the corpus shaped, Reflexes light up the parts that Mem0 cannot express — motif completion, drift, absence. Most teams finish in a weekend with the corpus dual-written for a week before flipping reads. The Mem0 client stays alive during the transition; reads can hit Contexta with a thin shim that adapts the Packet back to a Mem0-style flat list.
import { Contexta } from '@contexta/sdk';
import { Mem0 } from 'mem0ai';
const ctx = new Contexta({ apiKey: process.env.CONTEXTA_API_KEY });
const mem0 = new Mem0({ apiKey: process.env.MEM0_API_KEY });
const memories = await mem0.getAll({ userId: 'user_123' });
await ctx.import({
namespace: 'user_123',
source: 'mem0',
records: memories.map((m) => ({
externalRef: m.id,
content: m.memory,
metadata: m.metadata,
createdAt: m.createdAt,
})),
});Zep ships a temporal knowledge graph (Graphiti) with valid-time edges. The migration story is therefore the easiest of the five — the temporal model carries over almost verbatim. Zep nodes become Contexta nodes; Zep edges become Contexta edges with the same valid-from / valid-to bounds. Session-level chat history maps to a Conversation node with utterance nodes attached. The structural upgrade is what Zep does not express: Reflexes on motif completion, surgical forget with provenance cascade, and policy-aware retrieval. Most teams keep Zep online for a week of dual-write while migrating retrievers; Contexta exposes a Zep-compatible context-assembly endpoint so existing prompt templates keep working until you choose to switch them. The longer-term payoff is governance: namespaces, KMS profiles, audit ledgers, and replayable retraction — features Zep does not currently ship.
import { Contexta } from '@contexta/sdk';
import { ZepClient } from '@getzep/zep-cloud';
const ctx = new Contexta({ apiKey: process.env.CONTEXTA_API_KEY });
const zep = new ZepClient({ apiKey: process.env.ZEP_API_KEY });
const facts = await zep.graph.search({ groupId: 'tenant_a', limit: 10_000 });
await ctx.importGraph({
namespace: 'tenant_a',
source: 'zep',
nodes: facts.nodes.map((n) => ({ externalRef: n.uuid, ...n })),
edges: facts.edges.map((e) => ({
externalRef: e.uuid,
from: e.sourceNodeUuid,
to: e.targetNodeUuid,
validFrom: e.validAt,
validTo: e.invalidAt,
})),
});Letta agents own their memory as editable blocks inside the agent runtime. Migration is therefore a re-shaping job: each Letta block becomes a node in Contexta, scoped to the agent's namespace. Block content is parsed for entities and relationships during import so the flat block text gains graph structure. Letta core_memory blocks become long-lived "agent profile" nodes; recall_memory becomes timestamped utterance nodes. Once shaped, the agent calls Contexta for retrieval and writes new observations back as nodes — but you keep Letta for the agent loop itself. The net effect: Letta still runs the agent, but the memory is now graph-native, bi-temporal, and reactive, and it is shared across agents instead of locked inside one runtime.
import { Contexta } from '@contexta/sdk';
import { LettaClient } from '@letta-ai/letta-client';
const ctx = new Contexta({ apiKey: process.env.CONTEXTA_API_KEY });
const letta = new LettaClient({ apiKey: process.env.LETTA_API_KEY });
const state = await letta.agents.export({ agentId: 'agent_42' });
await ctx.import({
namespace: 'agent_42',
source: 'letta',
records: [
...state.coreMemory.map((b) => ({ kind: 'profile', content: b.value })),
...state.recallMemory.map((m) => ({
kind: 'event',
content: m.text,
createdAt: m.timestamp,
})),
],
});Custom Pinecone-RAG stacks are the most common starting point. The shape is familiar: a chunker, an embedder, Pinecone, a retriever, an LLM. The migration does not throw any of it away — Contexta uses embeddings under the hood and can either ingest Pinecone vectors directly or call into your existing index. The lift is the graph: each chunk becomes a node, the source document becomes a parent node, and an entity-extraction pass surfaces edges Pinecone could never store. Citations get a contract — every retrieved fact carries the source URI and span. Bi-temporal validity attaches to each fact, so "what did we believe in March" becomes a query rather than a forensic exercise. Reflexes turn the corpus from a passive index into a reactive substrate.
import { Contexta } from '@contexta/sdk';
import { Pinecone } from '@pinecone-database/pinecone';
const ctx = new Contexta({ apiKey: process.env.CONTEXTA_API_KEY });
const pc = new Pinecone({ apiKey: process.env.PINECONE_API_KEY });
const index = pc.index('docs');
const { matches } = await index.query({
topK: 10_000,
vector: new Array(1536).fill(0),
includeMetadata: true,
});
await ctx.importVectors({
namespace: 'docs',
source: 'pinecone',
records: matches.map((m) => ({
externalRef: m.id,
embedding: m.values,
content: String(m.metadata?.text ?? ''),
sourceUri: String(m.metadata?.source ?? ''),
})),
});LangMem ships memory primitives inside the LangChain ecosystem — namespaces, semantic memories, and reflective writes triggered from agent runs. The migration mirrors the Mem0 path with one twist: LangMem memories are typically already scoped to a LangGraph thread, so the thread maps cleanly onto a Conversation node in Contexta. Memories become nodes within the conversation, and the reflective-write trigger becomes a Reflex that fires on conversation turns. The dual-write window is shorter because LangMem deployments are usually newer and lower-volume than Mem0. The payoff is independence from the LangChain release cadence: Contexta sits behind whatever framework you pick this quarter, and your corpus survives the swap.
import { Contexta } from '@contexta/sdk';
import { InMemoryStore } from '@langchain/langgraph';
const ctx = new Contexta({ apiKey: process.env.CONTEXTA_API_KEY });
const store: InMemoryStore = /* your LangMem-backed store */ undefined!;
const memories = await store.search(['thread_42', 'memories']);
await ctx.import({
namespace: 'thread_42',
source: 'langmem',
records: memories.map((m) => ({
externalRef: m.key,
content: JSON.stringify(m.value),
createdAt: m.createdAt,
})),
});Bring your existing memory store. We will help you map your schema, plan the dual-write window, and ship the first Reflex.