Migration
This guide provides detailed instructions for migrating memories from other AI memory providers to MIF format.
Overview
Section titled “Overview”MIF supports migration from:
| Provider | Difficulty | Data Loss |
|---|---|---|
| Mem0 | Low | Minimal |
| Zep | Low | None |
| Letta (Agent File) | Medium | Minimal |
| Subcog | Low | None |
| Basic Memory | Low | Minimal |
| LangMem | Medium | Minimal |
Memory Type Mapping
Section titled “Memory Type Mapping”MIF uses three base memory types. When migrating, map provider-specific categories to these types:
| Cognitive Type | Use For | Provider Category Examples |
|---|---|---|
semantic | Facts, decisions, preferences, knowledge | preference, fact, decision, context, learning |
episodic | Events, sessions, incidents, conversations | episode, event, session, conversation |
procedural | Processes, runbooks, patterns, how-to | pattern, procedure, workflow, template |
Key Principle: The memoryType field uses one of the three base types, while specific categorization is expressed through the namespace field:
{ "memoryType": "semantic", "namespace": "_semantic/decisions"}This preserves the original provider category in the namespace hierarchy while maintaining MIF compatibility.
Source Format
Section titled “Source Format”Mem0 stores memories as JSON objects:
{ "id": "mem0_123abc", "memory": "User prefers dark mode for all applications", "user_id": "user_456", "metadata": { "category": "preference", "created_at": "2026-01-15T10:30:00Z", "custom_field": "value" }, "hash": "abc123..."}Mapping
Section titled “Mapping”| Mem0 Field | MIF Field | Notes |
|---|---|---|
id | @id | Prefix with urn:mif: |
memory | content | Direct mapping |
user_id | namespace | Use base type prefix (e.g., _semantic/preferences) |
metadata.category | memoryType | Map to base types (semantic, episodic, procedural) |
metadata.category | namespace (second part) | Preserve as namespace suffix (e.g., _semantic/{category}) |
metadata.created_at | created | Direct mapping |
metadata.* | extensions.mem0.* | Preserve in extensions |
hash | extensions.mem0.hash | Preserve hash |
Conversion Script
Section titled “Conversion Script”import jsonfrom datetime import datetimeimport uuid
def mem0_to_mif(mem0_data: dict) -> dict: """Convert Mem0 memory to MIF format with base memory types."""
# Map category to base memoryType and namespace # MIF uses semantic/episodic/procedural as base types # Specific categorization is expressed via namespace category_map = { "preference": ("semantic", "_semantic/preferences"), "fact": ("semantic", "_semantic/knowledge"), "context": ("semantic", "_semantic/knowledge"), "decision": ("semantic", "_semantic/decisions"), "conversation": ("episodic", "_episodic/sessions"), "event": ("episodic", "_episodic/sessions"), "pattern": ("procedural", "_procedural/patterns"), "procedure": ("procedural", "_procedural/runbooks"), None: ("semantic", "_semantic/knowledge") }
category = mem0_data.get("metadata", {}).get("category") memory_type, namespace = category_map.get(category, ("semantic", "_semantic/knowledge"))
# Build MIF document mif = { "@context": "https://mif-spec.dev/schema/context.jsonld", "@type": "Memory", "@id": f"urn:mif:{mem0_data['id']}", "memoryType": memory_type, "content": mem0_data["memory"], "created": mem0_data.get("metadata", {}).get( "created_at", datetime.utcnow().isoformat() + "Z" ), "namespace": namespace, }
# Preserve original data in extensions mif["extensions"] = { "mem0": { "original_id": mem0_data["id"], "original_category": category, "user_id": mem0_data.get("user_id"), "hash": mem0_data.get("hash"), "metadata": { k: v for k, v in mem0_data.get("metadata", {}).items() if k not in ["category", "created_at"] } } }
return mif
# Example usagemem0_memory = { "id": "mem0_123abc", "memory": "User prefers dark mode", "user_id": "user_456", "metadata": { "category": "preference", "created_at": "2026-01-15T10:30:00Z" }}
mif_memory = mem0_to_mif(mem0_memory)print(json.dumps(mif_memory, indent=2))Batch Migration
Section titled “Batch Migration”import jsonfrom pathlib import Path
def migrate_mem0_export(input_file: str, output_dir: str): """Migrate a Mem0 export file to MIF memories."""
output_path = Path(output_dir) output_path.mkdir(parents=True, exist_ok=True)
with open(input_file) as f: mem0_memories = json.load(f)
for mem in mem0_memories: mif = mem0_to_mif(mem)
# Write to file memory_id = mif["@id"].split(":")[-1] output_file = output_path / f"{memory_id}.memory.json"
with open(output_file, "w") as f: json.dump(mif, f, indent=2)
print(f"Migrated {len(mem0_memories)} memories to {output_dir}")Source Format
Section titled “Source Format”Zep uses a temporal knowledge graph structure:
{ "uuid": "zep_789xyz", "content": "User prefers dark mode for all applications", "created_at": "2026-01-15T10:30:00Z", "metadata": { "source": "conversation" }, "temporal": { "t_valid": "2026-01-15T00:00:00Z", "t_invalid": null }, "entity_edges": [ { "source": "user:jane", "target": "concept:dark-mode", "relation": "prefers" } ], "embedding": [0.1, 0.2, ...]}Mapping
Section titled “Mapping”| Zep Field | MIF Field | Notes |
|---|---|---|
uuid | @id | Prefix with urn:mif: |
content | content | Direct mapping |
created_at | created, temporal.recordedAt | Both fields |
temporal.t_valid | temporal.validFrom | Direct mapping |
temporal.t_invalid | temporal.validUntil | Direct mapping |
entity_edges | relationships | Transform to MIF format |
embedding | embedding.vectorUri | Store externally |
Conversion Script
Section titled “Conversion Script”def zep_to_mif(zep_data: dict) -> dict: """Convert Zep memory to MIF format."""
mif = { "@context": "https://mif-spec.dev/schema/context.jsonld", "@type": "Memory", "@id": f"urn:mif:{zep_data['uuid']}", "memoryType": "semantic", # Default to semantic for factual memories "content": zep_data["content"], "created": zep_data["created_at"], "namespace": "_semantic/knowledge", # Categorize via namespace }
# Map temporal data if "temporal" in zep_data: mif["temporal"] = { "@type": "TemporalMetadata", "validFrom": zep_data["temporal"].get("t_valid"), "validUntil": zep_data["temporal"].get("t_invalid"), "recordedAt": zep_data["created_at"] }
# Map entity edges to relationships if "entity_edges" in zep_data: mif["relationships"] = [] for edge in zep_data["entity_edges"]: mif["relationships"].append({ "@type": "Relationship", "relationshipType": map_zep_relation(edge["relation"]), "target": { "@id": f"urn:mif:entity:{edge['target'].replace(':', ':')}" }, "metadata": { "source": edge["source"], "original_relation": edge["relation"] } })
# Reference embedding (store vectors separately) if "embedding" in zep_data: mif["embedding"] = { "@type": "EmbeddingReference", "model": "zep-default", "sourceText": zep_data["content"], "vectorUri": f"urn:mif:vector:{zep_data['uuid']}" }
return mif
def map_zep_relation(relation: str) -> str: """Map Zep relation types to MIF relationship types.""" mapping = { "prefers": "RelatesTo", "uses": "Uses", "knows": "RelatesTo", "created": "Created", "part_of": "PartOf", } return mapping.get(relation, "RelatesTo")Letta (Agent File)
Section titled “Letta (Agent File)”Source Format
Section titled “Source Format”Letta uses memory blocks within agent files:
{ "agent_state": { "memory": { "human": { "label": "human", "value": "Name: Alice Johnson. Age: 32. Occupation: Software Engineer. Prefers dark mode. Uses Python and TypeScript.", "limit": 5000 }, "persona": { "label": "persona", "value": "I am a helpful coding assistant...", "limit": 3000 } } }}Mapping Strategy
Section titled “Mapping Strategy”Letta stores concatenated facts in a single block. Migration requires:
- Parse the block content into discrete facts
- Create individual MIF memories for each fact
- Link related memories together
Conversion Script
Section titled “Conversion Script”import refrom datetime import datetime
def letta_to_mif(letta_data: dict) -> list: """Convert Letta agent memory to MIF memories with base memory types."""
memories = [] memory_blocks = letta_data.get("agent_state", {}).get("memory", {})
for block_name, block in memory_blocks.items(): # Skip persona blocks (those are prompts, not memories) if block_name == "persona": continue
# Parse the block value into facts facts = parse_letta_block(block["value"])
for i, fact in enumerate(facts): memory_type, namespace = classify_fact(fact) mif = { "@context": "https://mif-spec.dev/schema/context.jsonld", "@type": "Memory", "@id": f"urn:mif:letta-{block_name}-{i}", "memoryType": memory_type, "content": fact, "created": datetime.utcnow().isoformat() + "Z", "namespace": namespace, "provenance": { "sourceType": "external_import", "confidence": 0.8, "trustLevel": "moderate_confidence" }, "extensions": { "letta": { "block": block_name, "original_limit": block.get("limit") } } } memories.append(mif)
return memories
def parse_letta_block(value: str) -> list: """Parse a Letta block into individual facts."""
# Split by sentence-ending punctuation sentences = re.split(r'(?<=[.!?])\s+', value)
facts = [] for sentence in sentences: sentence = sentence.strip() if sentence and len(sentence) > 10: # Skip very short fragments facts.append(sentence)
return facts
def classify_fact(fact: str) -> tuple: """Classify a fact into base memory type and namespace.
Returns: Tuple of (memory_type, namespace) """ fact_lower = fact.lower()
# Procedural: how-to, processes, patterns if any(kw in fact_lower for kw in ["step", "process", "how to", "procedure", "workflow"]): return ("procedural", "_procedural/patterns")
# Episodic: events, sessions, incidents elif any(kw in fact_lower for kw in ["happened", "occurred", "yesterday", "today", "session"]): return ("episodic", "_episodic/sessions")
# Semantic: facts, preferences, decisions (default) elif "prefer" in fact_lower or "like" in fact_lower: return ("semantic", "_semantic/preferences") elif "decided" in fact_lower or "chose" in fact_lower: return ("semantic", "_semantic/decisions") else: return ("semantic", "_semantic/knowledge")Subcog
Section titled “Subcog”Source Format
Section titled “Source Format”Subcog format is closest to MIF (MIF was partially inspired by Subcog):
{ "id": "subcog_abc123", "content": "Decision: Use PostgreSQL for data storage", "namespace": "decisions", "domain": "project", "tags": ["database", "architecture"], "created_at": "2026-01-15T10:30:00Z", "metadata": { "confidence": 0.95, "source": "conversation" }}Mapping
Section titled “Mapping”| Subcog Field | MIF Field | Notes |
|---|---|---|
id | @id | Prefix with urn:mif: |
content | content | Direct mapping |
namespace | memoryType + namespace | Map to base type and MIF namespace |
domain | extensions.subcog.domain | Preserved in extensions |
tags | tags | Direct mapping |
created_at | created | Direct mapping |
metadata.confidence | provenance.confidence | Direct mapping |
Conversion Script
Section titled “Conversion Script”from datetime import datetime
def subcog_to_mif(subcog_data: dict) -> dict: """Convert Subcog memory to MIF format with base memory types."""
# Map Subcog namespace to base memoryType and MIF namespace namespace_map = { "decisions": ("semantic", "_semantic/decisions"), "patterns": ("procedural", "_procedural/patterns"), "learnings": ("semantic", "_semantic/knowledge"), "preferences": ("semantic", "_semantic/preferences"), "facts": ("semantic", "_semantic/knowledge"), "context": ("semantic", "_semantic/knowledge"), "incidents": ("episodic", "_episodic/incidents"), "sessions": ("episodic", "_episodic/sessions"), "blockers": ("episodic", "_episodic/blockers"), "runbooks": ("procedural", "_procedural/runbooks"), "migrations": ("procedural", "_procedural/migrations"), }
subcog_ns = subcog_data.get("namespace", "") memory_type, mif_namespace = namespace_map.get(subcog_ns, ("semantic", "_semantic/knowledge"))
mif = { "@context": "https://mif-spec.dev/schema/context.jsonld", "@type": "Memory", "@id": f"urn:mif:{subcog_data['id']}", "memoryType": memory_type, "content": subcog_data["content"], "created": subcog_data.get("created_at", datetime.utcnow().isoformat() + "Z"), "namespace": mif_namespace, "tags": subcog_data.get("tags", []), }
# Map metadata to provenance if "metadata" in subcog_data: mif["provenance"] = { "sourceType": "external_import", "confidence": subcog_data["metadata"].get("confidence", 0.8), }
# Preserve extensions mif["extensions"] = { "subcog": { "original_id": subcog_data["id"], "original_namespace": subcog_ns, "domain": subcog_data.get("domain") } }
return mifBasic Memory
Section titled “Basic Memory”Source Format
Section titled “Source Format”Basic text files or simple JSON:
dark mode: enablededitor: vimlanguage: pythonOr JSON:
{ "preferences": { "dark_mode": true, "editor": "vim", "language": "python" }}Conversion Script
Section titled “Conversion Script”def text_to_mif(text_file: str) -> list: """Convert a plain text file to MIF memories."""
memories = []
with open(text_file) as f: lines = f.readlines()
for i, line in enumerate(lines): line = line.strip() if not line or line.startswith("#"): continue
# Parse key: value format if ":" in line: key, value = line.split(":", 1) content = f"{key.strip()}: {value.strip()}" else: content = line
mif = { "@context": "https://mif-spec.dev/schema/context.jsonld", "@type": "Memory", "@id": f"urn:mif:import-{uuid.uuid4()}", "memoryType": "semantic", # Base memory type "namespace": "_semantic/preferences" if ":" in line else "_semantic/knowledge", "content": content, "created": datetime.utcnow().isoformat() + "Z", "provenance": { "sourceType": "external_import", "confidence": 0.7 } } memories.append(mif)
return memoriesLangMem
Section titled “LangMem”Source Format
Section titled “Source Format”LangMem uses a graph-based structure:
{ "memories": [ { "id": "mem_123", "text": "User prefers dark mode", "embedding": [...], "metadata": { "timestamp": "2026-01-15T10:30:00Z", "type": "semantic" } } ], "relationships": [ { "source": "mem_123", "target": "mem_456", "type": "related" } ]}Conversion Script
Section titled “Conversion Script”from datetime import datetime
def langmem_to_mif(langmem_data: dict) -> list: """Convert LangMem export to MIF memories with base memory types."""
# Build relationship lookup relationships = {} for rel in langmem_data.get("relationships", []): source = rel["source"] if source not in relationships: relationships[source] = [] relationships[source].append({ "target": rel["target"], "type": rel["type"] })
# Convert memories mif_memories = []
for mem in langmem_data.get("memories", []): langmem_type = mem.get("metadata", {}).get("type") memory_type, namespace = map_langmem_type(langmem_type)
mif = { "@context": "https://mif-spec.dev/schema/context.jsonld", "@type": "Memory", "@id": f"urn:mif:{mem['id']}", "memoryType": memory_type, "namespace": namespace, "content": mem["text"], "created": mem.get("metadata", {}).get( "timestamp", datetime.utcnow().isoformat() + "Z" ), }
# Add relationships if mem["id"] in relationships: mif["relationships"] = [ { "@type": "Relationship", "relationshipType": map_langmem_relation(rel["type"]), "target": {"@id": f"urn:mif:{rel['target']}"} } for rel in relationships[mem["id"]] ]
mif_memories.append(mif)
return mif_memories
def map_langmem_type(langmem_type: str) -> tuple: """Map LangMem memory type to MIF base type and namespace.
LangMem already uses base type naming, so we preserve the type and map to appropriate MIF namespaces.
Returns: Tuple of (memory_type, namespace) """ mapping = { "semantic": ("semantic", "_semantic/knowledge"), "episodic": ("episodic", "_episodic/sessions"), "procedural": ("procedural", "_procedural/patterns"), } return mapping.get(langmem_type, ("semantic", "_semantic/knowledge"))
def map_langmem_relation(relation: str) -> str: """Map LangMem relation to MIF relationship type.""" mapping = { "related": "RelatesTo", "derived": "DerivedFrom", "supersedes": "Supersedes", "conflicts": "ConflictsWith", } return mapping.get(relation, "RelatesTo")Validation After Migration
Section titled “Validation After Migration”After migrating, validate your MIF documents:
# Validate all migrated memoriesfor f in memories/*.json; do npx ajv validate -s schema/mif.schema.json -d "$f" || echo "FAILED: $f"doneCommon Issues
Section titled “Common Issues”Missing Required Fields
Section titled “Missing Required Fields”If validation fails for missing fields, ensure:
@contextis set to"https://mif-spec.dev/schema/context.jsonld"@typeis"Memory"@idstarts withurn:mif:createdis a valid ISO 8601 datetime
Invalid Memory Types
Section titled “Invalid Memory Types”Map provider-specific types to MIF types:
semantic->factepisodic->episodeprocedural->pattern- Unknown ->
memory
Relationship Mapping
Section titled “Relationship Mapping”When relationship types don’t map directly:
- Use
RelatesToas fallback - Store original type in
metadata - Document custom mappings
Embedding Preservation
Section titled “Embedding Preservation”Store embeddings externally and reference via vectorUri:
"embedding": { "model": "original-model", "sourceText": "...", "vectorUri": "file://vectors/memory-id.bin"}