Skip to main content
doc0blade47/mem0documentation home
Sign in

Memory Lifecycle

Last updated Oct 11, 2026100% grounded
Relevant source files

The following files were used as context for generating this wiki page:

Overview

The memory lifecycle manages the end-to-end journey of stored information, orchestrating how conversations are parsed into structured facts, linked across entities, updated in place, or systematically cleared. Operating across synchronous and asynchronous interfaces mem0/memory/main.py, the system standardizes public methods mem0/memory/main.py alongside language model fact extraction mem0-ts/src/oss/src/prompts/index.ts to transform raw interactions into queryable vectors. It governs metadata tracking, temporal expiration filters, and telemetry-driven operational notices mem0/memory/notices.py to maintain high retrieval precision and scale thresholds mem0-ts/src/oss/src/memory/index.ts.

Sources: mem0/memory/main.py:1960-2005, mem0-ts/src/oss/src/memory/index.ts:1-82, mem0-ts/src/oss/src/prompts/index.ts:1-43, mem0/memory/notices.py:99-163

Public Memory Engine Interface

Engine Method Signatures

The memory engine exposes core operations for both synchronous and asynchronous execution across management routines mem0/memory/main.py. The public interface handles initialization through MemoryConfig mem0/configs/base.py and coordinates internal lifecycle helpers like _create_memory, _update_memory, _delete_memory, and reset mem0/memory/main.py.

MethodParametersReturn TypePurpose
historymemory_idlistGet the history of changes for a memory by ID mem0/memory/main.py
_create_memorydata, existing_embeddings, metadata=NonestrInternal helper to embed, store, and record audit history for new text mem0/memory/main.py
_create_procedural_memorymessages, metadata=None, prompt=NonedictGenerate procedural memory summaries using an LLM prompt workflow mem0/memory/main.py
_update_memorymemory_id, data, existing_embeddings, metadata=NonestrModify existing memory text and refresh entity-store links mem0/memory/main.py
_delete_memorymemory_id, existing_memory=NonestrRemove vector entries, log deletion audit history, and clear entity relations mem0/memory/main.py
resetNoneNoneReset history database, close connections, and reinitialize vector store collections mem0/memory/main.py

Sources: mem0/memory/main.py:1960-2162

Configuration and Item Schemas

The engine initializes using MemoryConfig defaults and validates core payloads against MemoryItem mem0/configs/base.py. The configuration binds vector store settings, language models, embedding models, and audit database paths mem0/configs/base.py.

class MemoryItem(BaseModel):
    id: str = Field(..., description="The unique identifier for the text data")
    memory: str = Field(
        ..., description="The memory deduced from the text data"
    )  # TODO After prompt changes from platform, update this
    hash: Optional[str] = Field(None, description="The hash of the memory")
    # The metadata value can be anything and not just string. Fix it
    metadata: Optional[Dict[str, Any]] = Field(None, description="Additional metadata for the text data")
    score: Optional[float] = Field(None, description="The score associated with the text data")
    created_at: Optional[str] = Field(None, description="The timestamp when the memory was created")
    updated_at: Optional[str] = Field(None, description="The timestamp when the memory was updated")
 
 
class MemoryConfig(BaseModel):
    vector_store: VectorStoreConfig = Field(
        description="Configuration for the vector store",
        default_factory=VectorStoreConfig,
    )
    llm: LlmConfig = Field(
        description="Configuration for the language model",
        default_factory=LlmConfig,
    )
    embedder: EmbedderConfig = Field(
        description="Configuration for the embedding model",
        default_factory=EmbedderConfig,
    )
    history_db_path: str = Field(
        description="Path to the history database",
        default=os.path.join(mem0_dir, "history.db"),
    )
    reranker: Optional[RerankerConfig] = Field(
        description="Configuration for the reranker",
        default=None,
    )
    version: str = Field(
        description="The version of the API",
        default="v1.1",
    )
    custom_instructions: Optional[str] = Field(
        description="Custom instructions for fact extraction",
        default=None,
    )
 

Sources: mem0/configs/base.py:16-58

Note

During a database or vector store reset, reset() mem0/memory/main.py closes active SQLite connections, creates a fresh SQLiteManager instance at history_db_path mem0/configs/base.py, and attempts collection recreation via VectorStoreFactory.

Sources: mem0/memory/main.py:2144-2162, mem0/configs/base.py:42-45

LLM Fact Extraction Workflow

Fact Distillation Prompts and Schemas

The fact extraction workflow transforms raw conversational turns into discrete JSON objects containing structured fact lists mem0/configs/prompts.py. The engine uses targeted templates such as USER_MEMORY_EXTRACTION_PROMPT and AGENT_MEMORY_EXTRACTION_PROMPT to isolate user preferences, personal details, plans, and professional attributes mem0/configs/prompts.py.

Warning

Fact generation must isolate target participants strictly based on designated roles; USER_MEMORY_EXTRACTION_PROMPT explicitly penalizes the model if information from assistant or system messages is included mem0/configs/prompts.py.

Sources: mem0/configs/prompts.py:63-121

Procedural Memory Generation

Procedural memory creation aggregates entire conversation histories into a structured operational summary using _create_procedural_memory mem0/memory/main.py. The system wraps incoming messages with a system prompt and a terminal user turn, queries the language model, strips code block markdown, and stores the resulting summary with a procedural type tag mem0/memory/main.py.

def _create_procedural_memory(self, messages, metadata=None, prompt=None):
    """
    Create a procedural memory
 
    Args:
        messages (list): List of messages to create a procedural memory from.
        metadata (dict): Metadata to create a procedural memory from.
        prompt (str, optional): Prompt to use for the procedural memory creation. Defaults to None.
    """
    logger.info("Creating procedural memory")
 
    parsed_messages = [
        {"role": "system", "content": prompt or PROCEDURAL_MEMORY_SYSTEM_PROMPT},
        *messages,
        {
            "role": "user",
            "content": "Create procedural memory of the above conversation.",
        },
    ]
 
    try:
        procedural_memory = self.llm.generate_response(messages=parsed_messages)
        procedural_memory = remove_code_blocks(procedural_memory)
    except Exception as e:
        logger.error(f"Error generating procedural memory summary: {e}")
        raise
 
    if not procedural_memory:
        raise ValueError(
            "The LLM returned no content for the procedural memory summary. "
            "The model may have declined the request or returned an empty response."
        )
 
    if metadata is None:
        raise ValueError("Metadata cannot be done for procedural memory.")
 
    metadata = {**metadata, "memory_type": MemoryType.PROCEDURAL.value}
    embeddings = self.embedding_model.embed(procedural_memory, memory_action="add")
    memory_id = self._create_memory(procedural_memory, {procedural_memory: embeddings}, metadata=metadata)
    capture_event("mem0._create_procedural_memory", self, {"memory_id": memory_id, "sync_type": "sync"})
 
    result = {"results": [{"id": memory_id, "memory": procedural_memory, "event": "ADD"}]}
 
    return result

Sources: mem0/memory/main.py:2007-2050

Caution

_create_procedural_memory raises a ValueError if the provided metadata dictionary is None or if the language model returns an empty string response mem0/memory/main.py.

Sources: mem0/memory/main.py:2034-2042

Vector Ingestion and Entity Linking

Metadata Preparation and Embedding Upsert

When new items are created via _create_memory, the engine checks whether precomputed embeddings exist in existing_embeddings mem0/memory/main.py. If missing, text strings are transformed into dense vector representations by invoking self.embedding_model.embed(data, memory_action="add") mem0/memory/main.py. A unique uuid.uuid4() identifier is assigned, and metadata dictionaries are cloned and populated with essential audit fields mem0/memory/main.py.

def _create_memory(self, data, existing_embeddings, metadata=None):
    logger.debug(f"Creating memory with {data=}")
    if data in existing_embeddings:
        embeddings = existing_embeddings[data]
    else:
        embeddings = self.embedding_model.embed(data, memory_action="add")
    memory_id = str(uuid.uuid4())
    new_metadata = deepcopy(metadata) if metadata is not None else {}
    new_metadata["data"] = data
    new_metadata["hash"] = hashlib.md5(data.encode()).hexdigest()
    if "created_at" not in new_metadata:
        new_metadata["created_at"] = datetime.now(timezone.utc).isoformat()
    new_metadata["updated_at"] = new_metadata["created_at"]
    new_metadata["text_lemmatized"] = lemmatize_for_bm25(data)
 
    self.vector_store.insert(
        vectors=[embeddings],
        ids=[memory_id],
        payloads=[new_metadata],
    )

Sources: mem0/memory/main.py:1975-1994

Note

Metadata preparation automatically calculates an MD5 hash of the raw text and generates lemmatized tokens using lemmatize_for_bm25(data) to support keyword matching alongside dense vector search mem0/memory/main.py.

Sources: mem0/memory/main.py:1983-1988

Audit Recording and Entity Synchronization

Once the vector payload is inserted into the vector store, the transaction is recorded in the relational audit trail via self.db.add_history mem0/memory/main.py. During memory updates or deletions, _update_memory and _delete_memory invoke entity-store cleanup handlers (_remove_memory_from_entity_store and _link_entities_for_memory) to maintain cross-reference integrity across session filters (user_id, agent_id, run_id) mem0/memory/main.py.

self.db.add_history(
    memory_id,
    None,
    data,
    "ADD",
    created_at=new_metadata.get("created_at"),
    updated_at=new_metadata.get("updated_at"),
    actor_id=new_metadata.get("actor_id"),
    role=new_metadata.get("role"),
)
return memory_id

Sources: mem0/memory/main.py:1995-2005

Warning

Entity-store synchronization during memory modifications is text-content sensitive: _update_memory skips entity removal and relinking if the underlying text value has not changed (text_changed is false) mem0/memory/main.py.

Sources: mem0/memory/main.py:2107-2111

Memory Updates and Deletion Lifecycle

Memory Modification and Entity Resynchronization

The memory engine updates existing entries through _update_memory, which retrieves the target vector payload, validates its existence, and recomputes embeddings and hashes when text content changes mem0/memory/main.py. If the incoming data parameter is None, the engine retains the previous text value (prev_value) mem0/memory/main.py.

def _update_memory(self, memory_id, data, existing_embeddings, metadata=None):
    logger.info(f"Updating memory with {data=}")
 
    try:
        existing_memory = self.vector_store.get(vector_id=memory_id)
    except Exception:
        # Backing-store failure, not a bad memory_id: re-raise the original so the REST layer maps it to 5xx, not 4xx.
        logger.error(f"Error getting memory with ID {memory_id} during update.")
        raise
 
    if existing_memory is None:
        raise ValueError(f"Memory with id {memory_id} not found. Please provide a valid 'memory_id'")
 
    prev_value = existing_memory.payload.get("data")
    if data is None:
        data = prev_value
    if not isinstance(data, str):
        raise ValueError(f"Memory with id {memory_id} does not have text content to update")
    text_changed = data != prev_value
 
    new_metadata = deepcopy(existing_memory.payload)
    if metadata is not None:
        new_metadata.update(_strip_identity_keys(metadata, existing_memory.payload))
 
    new_metadata["data"] = data
    new_metadata["hash"] = hashlib.md5(data.encode()).hexdigest()
    new_metadata["text_lemmatized"] = lemmatize_for_bm25(data)
    new_metadata["created_at"] = existing_memory.payload.get("created_at")
    new_metadata["updated_at"] = datetime.now(timezone.utc).isoformat()
 
    if data in existing_embeddings:
        embeddings = existing_embeddings[data]
    else:
        embeddings = self.embedding_model.embed(data, "update")
 
    self.vector_store.update(
        vector_id=memory_id,
        vector=embeddings,
        payload=new_metadata,
    )
    logger.info(f"Updating memory with ID {memory_id=} with {data=}")
 
    self.db.add_history(
        memory_id,
        prev_value,
        data,
        "UPDATE",
        created_at=new_metadata["created_at"],
        updated_at=new_metadata["updated_at"],
        actor_id=new_metadata.get("actor_id"),
        role=new_metadata.get("role"),
    )
 
    # Entity-store cleanup: strip this memory's id from old-text entities,
    # then re-extract entities from the new text and link them back.
    session_filters = {k: new_metadata[k] for k in ("user_id", "agent_id", "run_id") if new_metadata.get(k)}
    if text_changed:
        self._remove_memory_from_entity_store(memory_id, session_filters)
        self._link_entities_for_memory(memory_id, data, session_filters)
 
    return memory_id

Sources: mem0/memory/main.py:2052-2112

Caution

If vector_store.get() fails due to a backing-store error rather than a missing identifier, the error is re-raised directly so that REST wrapper layers map it to a 5xx server response instead of a 4xx client error mem0/memory/main.py.

Sources: mem0/memory/main.py:2055-2060

Single Deletion and Scope Resets

Individual memory removal is executed via _delete_memory, which extracts existing payload metadata, normalizes creation timestamps to UTC, updates the vector store, records a DELETE event in history with is_deleted=1, and purges the memory identifier from the entity store mem0/memory/main.py. Bulk resets are handled by reset(), which re-initializes the SQLite history database and recreates or resets the vector collection mem0/memory/main.py.

def _delete_memory(self, memory_id, existing_memory=None):
    logger.info(f"Deleting memory with {memory_id=}")
    if existing_memory is None:
        existing_memory = self.vector_store.get(vector_id=memory_id)
        if existing_memory is None:
            raise ValueError(f"Memory with id {memory_id} not found. Please provide a valid 'memory_id'")
    prev_value = existing_memory.payload.get("data", "")
    created_at = _normalize_iso_timestamp_to_utc(existing_memory.payload.get("created_at"))
    updated_at = datetime.now(timezone.utc).isoformat()
    payload = existing_memory.payload or {}
    session_filters = {k: payload[k] for k in ("user_id", "agent_id", "run_id") if payload.get(k)}
    self.vector_store.delete(vector_id=memory_id)
    self.db.add_history(
        memory_id,
        prev_value,
        None,
        "DELETE",
        created_at=created_at,
        updated_at=updated_at,
        actor_id=existing_memory.payload.get("actor_id"),
        role=existing_memory.payload.get("role"),
        is_deleted=1,
    )
 
    # Entity-store cleanup: strip this memory's id from any entity records
    # that linked to it. Non-fatal — the helper swallows errors.
    self._remove_memory_from_entity_store(memory_id, session_filters)
 
    return memory_id
 
def reset(self):
    """
    Reset the memory store by:
        Deletes the vector store collection
        Resets the database
        Recreates the vector store with a new client
    """
    logger.warning("Resetting all memories")
 
    self.db.reset()
    self.db.close()
    self.db = SQLiteManager(self.config.history_db_path)
 
    if hasattr(self.vector_store, "reset"):
        self.vector_store = VectorStoreFactory.reset(self.vector_store)
    else:
        logger.warning("Vector store does not support reset. Skipping.")
        self.vector_store.delete_col()
        self.vector_store = VectorStoreFactory.create(

Sources: mem0/memory/main.py:2114-2162

Warning

When executing reset(), if the vector store implementation lacks a native reset() method, the engine falls back to calling delete_col() followed by a factory recreation step mem0/memory/main.py.

Sources: mem0/memory/main.py:2157-2162

Temporal Filtering and Usage Notices

Telemetry Feature Flags and Usage Notice Constants

The open-source notices framework (mem0/memory/notices.py) manages telemetry-driven feature disclosures, temporal usage tracking, decay detection, and scale evaluation thresholds mem0/memory/notices.py. Notice evaluation uses remote configurations fetched from GitHub or bundled fallbacks governed by a time-to-live (_CONFIG_TTL_SECONDS) of 3600 seconds mem0/memory/notices.py.

Notice ID / ConstantValue / ThresholdMeaning / Purpose
NOTICE_ID"first_run"Identifier for first-run telemetry notices mem0/memory/notices.py
TEMPORAL_USAGE_NOTICE_ID"temporal_usage"Notice identifier for temporal usage tracking mem0/memory/notices.py
DECAY_USAGE_NOTICE_ID"decay_usage"Notice identifier for decay and deletion usage mem0/memory/notices.py
SCALE_THRESHOLD_NOTICE_ID"scale_threshold"Notice identifier for vector scale evaluations mem0/memory/notices.py
PERFORMANCE_SLOW_QUERY_NOTICE_ID"performance_slow_query"Notice identifier for slow query tracking mem0/memory/notices.py
TEMPORAL_USAGE_CAP10Maximum temporal usage notices per window mem0/memory/notices.py
DECAY_USAGE_CAP10Maximum decay usage notices per window mem0/memory/notices.py
DECAY_USAGE_DELETE_THRESHOLD5Successful deletes required to trigger decay notice mem0/memory/notices.py
SCALE_MEMORY_COUNT_THRESHOLD2000Vector count threshold triggering scale notices mem0/memory/notices.py
SCALE_TOP_K_THRESHOLD50top_k query parameter threshold for scale notices mem0/memory/notices.py
PERFORMANCE_SLOW_QUERY_THRESHOLD_SECONDS2.0Query execution time limit for slow query warnings mem0/memory/notices.py
NOTICE_ID = "first_run"
TEMPORAL_FEATURE_NOTICE_ID = "temporal_stub"
TEMPORAL_USAGE_NOTICE_ID = "temporal_usage"
DECAY_FEATURE_NOTICE_ID = "decay_stub"
DECAY_USAGE_NOTICE_ID = "decay_usage"
SCALE_THRESHOLD_NOTICE_ID = "scale_threshold"
PERFORMANCE_SLOW_QUERY_NOTICE_ID = "performance_slow_query"
NOTICE_EVENT = "mem0.notice_displayed"
DISPLAYED_VARIANT = "displayed"
HOLDOUT_VARIANT = "holdout"
STATE_SECTION = "notice_state"
STATE_KEY = "first_run"
TEMPORAL_USAGE_STATE_KEY = "temporal_usage"
TEMPORAL_USAGE_CAP = 10
TEMPORAL_USAGE_WINDOW = timedelta(days=7)
DECAY_USAGE_STATE_KEY = "decay_usage"
DECAY_USAGE_CAP = 10
DECAY_USAGE_WINDOW = timedelta(days=7)
DECAY_USAGE_DELETE_THRESHOLD = 5
SCALE_THRESHOLD_STATE_KEY = "scale_threshold"
SCALE_THRESHOLD_CAP = 10
SCALE_THRESHOLD_WINDOW = timedelta(days=7)
SCALE_MEMORY_COUNT_THRESHOLD = 2000
SCALE_MEMORY_COUNT_CHECK_INTERVAL = 100
SCALE_TOP_K_THRESHOLD = 50
PERFORMANCE_SLOW_QUERY_STATE_KEY = "performance_slow_query"
PERFORMANCE_SLOW_QUERY_CAP = 10
PERFORMANCE_SLOW_QUERY_WINDOW = timedelta(days=7)
PERFORMANCE_SLOW_QUERY_THRESHOLD_SECONDS = 2.0

Sources: mem0/memory/notices.py:99-127

Warning

The OSS Python Memory SDK explicitly does not support advanced query parameters such as timestamp, reference_date, or decay, raising structured error messages when callers attempt to invoke them mem0/memory/notices.py.

Sources: mem0/memory/notices.py:130-134

Temporal Extraction and Expiry Regex Patterns

Temporal filtering engines employ strict regular expression boundaries to detect ISO dates and relative conversational time anchors such as yesterday, last week, or specific date ranges mem0/memory/notices.py.

_ISO_DATE_RE = re.compile(
    r"\b\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)?\b"
)
_RELATIVE_TIME_RE = re.compile(
    r"\b("
    r"today|yesterday|tomorrow|"
    r"last\s+(?:night|week|month|year)|"
    r"this\s+(?:week|month|year)|"
    r"next\s+(?:week|month|year)|"
    r"(?:past|last)\s+\d+\s+(?:day|days|week|weeks|month|months|year|years)|"
    r"(?:since|before|after|until)\s+(?:today|yesterday|tomorrow|\d{4}-\d{2}-\d{2}|last\s+(?:week|month|year))"
    r")\b",
    re.IGNORECASE,
)
_RANGE_OPERATORS = {"gt", "gte", "lt", "lte"}

Sources: mem0/memory/notices.py:136-150

Note

Usage notice tracking enforces concurrency protection via _state_lock and process-level flags to ensure telemetry events do not exceed capacity caps within their defined rolling time windows mem0/memory/notices.py.

Sources: mem0/memory/notices.py:152-163

Was this page helpful?