Relevant source files
The following files were used as context for generating this wiki page:
- integrations/mem0-strands/python/src/mem0_strands/init.py
- integrations/hermes-plugin-mem0/init.py
- integrations/mem0-strands/python/src/mem0_strands/store.py
- integrations/mem0-agent-plugin/core/memory_core.py
- integrations/antigravity-plugin/core/memory_core.py
- integrations/opencode-plugin/opencode-mem0.ts
- integrations/codex-plugin/core/memory_core.py
- integrations/cursor-plugin/core/memory_core.py
- integrations/agent-plugin-core/python/memory_core.py
- integrations/mem0-strands/python/src/mem0_strands/client.py
- integrations/kimi-plugin/core/memory_core.py
- integrations/claude-code-plugin/core/memory_core.py
- mem0/memory/main.py
- integrations/antigravity-plugin/core/memory_cli.py
- integrations/cursor-plugin/core/memory_cli.py
- mem0-ts/src/community/src/integrations/langchain/mem0.ts
- mem0/embeddings/init.py
- integrations/mem0-strands/python/src/mem0_strands/telemetry.py
- mem0-ts/src/oss/src/prompts/index.ts
- mem0/memory/init.py
- mem0/client/main.py
- integrations/mem0-strands/python/pyproject.toml
- mem0/configs/embeddings/init.py
- mem0/vector_stores/init.py
- mem0/client/init.py
- mem0/configs/rerankers/init.py
Overview
The mem0-strands integration provides persistent long-term memory for Strands agents, bridging the framework's MemoryStore abstraction with Mem0 backend capabilities. By plugging directly into the agent loop via MemoryManager, it handles cross-session recall and automated memory injection and extraction without requiring explicit model-called tool invocations.
Sources: integrations/mem0-strands/python/src/mem0_strands/init.py:1-11, integrations/mem0-strands/python/src/mem0_strands/store.py:1-10
Strands Package Architecture and Package Setup
Package Structure and Build Configuration
The mem0-strands package is structured around the hatchling build backend, defining its core metadata, dependencies, and development environments inside pyproject.toml. The package requires Python >=3.10 and licenses under Apache-2.0.
| Project Metadata Key | Value | Sources |
|---|---|---|
name | mem0-strands | integrations/mem0-strands/python/pyproject.toml:6-6 |
version | 0.1.1 | integrations/mem0-strands/python/pyproject.toml:7-7 |
requires-python | >=3.10 | integrations/mem0-strands/python/pyproject.toml:10-10 |
license | Apache-2.0 | integrations/mem0-strands/python/pyproject.toml:11-11 |
Sources: integrations/mem0-strands/python/pyproject.toml:1-25
The package relies on strands-agents>=1.45.0 for the strands.memory module, which introduces the MemoryStore and MemoryManager abstractions, alongside mem0ai>=2.0.11 for memory management functionality.
# strands-agents>=1.45.0: first release shipping the `strands.memory` module
# (MemoryStore / MemoryManager) that Mem0MemoryStore implements.
dependencies = [
"strands-agents>=1.45.0",
"mem0ai>=2.0.11",
]
Sources: integrations/mem0-strands/python/pyproject.toml:27-32
Note
strands-agents>=1.45.0 is a strict dependency because it is the first release shipping the strands.memory module that Mem0MemoryStore implements.
Sources: integrations/mem0-strands/python/pyproject.toml:27-28
High-Level Module Exports
The package root exposes key integration classes through __all__, allowing direct imports from mem0_strands. Version resolution is handled dynamically using importlib.metadata, falling back to 0.0.0+unknown if the package metadata is missing in source tree executions.
from importlib.metadata import PackageNotFoundError, version
from mem0_strands.client import Mem0ServiceClient
from mem0_strands.store import Mem0MemoryStore
__all__ = [
"Mem0MemoryStore",
"Mem0ServiceClient",
]
try:
__version__ = version("mem0-strands")
except PackageNotFoundError: # pragma: no cover - only when running from a source tree
__version__ = "0.0.0+unknown"Sources: integrations/mem0-strands/python/src/mem0_strands/init.py:13-26
Mem0 Memory Store Implementation
Initialization and Entity Scoping
Mem0MemoryStore extends the Strands MemoryStore abstraction to provide cross-session recall and automated memory synchronization. During instantiation, the store validates that at least one entity scope field is provided among user_id, agent_id, run_id, or app_id.
| Scope Field | Description | Sources |
|---|---|---|
user_id | Mem0 user namespace that owns the memories | integrations/mem0-strands/python/src/mem0_strands/store.py:66-84 |
agent_id | Mem0 agent namespace | integrations/mem0-strands/python/src/mem0_strands/store.py:67-85 |
run_id | Mem0 run/session namespace | integrations/mem0-strands/python/src/mem0_strands/store.py:68-86 |
app_id | Mem0 app namespace (platform only) | integrations/mem0-strands/python/src/mem0_strands/store.py:69-87 |
Sources: integrations/mem0-strands/python/src/mem0_strands/store.py:50-124
Caution
The app_id scope is restricted to the Mem0 platform. Attempting to combine app_id with a self-hosted configuration dictionary via config triggers a ValueError during initialization because self-hosted Mem0 instances do not support app-level scoping on write operations.
Sources: integrations/mem0-strands/python/src/mem0_strands/store.py:115-123
Client Management and Search Execution
The underlying Mem0ServiceClient is constructed lazily on first access. Because initializing the client can block the event loop—such as validating platform API keys over HTTP or building vector stores and embedders for self-hosted instances—client resolution and initial telemetry recording execute inside worker threads via asyncio.to_thread.
@property
def client(self) -> Mem0ServiceClient:
"""The Mem0 service client, constructed lazily on first use.
Note: constructing the underlying SDK client can block (the platform client
validates the API key over HTTP; the OSS client builds embedders / vector
stores), so first use is deferred and always happens inside a worker thread
via :func:`asyncio.to_thread`, never on the event loop.
"""
client_injected = self._client is not None
if self._client is None:
self._client = Mem0ServiceClient(api_key=self._api_key, host=self._host, config=self._config)
if not self._recorded_init:
self._recorded_init = True
telemetry.record(
"store.init",
self._client,
scopes=sorted(self.scope),
writable=self.writable,
extraction_enabled=bool(self.extraction),
has_default_metadata=bool(self.metadata),
max_search_results=self.max_search_results,
host_overridden=bool(self._host),
client_injected=client_injected,
)
return self._clientSources: integrations/mem0-strands/python/src/mem0_strands/store.py:141-166
Semantic search queries retrieve matching entries ordered by relevance. The search method resolves the maximum result limit by checking search options, falling back to the store's default configuration, or defaulting to 5 (DEFAULT_MAX_SEARCH_RESULTS).
DEFAULT_MAX_SEARCH_RESULTS = 5Sources: integrations/mem0-strands/python/src/mem0_strands/store.py:48-48
async def search(self, query: str, options: SearchOptions | None = None) -> list[MemoryEntry]:
"""Search Mem0 for entries matching ``query``, ordered by relevance."""
top_k = options.get("max_search_results") if options is not None else None
if top_k is None:
top_k = self.max_search_results
if top_k is None:
top_k = DEFAULT_MAX_SEARCH_RESULTS
# ``self.client`` is resolved inside the thread so lazy construction (a
# blocking call) does not run on the event loop.
started = time.perf_counter()
try:
memories = await asyncio.to_thread(lambda: self.client.search_memories(query, self.scope, top_k))
except Exception as exc:
self._record("store.search", started, False, top_k=top_k, error_kind=telemetry.error_kind(exc))
raise
self._record("store.search", started, True, top_k=top_k, result_count=len(memories))
return [self._to_entry(memory) for memory in memories]
Sources: integrations/mem0-strands/python/src/mem0_strands/store.py:168-186
Service Client Wrapper and Client Abstractions
Backend Initialization and Resolution Strategy
The Mem0ServiceClient class acts as an adaptation layer bridging Mem0MemoryStore to either the hosted Mem0 platform client (mem0.MemoryClient) or the self-hosted OSS client (mem0.Memory). Instantiation follows a strict priority order based on the parameters passed by callers:
- Pre-built
client: Uses the provided client instance directly. Asynchronous client types are explicitly rejected during initialization, raising aValueErrorbecause the store executes synchronous SDK operations inside worker threads viaasyncio.to_thread. - Configuration
config: ImportsMemoryfrommem0and constructs a self-hosted backend viaMemory.from_config(config), settingis_platformtoFalse. - Default hosted platform: Imports
MemoryClientfrommem0, resolves the API key from the explicitapi_keyargument or theMEM0_API_KEYenvironment variable, and initializes the platform client with an optional customhost.
def __init__(
self,
api_key: str | None = None,
host: str | None = None,
config: dict[str, Any] | None = None,
client: Any | None = None,
) -> None:
"""Initialize the Mem0 client.
Args:
api_key: Mem0 platform API key. Falls back to ``$MEM0_API_KEY``.
host: Mem0 platform base URL. Defaults to the SDK default
(``https://api.mem0.ai``).
config: A Mem0 OSS config dict; when given, a self-hosted
:class:`mem0.Memory` is built instead of the platform client.
client: A pre-built Mem0 client to use directly (platform or OSS).
Raises:
ValueError: If ``client`` is an async Mem0 client (its coroutines
would never be awaited off the worker thread).
"""
if client is not None:
if _is_async_client(client):
raise ValueError(
"Async Mem0 clients are not supported. Pass a synchronous "
"mem0.MemoryClient (or a mem0.Memory / config): the store runs the "
"SDK in a worker thread, so an async client's coroutines would "
"never be awaited and every write would silently no-op."
)
self.mem0 = client
self.is_platform = _is_platform_client(client)
return
if config is not None:
try:
from mem0 import Memory
except ImportError as err: # pragma: no cover - exercised via install docs
raise ImportError(
"The mem0ai package is required. Install it with: pip install 'mem0-strands'"
) from err
self.mem0 = Memory.from_config(config)
self.is_platform = False
return
try:
from mem0 import MemoryClient
except ImportError as err: # pragma: no cover - exercised via install docs
raise ImportError("The mem0ai package is required. Install it with: pip install 'mem0-strands'") from err
api_key = api_key or os.environ.get("MEM0_API_KEY")
# MemoryClient(host=None) would override the SDK default with None, so only
# pass host when the caller actually set one.
self.mem0 = MemoryClient(api_key=api_key, host=host) if host else MemoryClient(api_key=api_key)
self.is_platform = True
Sources: integrations/mem0-strands/python/src/mem0_strands/client.py:60-113
Warning
Passing an asynchronous Mem0 client such as AsyncMemoryClient triggers a ValueError during initialization. Because add and search on async clients are coroutine functions, running them inside asyncio.to_thread would return un-awaited coroutine objects, causing every memory write and search query to silently no-op.
Sources: integrations/mem0-strands/python/src/mem0_strands/client.py:24-28, integrations/mem0-strands/python/src/mem0_strands/client.py:81-89
Scope Validation and Write Adapters
Mem0ServiceClient validates entity scopes prior to executing storage or search requests against the underlying SDK. Specifically, the _check_scope method enforces backend capabilities by rejecting app_id usage when interacting with self-hosted OSS instances, as OSS Memory implementations lack support for app-level namespaces.
def _check_scope(self, scope: dict[str, str]) -> None:
"""Reject scope the selected backend cannot honor.
``app_id`` exists only on the platform; the OSS ``Memory`` API has no
``app_id`` parameter, so we fail loudly here rather than let it surface as
a ``TypeError`` on ``add`` or silently miss on ``search``.
"""
if not self.is_platform and "app_id" in scope:
raise ValueError(
"app_id is a Mem0 platform-only scope. The OSS backend supports "
"user_id, agent_id, and run_id; drop app_id or use the platform client."
)
Sources: integrations/mem0-strands/python/src/mem0_strands/client.py:114-126
Platform write operations automatically attach a telemetry source tag via _write_extras, appending {"source": "STRANDS"} to attribute generated memories correctly within the Mem0 backend allowlist. Self-hosted OSS writes omit this tag to prevent signature validation errors from unknown keyword arguments.
# Tags platform writes so Mem0's backend attributes the memory to this integration
# in telemetry (recognized values live in the backend's KNOWN_EVENT_SOURCES
# allowlist; unknown ones bucket into "OTHERS"). Platform only.
_SOURCE = "STRANDS"
Sources: integrations/mem0-strands/python/src/mem0_strands/client.py:30-34
def _write_extras(self) -> dict[str, str]:
"""Extra kwargs attached to platform writes: the telemetry ``source`` tag."""
return {"source": _SOURCE} if self.is_platform else {}Sources: integrations/mem0-strands/python/src/mem0_strands/client.py:127-129
The client exposes three core methods for data manipulation and retrieval:
| Method | Parameters | Description | Sources |
|---|---|---|---|
store_memory | content, scope, metadata | Stores a single discrete fact verbatim with infer=False and merged write extras | integrations/mem0-strands/python/src/mem0_strands/client.py:131-144 |
store_messages | messages, scope | Submits rendered conversation turns for server-side extraction with infer=True | integrations/mem0-strands/python/src/mem0_strands/client.py:146-154 |
search_memories | query, scope, top_k | Performs semantic search wrapping scope filters and normalizes the payload via _extract_results | integrations/mem0-strands/python/src/mem0_strands/client.py:155-163 |
Sources: integrations/mem0-strands/python/src/mem0_strands/client.py:131-164
Search responses handle structural variations across Mem0 API versions by inspecting result payloads. The helper function _extract_results checks whether the response is a dictionary containing a "results" key or a bare list, returning a safe list of memory dictionaries in both cases.
def _extract_results(response: Any) -> list[dict[str, Any]]:
"""Normalize a Mem0 search response to a list of memory dicts.
Mem0 returns ``{"results": [...]}`` (v1.1) or, on older paths, a bare list.
"""
if isinstance(response, dict):
results = response.get("results", [])
return list(results) if isinstance(results, list) else []
if isinstance(response, list):
return response
return []Sources: integrations/mem0-strands/python/src/mem0_strands/client.py:166-176
Strands Memory Ingestion and Message Serialization
Message Ingestion and Extraction Sinks
Mem0MemoryStore provides dual write mechanisms for capturing conversation history: single-fact ingestion via add and batch message processing via add_messages. The add method writes a single distilled fact verbatim by passing infer=False to the underlying client, serving as the storage sink for explicit tools like add_memory. Conversely, add_messages takes raw conversation turns and routes them directly to Mem0 for server-side extraction with infer=True, eliminating client-side model overhead while leveraging server-side deduplication.
``Mem0MemoryStore`` implements both write sinks, which is what sets it apart from a
vector-DB-style store:
- :meth:`add` writes a single distilled fact verbatim (``infer=False``). This is
the sink for the ``add_memory`` tool and for a client-side extractor.
- :meth:`add_messages` renders raw conversation turns to text and hands them to
Mem0 for **server-side extraction** (``infer=True``). Because this sink exists,
enabling ``extraction`` routes messages straight to Mem0's own extraction
pipeline -- no extra client-side model call, and Mem0's de-duplication applies.
Sources: integrations/mem0-strands/python/src/mem0_strands/store.py:10-19
Note
Extraction writes using add_messages operate with at-least-once semantics, safely tolerating duplicate content submissions because Mem0 handles de-duplication natively on the server side.
Sources: integrations/mem0-strands/python/src/mem0_strands/store.py:187-191
Telemetry Integration and Usage Diagnostics
Anonymous Event Dispatch and Privacy Controls
The telemetry subsystem records anonymous usage diagnostics for the Strands memory store without introducing extra dependencies by leveraging the Mem0 SDK's built-in PostHog client. Events are dispatched via client_telemetry.capture_event under the namespace strands.* and include standard properties such as language, integration source, package version, and backend type.
def record(event: str, client: Mem0ServiceClient, **properties: Any) -> None:
"""Send one strands.* usage event. Never raises."""
try:
client_telemetry.capture_event(
f"strands.{event}",
{
"source": SOURCE,
"language": "python",
"strands_store_version": _VERSION,
"backend": "platform" if client.is_platform else "oss",
**properties,
},
_distinct_id(client),
)Sources: integrations/mem0-strands/python/src/mem0_strands/telemetry.py:55-68
Warning
Telemetry collection strictly omits sensitive data. Captured properties never include queries, memory text, message content, entity identifiers, metadata dictionaries, or API keys.
Sources: integrations/mem0-strands/python/src/mem0_strands/telemetry.py:9-13
Users can completely opt out of anonymous usage tracking by setting the environment variable MEM0_TELEMETRY=false. Additionally, all telemetry dispatch calls are wrapped in exception-suppressing blocks so that telemetry failures never interrupt agent execution.
Opt out with MEM0_TELEMETRY=false.Sources: integrations/mem0-strands/python/src/mem0_strands/telemetry.py:12-12
except Exception:
passSources: integrations/mem0-strands/python/src/mem0_strands/telemetry.py:69-70
Distinct Identity and Error Classification
Identity attribution adapts dynamically depending on whether the store connects to the hosted platform or a self-hosted backend. The helper function _distinct_id inspects the underlying client instance for a user_email attribute, falling back to None so the SDK automatically uses its machine-local anonymous identifier for OSS deployments.
Sources: integrations/mem0-strands/python/src/mem0_strands/telemetry.py:49-53
Failures encountered during operations are categorized into content-free error kinds before being recorded in telemetry properties. The classifier inspects exception type names and string representations to map errors into standard diagnostic labels:
| Condition / Match String | Error Kind Label | Sources |
|---|---|---|
"timeout" or "timed out" | "timeout" | integrations/mem0-strands/python/src/mem0_strands/telemetry.py:36-37 |
"401", "403", "unauthor", or "forbidden" | "auth" | integrations/mem0-strands/python/src/mem0_strands/telemetry.py:38-39 |
"429" or "rate limit" | "rate-limited" | integrations/mem0-strands/python/src/mem0_strands/telemetry.py:40-41 |
"500", "502", "503", or "504" | "server-error" | integrations/mem0-strands/python/src/mem0_strands/telemetry.py:42-43 |
"400" or "422" | "bad-request" | integrations/mem0-strands/python/src/mem0_strands/telemetry.py:44-45 |
| Unmatched exceptions | type(exc).__name__ | integrations/mem0-strands/python/src/mem0_strands/telemetry.py:46-46 |
Sources: integrations/mem0-strands/python/src/mem0_strands/telemetry.py:33-46
Related
On This Page
OverviewStrands Package Architecture and Package SetupPackage Structure and Build ConfigurationHigh-Level Module ExportsMem0 Memory Store ImplementationInitialization and Entity ScopingClient Management and Search ExecutionService Client Wrapper and Client AbstractionsBackend Initialization and Resolution StrategyScope Validation and Write AdaptersStrands Memory Ingestion and Message SerializationMessage Ingestion and Extraction SinksTelemetry Integration and Usage DiagnosticsAnonymous Event Dispatch and Privacy ControlsDistinct Identity and Error ClassificationRelated