# blade47/mem0 Wiki ## Technical docs: mem0 API URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/api-overview # mem0 API **Version:** inferred **27** endpoints detected from `(inferred from code)` ## Endpoints ### General | Method | Path | Description | |--------|------|-------------| | `GET` | `/configure` | Get current Mem0 configuration | | `GET` | `/configure/providers` | List bundled LLM and embedder providers | | `POST` | `/configure` | Configure Mem0 | | `POST` | `/generate-instructions` | Generate custom instructions from a use case | | `POST` | `/memories` | Create memories | | `GET` | `/memories` | Get memories | | `GET` | `/memories/{memory_id}` | Get a memory | | `POST` | `/search` | Search memories | | `PUT` | `/memories/{memory_id}` | Update a memory | | `GET` | `/memories/{memory_id}/history` | Get memory history | | `DELETE` | `/memories/{memory_id}` | Delete a memory | | `DELETE` | `/memories` | Delete all memories | | `POST` | `/reset` | Reset all memories | ### api-keys | Method | Path | Description | |--------|------|-------------| | `GET` | `/api-keys` | list_keys | | `POST` | `/api-keys` | create_key | | `DELETE` | `/api-keys/{key_id}` | revoke_key | ### auth | Method | Path | Description | |--------|------|-------------| | `GET` | `/auth/setup-status` | Setup Status | | `POST` | `/auth/register` | Register | | `POST` | `/auth/login` | Login | | `POST` | `/auth/refresh` | Refresh Token | | `GET` | `/auth/me` | Get Current User | | `PATCH` | `/auth/me` | Update Current User | | `POST` | `/auth/change-password` | Change Password | | `POST` | `/auth/onboarding-complete` | Complete Onboarding | ### entities | Method | Path | Description | |--------|------|-------------| | `GET` | `/entities` | List Entities | | `DELETE` | `/entities/{entity_type}/{entity_id}` | Delete Entity | ### requests | Method | Path | Description | |--------|------|-------------| | `GET` | `/requests` | List Requests | --- ## Technical docs: GET Get current Mem0 configuration URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/get-config ## Responses ## Try It --- ## Technical docs: Overview URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/orientation/overview
Relevant source files The following files were used as context for generating this wiki page: - [cli/python/src/mem0_cli/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/__init__.py) - [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts) - [cli/python/src/mem0_cli/app.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py) - [integrations/agent-plugin-core/python/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py) - [integrations/mem0-agent-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py) - [integrations/antigravity-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_cli.py) - [integrations/kimi-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_cli.py) - [integrations/cursor-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_cli.py) - [integrations/codex-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_cli.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/claude-code-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_cli.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [cli/node/src/commands/init.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/init.ts) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/__init__.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [mem0/client/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/__init__.py) - [integrations/mem0-agent-plugin/mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/mcp.json) - [mem0/memory/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/__init__.py) - [cli/cli-spec.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [integrations/mem0-strands/python/src/mem0_strands/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/__init__.py) - [cli/python/src/mem0_cli/__main__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/__main__.py) - [cli/python/src/mem0_cli/commands/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/__init__.py) - [integrations/mem0-agent-plugin/core/_harness_id.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/_harness_id.py) - [integrations/pi-agent-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/index.ts)
## Overview Mem0 provides an extensible memory infrastructure and agent runtime integration suite designed to equip AI agents and applications with persistent context across dual deployment tiers: open-source (OSS) local backends and the hosted Mem0 Platform. At its core, the architecture coordinates cross-runtime command-line interfaces in both Python and Node.js [Python CLI Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/python-cli-architecture), [Node.js CLI Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/node-js-cli-architecture), alongside a unified family of agent harness plugins such as [Claude Code Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin) that synchronize conversation history, background session flows, and diagnostic telemetry. ## Platform Architecture and Core Concepts ### Core Components and Client Entrypoints Mem0 defines its foundational memory layers and client entrypoints across dedicated package modules. The core package exposes synchronous and asynchronous interfaces for both local engine management via `Memory` and `AsyncMemory`, alongside hosted cloud interactions through `MemoryClient` and `AsyncMemoryClient`. | Component / Class | Module Path | Purpose | | :--- | :--- | :--- | | `Memory` | `mem0.memory.main` | Synchronous core memory engine for local management | | `AsyncMemory` | `mem0.memory.main` | Asynchronous core memory engine | | `MemoryClient` | `mem0.client.main` | Synchronous client interface for the Mem0 platform | | `AsyncMemoryClient` | `mem0.client.main` | Asynchronous client interface for the Mem0 platform | Sources: [mem0/__init__.py:5-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/__init__.py#L5-L6) ### Deployment Tiers and Versioning The architecture operates across dual deployment tiers: open-source self-hosted backends and remote platform synchronization. Package versioning is dynamically retrieved via metadata for the underlying distribution (`mem0ai`), while command-line operations are governed by the dedicated CLI distribution version. ```python """mem0 CLI — the command-line interface for the mem0 memory layer.""" __version__ = "0.2.13" ``` Sources: [cli/python/src/mem0_cli/__init__.py:1-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/__init__.py#L1-L3), [mem0/__init__.py:1-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/__init__.py#L1-L3) > [!NOTE] > The Python core package inspects installed distribution metadata via `importlib.metadata.version("mem0ai")` to populate `__version__`, distinguishing the core library version from the `mem0-cli` package distribution version (`0.2.13`). > Sources: [cli/python/src/mem0_cli/__init__.py:3-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/__init__.py#L3-L3), [mem0/__init__.py:3-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/__init__.py#L3-L3) ## Unified CLI Specification and Dual-Runtime Interfaces ### CLI Schema and Branding Specifications Mem0 unifies its command-line interfaces through a cross-runtime JSON specification file (`cli/cli-spec.json`) that establishes metadata, brand assets, configuration directories, and remote API endpoints for both Python and Node client entrypoints. The specification defines global branding identifiers, color palettes, and operational icons used to render terminal output consistently across runtime boundaries. | Branding Property | Key / Value | Purpose | | :--- | :--- | :--- | | `name` | `mem0` | CLI application command name | | `version` | `0.1.0` | CLI specification version | | `logoMini` | `◆ mem0` | Compact CLI banner logo | | `brand` | `#8b5cf6` | Primary brand color hex code | | `accent` | `#a78bfa` | Accent color hex code | | `success` | `#22c55e` | Success indicator color | | `error` | `#ef4444` | Error indicator color | | `warning` | `#f59e0b` | Warning indicator color | | `dim` | `#6b7280` | Dimmed text color | Sources: [cli/cli-spec.json:1-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L1-L34) > [!NOTE] > The cross-runtime specification centralizes icon characters such as success (`✓`), error (`✗`), warning (`⚠`), info (`◆`), pending (`⧗`), add (`+`), update (`~`), delete (`-`), noop (`·`), connected (`●`), and disconnected (`●`) to ensure identical terminal rendering across Python and Node.js wrapper implementations. > Sources: [cli/cli-spec.json:20-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L20-L32) ### Configuration Schema and Environment Bindings Client configuration is structured around local directory storage combined with environment variable overrides. The configuration subsystem maps platform settings and default identifiers to specific environment variables, allowing seamless authentication and scoping for autonomous agents and developer workstations. | Config Section | Field Name | Type | Default Value | Environment Variable | Redact | | :--- | :--- | :--- | :--- | :--- | :--- | | `platform` | `api_key` | `string` | `""` | `MEM0_API_KEY` | `true` | | `platform` | `base_url` | `string` | `https://api.mem0.ai` | `MEM0_BASE_URL` | — | | `defaults` | `user_id` | `string` | `""` | `MEM0_USER_ID` | — | | `defaults` | `agent_id` | `string` | `""` | `MEM0_AGENT_ID` | — | | `defaults` | `app_id` | `string` | `""` | `MEM0_APP_ID` | — | | `defaults` | `run_id` | `string` | `""` | `MEM0_RUN_ID` | — | | `defaults` | `enable_graph` | `boolean` | `false` | `MEM0_ENABLE_GRAPH` | — | Sources: [cli/cli-spec.json:35-86](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L35-L86) > [!WARNING] > The `api_key` field enforces credential redaction (`redact: true`) within logs and configuration dumps, preventing sensitive tokens from leaking into terminal outputs or diagnostic telemetry. > Sources: [cli/cli-spec.json:43-48](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L43-L48) ### REST Endpoint Mappings The CLI specification maps remote control actions to standardized REST API endpoints, defining HTTP methods, path patterns, authentication headers, and entity type mappings for remote synchronization [Python Client SDK](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/python-client-sdk). | Operation Name | HTTP Method | API Path Pattern | | :--- | :--- | :--- | | `add` | `POST` | `/v1/memories/` | | `search` | `POST` | `/v2/memories/search/` | | `get` | `GET` | `/v1/memories/{memory_id}/` | | `list` | `POST` | `/v2/memories/` | | `update` | `PUT` | `/v1/memories/{memory_id}/` | | `delete` | `DELETE` | `/v1/memories/{memory_id}/` | | `deleteAll` | `DELETE` | `/v1/memories/` | | `entities` | `GET` | `/v1/entities/` | | `deleteEntities` | `DELETE` | `/v1/entities/` | Sources: [cli/cli-spec.json:87-98](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L87-L98) > [!TIP] > All REST requests enforce an authentication scheme using the `Authorization` header prefixed with the `Token` scheme, operating with a default network timeout of 30 seconds. > Sources: [cli/cli-spec.json:99-101](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L99-L101) ## Agent Harness Architecture and Plugins Family ### Shared Core and Harness Configuration The Mem0 agent plugin family relies on a shared core implementation (`memory_core.py`) that handles session event recording, background flushing, redacting sensitive secrets, and local storage operations across diverse IDE harnesses and coding assistants. Plugins configure their runtime environment using `configure_harness()`, which sets up harness-specific naming prefixes, data directories, and source tags for telemetry tracking. | Harness Parameter | Default Value | Purpose | | :--- | :--- | :--- | | `name` | `generic` | Identifier for the active coding assistant harness | | `env_prefix` | `MEM0_PLUGIN` | Environment variable prefix for configuration overrides | | `data_dir_name` | `mem0-plugin` | Directory name for local evidence and state storage | | `source_tag` | `mem0_plugin` | Telemetry source tag identifying the originating plugin | Sources: [integrations/mem0-agent-plugin/core/memory_core.py:34-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L34-L38), [integrations/antigravity-plugin/core/memory_core.py:34-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L34-L38), [integrations/kimi-plugin/core/memory_core.py:34-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L34-L38), [integrations/cursor-plugin/core/memory_core.py:34-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L34-L38), [integrations/codex-plugin/core/memory_core.py:34-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L34-L38) > [!NOTE] > The shared core defines strict operational boundaries including command, result, and episode character limits, extraction input token caps, and a maximum of 5 flush attempts for offline retry queues. > Sources: [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L62-L72), [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L62-L72), [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L62-L72), [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L62-L72), [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L62-L72) ### Diagnostic CLI and Control Operations Each IDE plugin embeds a command-line interface (`memory_cli.py`) that exposes diagnostic checks, user controls, and lifecycle actions for managing local and remote memories. The CLI uses Python's `argparse` module to dispatch subcommands such as status inspection, health diagnostics, pausing, resuming, and repository purging. | CLI Subcommand | Arguments / Flags | Action Performed | | :--- | :--- | :--- | | `status` | `--json` | Prints operational summary, repo ID, local data path, and API key status | | `doctor` | `--json` | Runs diagnostic checks against the current workspace and environment | | `pause` | — | Stops saving and searching memories locally | | `resume` | — | Resumes saving and searching memories locally | | `forget` | `--remote`, `--yes`, `--include-project-memory` | Deletes local repository evidence and optionally purges remote scopes | Sources: [integrations/mem0-agent-plugin/core/memory_cli.py:61-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L61-L80), [integrations/antigravity-plugin/core/memory_cli.py:61-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_cli.py#L61-L80), [integrations/kimi-plugin/core/memory_cli.py:61-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_cli.py#L61-L80), [integrations/cursor-plugin/core/memory_cli.py:61-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_cli.py#L61-L80), [integrations/codex-plugin/core/memory_cli.py:61-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_cli.py#L61-L80) > [!WARNING] > The `forget` command enforces a safety check requiring the `--yes` flag; without it, the command refuses to delete data and exits with code 2. > Sources: [integrations/mem0-agent-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L123-L129), [integrations/antigravity-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_cli.py#L123-L129), [integrations/kimi-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_cli.py#L123-L129), [integrations/cursor-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_cli.py#L123-L129), [integrations/codex-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_cli.py#L123-L129) ### MCP Server Integration and Manifests Model Context Protocol (MCP) manifests (`mcp.json`) bind the memory core into host IDE environments using standard input/output transport streams. The manifest defines server registration parameters that execute the Python MCP server script within the plugin root directory. ```json { "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", "mcpServers": { "mem0": { "type": "stdio", "command": "python3", "args": [ "${PLUGIN_ROOT}/core/mcp_server.py" ] } } } ``` Sources: [integrations/mem0-agent-plugin/mcp.json:1-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/mcp.json#L1-L12) ## External Agent and Workflow Ecosystem ### OpenClaw and Pi Agent Integrations Mem0 extends beyond core LLM memory engines by providing specialized integrations for agent frameworks, runtime environments, and workflow automation tools. These packages bridge external architectures into the Mem0 memory fabric without requiring custom storage layers. The [OpenClaw Integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/openclaw-integration) plugin (`openclaw-mem0`) exposes six core memory tools (`memory_search`, `memory_add`, `memory_get`, `memory_list`, `memory_update`, and `memory_delete`) while handling automatic recall and capture across agent turns. It isolates multi-agent setups by routing per-agent namespaces through session key resolution and supports both platform and self-hosted backends. Similarly, the [Pi Agent Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/pi-agent-plugin) package structures memory management through exported configuration loaders, scoping handlers, formatting utilities, and command extensions. ```typescript /** * OpenClaw Memory (Mem0) Plugin * * Long-term memory via Mem0 — supports both the Mem0 platform * and the open-source self-hosted SDK. Uses the official `mem0ai` package. * * Features: * - 6 core tools: memory_search, memory_add, memory_get, memory_list, * memory_update, memory_delete * - Short-term (session-scoped) and long-term (user-scoped) memory * - Auto-recall: injects relevant memories (both scopes) before each agent turn * - Auto-capture: stores key facts scoped to the current session after each agent turn * - Per-agent isolation: multi-agent setups write/read from separate userId namespaces * automatically via sessionKey routing (zero breaking changes for single-agent setups) * - CLI: openclaw mem0 search, openclaw mem0 status * - Dual mode: platform or open-source (self-hosted) */ ``` Sources: [integrations/openclaw/index.ts:1-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L1-L17) ```typescript export type { Scope, Mem0Config, ScopeContext, CustomCategory, } from "./types.ts"; export { DEFAULT_CUSTOM_CATEGORIES } from "./types.ts"; export { loadConfig, CONFIG_DIR } from "./config/index.ts"; export { registerMemoryTool, buildToolExecute } from "./memory/tools.ts"; export { detectAppId, detectRunId, resolveSearchFilters, resolveAddParams } from "./memory/scoping.ts"; export { formatAge, formatMemoryCompact, formatMemoryList, groupByCategory } from "./memory/formatting.ts"; export { setupAutoCapture, extractConversation } from "./capture/index.ts"; export { MEMORY_POLICY } from "./prompt.ts"; export { registerCommands } from "./commands.ts"; export { captureEvent, captureToolEvent, captureCommandEvent, _getEventQueue, _resetForTesting } from "./telemetry.ts"; ``` Sources: [integrations/pi-agent-plugin/src/index.ts:1-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/index.ts#L1-L22) > [!NOTE] > The OpenClaw plugin reads authentication settings dynamically from `openclaw.json` plugin config via `readPluginAuth()`, accommodating post-startup login commands (`openclaw mem0 login`) as the single source of truth. > Sources: [integrations/openclaw/index.ts:98-104](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L98-L104) ### Strands Agent Framework Integration The [Mem0 Strands](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/mem0-strands) package (`mem0-strands`) provides persistent long-term memory for Strands agents via `Mem0MemoryStore`. It plugs into agent execution loops through a `MemoryManager` to deliver automatic memory injection and extraction. ```python """Strands Mem0 -- persistent long-term memory for Strands agents, backed by Mem0. :class:`Mem0MemoryStore` is a Strands ``MemoryStore`` that plugs into the agent loop via a :class:`~strands.memory.MemoryManager`, with automatic memory injection and extraction. It implements both write sinks, so ``extraction`` uses Mem0's server-side extraction (no extra model call). For the explicit, model-called tool (``store`` / ``retrieve`` / ``get`` / ``delete``), use the ``mem0_memory`` tool from ``strands-agents-tools``; a store and the tool can share one Mem0 backend and namespace. """ ``` Sources: [integrations/mem0-strands/python/src/mem0_strands/__init__.py:1-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/__init__.py#L1-L11) > [!TIP] > `Mem0MemoryStore` implements both write sinks so that extraction leverages Mem0's server-side extraction natively, avoiding extra language model calls during agent execution. > Sources: [integrations/mem0-strands/python/src/mem0_strands/__init__.py:4-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/__init__.py#L4-L6) ## Cross-Subsystem Data and Telemetry Flow ### Background Telemetry and Execution Coordination End-to-end data coordination across host harnesses, background sync workers, and anonymized diagnostic telemetry relies on decoupled logging and asynchronous flushing. In agent CLI implementations, commands execute within an instantiated `EvidenceStore` context that records control actions, telemetry payloads, and local sqlite state updates before finalizing execution through deferred background worker threads. ```python if args.plugin_data_dir: os.environ["MEM0_CODE_DATA_DIR"] = args.plugin_data_dir store = EvidenceStore() try: repo = resolve_repo(os.getcwd()) telemetry.record("control", repo=repo, action=args.command) ``` Sources: [integrations/mem0-agent-plugin/core/memory_cli.py:86-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L86-L91) ```python finally: store.close() telemetry.spawn_flush() return 0 ``` Sources: [integrations/mem0-agent-plugin/core/memory_cli.py:147-150](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L147-L150) > [!NOTE] > Telemetry flushing and local store closures are guaranteed via a `finally` block in CLI entry points, invoking `telemetry.spawn_flush()` to transmit diagnostics asynchronously without blocking shell process exit. > Sources: [integrations/mem0-agent-plugin/core/memory_cli.py:147-150](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L147-L150) ### Harness Configuration and Data Routing Runtime contexts receive environment parameters, configuration prefixes, and source identifiers through modular configuration routines. The harness configuration initializes specific global variables governing environment lookup paths, local plugin data directories, and source telemetry tagging. ```python def configure_harness( name: str, env_prefix: str = "", data_dir_name: str = "", source_tag: str = "", ) -> None: global _harness_name, _harness_env_prefix, _harness_data_dir_name, _harness_source_tag _harness_name = name _harness_env_prefix = env_prefix or f"MEM0_{name.upper().replace('-', '_')}" _harness_data_dir_name = data_dir_name or f"{name}-plugin" _harness_source_tag = source_tag or f"{name.replace('-', '_')}_plugin" ``` Sources: [integrations/mem0-agent-plugin/core/memory_core.py:40-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L40-L51) | Parameter | Default Value | Purpose | Sources | | :--- | :--- | :--- | :--- | | `name` | `"generic"` | Identifies the active agent harness or plugin framework | [integrations/mem0-agent-plugin/core/memory_core.py:34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L34) | | `env_prefix` | `"MEM0_PLUGIN"` | Prefix for environment variable overrides | [integrations/mem0-agent-plugin/core/memory_core.py:35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L35) | | `data_dir_name` | `"mem0-plugin"` | Subdirectory name for local evidence and state storage | [integrations/mem0-agent-plugin/core/memory_core.py:36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L36) | | `source_tag` | `"mem0_plugin"` | Telemetry tag identifying the event origin channel | [integrations/mem0-agent-plugin/core/memory_core.py:37](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L37) | Sources: [integrations/mem0-agent-plugin/core/memory_core.py:34-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L34-L51) ## Where to go next | I want to… | Read | | --- | --- | | How do developers and autonomous agents initialize Mem0, capture conversational memories, and query hybrid memory stores with copy-pasteable snippets across Python and Node.js? | [Quick Start](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/orientation/quick-start) | | How is the Mem0 monorepo organized across Python packages, TypeScript packages, FastAPI backend, Next.js dashboard, CLI implementations, and agent plugins? | [Project Structure](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/orientation/project-structure) | | How does the Python core memory engine extract, add, update, delete, search, and reset user, agent, and session memories using LLMs and vector backends? | [Memory Lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle) | | How does the Python retrieval engine fuse dense vector similarity, BM25 keyword matching, entity boosting, and temporal recency scoring to rank memories? | [Multi-Signal Retrieval](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/multi-signal-retrieval) | | How does the Python engine record state changes, manage SQLite or relational audit logs, and isolate user, agent, run, and metadata scopes? | [Storage and History](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/storage-and-history) | | How does Mem0 parse structured entities and temporal facts from raw messages using spaCy and entity extraction pipelines for cross-memory linking? | [Entity Extraction](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/entity-extraction) | | How does the Python client interface with the Mem0 hosted platform and self-hosted HTTP endpoints for remote memory synchronization and project scoping? | [Python Client SDK](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/python-client-sdk) | | How does the TypeScript OSS package implement the core Memory class, orchestrate LLM extraction, manage message state, and execute CRUD memory operations? | [Typescript Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/typescript-core/typescript-core) | | How does the Python core adapt diverse LLM backends (OpenAI, Anthropic, Bedrock, DeepSeek, Gemini, Groq, Ollama, vLLM, LiteLLM) for structured fact extraction? | [Python LLM Adapters](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/python-llm-adapters) | | How does the TypeScript engine configure and communicate with language model providers including OpenAI, Anthropic, Google, DeepSeek, and Mistral? | [TypeScript LLM Adapters](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/typescript-llm-adapters) | | How does the Python engine generate vector embeddings across OpenAI, Azure OpenAI, AWS Bedrock, FastEmbed, Gemini, Hugging Face, Ollama, and Vertex AI? | [Python Embedding Adapters](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/python-embedding-adapters) | | How does the TypeScript engine instantiate and call embedding generators across providers such as OpenAI, Google, Hugging Face, Bedrock, and FastEmbed? | [TypeScript Embedding Adapters](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/typescript-embedding-adapters) | | How do Cohere, Hugging Face Cross-Encoder, LLM-based, Sentence Transformer, and ZeroEntropy rerankers re-score candidate memory retrievals? | [Reranking Models](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/reranking-models) | | What base abstractions, collection schemas, and query filters govern vector store drivers across both Python and TypeScript runtimes? | [Vector Store Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/vector-store-architecture) | | How do Qdrant, Pinecone, Milvus, Weaviate, Turbopuffer, Upstash, and S3 Vectors execute cloud-native vector insertion and ANN querying? | [Cloud Vector Backends](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/cloud-vector-backends) | | How do relational and search engines (pgvector, Elasticsearch, OpenSearch, Redis, Valkey, Cassandra, Neptune Analytics, Oracle, MongoDB, Azure AI Search, Baidu) store embeddings? | [Database Vector Stores](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/database-vector-stores) | | How do Chroma, FAISS, and in-memory vector implementations provide zero-dependency local indexing for prototyping and unit testing? | [Embedded Vector Stores](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/embedded-vector-stores) | | How does the self-hosted FastAPI application bootstrap application state, handle CORS, register routers, enforce rate limits, and map errors? | [FastAPI Application](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application) | | How does the server issue and validate API keys, manage user passwords, enforce admin RBAC, and handle JWT refresh tokens? | [Server Authentication](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/server-authentication) | | How are database connections, SQLAlchemy models, and Alembic migrations structured for user credentials, API keys, request logs, and system settings? | [Database and Migrations](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/database-and-migrations) | | What REST endpoints and schemas are exposed for memory manipulation, entity browsing, API key rotation, request telemetry, and health inspection? | [Server API Endpoints](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/server-api-endpoints) | | How is the Next.js admin dashboard structured with Redux store state, API query hooks, authentication session providers, and setup wizards? | [Dashboard Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/dashboard-architecture) | | How do the memories, entities, analytics, API keys, webhooks, and settings dashboard pages render system data and trigger admin mutations? | [Dashboard Views](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/dashboard-views) | | How does the Python CLI dispatch terminal commands, switch backends (OSS vs Platform), and format rich output? | [Python CLI Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/python-cli-architecture) | | How does the TypeScript/Node.js CLI process command line options, resolve authentication tokens, and talk to platform or local backends? | [Node.js CLI Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/node-js-cli-architecture) | | How do the CLI init and agent mode commands allow autonomous agents to mint API keys without web browsers and later claim accounts? | [Agent Account Provisioning](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/agent-account-provisioning) | | How does the CLI sync configuration, MCP tool declarations, and hooks into agent IDE environments? | [Plugin Synchronization](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/plugin-synchronization) | | How does the common Python plugin core implement memory capture, background flush workers, hook runners, and MCP servers across IDE plugins? | [Python Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core) | | How does the common TypeScript agent core manage lifecycle hooks, session scoping, prompt synthesis, identity detection, and memory formatting? | [TypeScript Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/typescript-agent-core) | | How does the Claude Code plugin intercept transcript streams and run MCP tools to synchronize conversation history with Mem0? | [Claude Code Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin) | | How do the Cursor, Codex, Kimi, and Antigravity plugins configure MCP manifests, adapt host event hooks, and forward IDE state? | [IDE Plugins Family](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/ide-plugins-family) | | How does OpenClaw register memory tools, handle isolation and recall, provide CLI setup wizards, and execute memory CRUD operations? | [OpenClaw Integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/openclaw-integration) | | How does the OpenCode plugin bind workspace project scopes, manage API key authentication, and inject memories into developer agent turns? | [OpenCode Integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/opencode-integration) | | How does the Pi Agent plugin implement attribution, command handling, memory formatting, and scoping for agent execution loops? | [Pi Agent Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/pi-agent-plugin) | | How does the Hermes plugin bridge Mem0 memory management into Hermes agent pipelines using custom LLM bindings? | [Hermes Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/hermes-plugin) | | How does Mem0 wrap Vercel AI SDK LanguageModel instances to inject long-term user context and intercept completions? | [Vercel AI SDK Integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/vercel-ai-sdk-integration) | | How does the mem0-strands package structure thread-level and hierarchical memory graphs for complex agent trajectories? | [Mem0 Strands](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/mem0-strands) | | How does the n8n community node expose Mem0 memory operations inside low-code workflow automations? | [n8n Workflow Nodes](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/n8n-workflow-nodes) | | How does the Zapier integration implement authentication, memory addition, deletion, and search triggers for automated app zaps? | [Zapier Integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/zapier-integration) | | How do developer agent skills and the doc search tool provide inline documentation retrieval to AI coding assistants? | [Documentation Agent Skills](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/documentation-agent-skills) | | How does Mem0 record, anonymize, and transmit anonymous usage metrics across Python, TypeScript, CLI, and server components? | [Telemetry and Metrics](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/telemetry-ops/telemetry-and-metrics) | | How does the conformance suite validate plugin adapters and harness behaviors against standardized memory artifacts? | [Plugin Conformance Testing](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/telemetry-ops/plugin-conformance-testing) | | How do operators migrate memory collections, vector embeddings, and configurations from open-source instances to the Mem0 cloud platform? | [OSS to Platform Migration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/telemetry-ops/oss-to-platform-migration) | ## Related - [Quick Start](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/orientation/quick-start) - [Project Structure](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/orientation/project-structure) - [Memory Lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle) --- ## Technical docs: GET List bundled LLM and embedder providers URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/list-bundled-providers ## Responses ## Try It --- ## Technical docs: Quick Start URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/orientation/quick-start
Relevant source files The following files were used as context for generating this wiki page: - [mem0-ts/src/community/src/integrations/langchain/mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [integrations/mem0-strands/python/src/mem0_strands/store.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py) - [integrations/deepseek-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [mem0/configs/prompts.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/prompts.py) - [mem0-ts/src/oss/src/prompts/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/prompts/index.ts) - [integrations/vercel-ai-sdk/src/mem0-utils.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts) - [integrations/mem0-strands/python/src/mem0_strands/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/__init__.py) - [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts) - [integrations/mem0-strands/python/src/mem0_strands/client.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py) - [mem0/proxy/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/proxy/main.py) - [mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/__init__.py) - [integrations/pi-agent-plugin/src/prompt.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/prompt.ts)
## Overview This quick start guide demonstrates how to install, initialize, and integrate Mem0 across Python and TypeScript environments to equip language models and coding agents with persistent, context-aware memory [Python Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core), [Typescript Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/typescript-core/typescript-core). By combining client instantiation, conversational write paths, and scoped semantic search, developers can store user preferences, manage project trajectories, and bridge agent frameworks without boilerplate [Memory Lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle), [Mem0 Strands](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/mem0-strands). ```typescript constructor(config: Partial = {}) { // Merge and validate config this.config = ConfigManager.mergeConfig(config); ``` Sources: [mem0-ts/src/oss/src/memory/index.ts:220-222](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts#L220-L222) The architecture supports both local self-hosted configurations and the managed Mem0 platform [Hermes Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/hermes-plugin), enabling seamless zero-code proxying via OpenAI-compatible endpoints Proxy as well as native plugins for ecosystem harnesses like LangChain mem0.ts, Vercel AI SDK mem0-utils.ts, and IDE assistants [Claude Code Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin). ```typescript add_memory: tool({ description: "Add a new memory. This method is called everytime the user informs anything about themselves, their preferences, or anything that has any relevant information which can be useful in the future conversation. This can also be called when the user asks you to remember something. Set infer to false to store the memory verbatim without LLM fact extraction.", args: { text: tool.schema.string().describe("Memory text content"), user_id: tool.schema.string().optional().describe("User ID"), app_id: tool.schema.string().optional().describe("App/Project ID"), agent_id: tool.schema.string().optional().describe("Agent ID"), metadata: tool.schema.record(tool.schema.string(), tool.schema.any()).optional().describe("Metadata key-value pairs"), infer: tool.schema.boolean().optional().describe("Set to false to store memory verbatim without LLM fact extraction"), scope: tool.schema.string().optional().describe('Write scope: "project" (this repo, default), "session" (this run), or "global" (user-wide, all projects). Use "global" only when explicitly asked.') }, ``` Sources: [integrations/opencode-plugin/opencode-mem0.ts:380-390](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L380-L390) ## Installing and Initializing Mem0 ### Python and TypeScript SDK Setup To install Mem0 in Python and TypeScript environments, package managers install the core client libraries from PyPI and npm. Python environments import `Memory` or `MemoryClient` from the `mem0` package mem0/__init__.py, while TypeScript projects utilize `MemoryClient` from `mem0ai` or community integrations such as `Mem0Memory` mem0-ts/src/community/src/integrations/langchain/mem0.ts. ```bash pip install mem0ai npm install mem0ai @langchain/core @langchain/community ``` Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:1-2](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L1-L2), [mem0/__init__.py:3-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/__init__.py#L3-L6) ### Client Instantiation and Configuration Client initialization requires passing configuration options containing credentials and optional host endpoints. In TypeScript, `Mem0Memory` validates that both `apiKey` and `sessionId` are present during construction before instantiating the underlying `MemoryClient` mem0-ts/src/community/src/integrations/langchain/mem0.ts. ```typescript import { Mem0Memory } from "./mem0"; const memory = new Mem0Memory({ sessionId: "user123", apiKey: "your-api-key", memoryOptions: { user_id: "user123", }, }); ``` Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:151-206](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L151-L206) > [!WARNING] > Omitting `apiKey` or `sessionId` when constructing `Mem0Memory` throws an immediate runtime error before any client connection occurs. Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:172-178](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L172-L178) | Property | Type | Default | Purpose | | :--- | :--- | :--- | :--- | | `apiKey` | `string` | *Required* | Authentication credential for the Mem0 service | | `sessionId` | `string` | *Required* | Unique identifier for the chat session or user scope | | `memoryKey` | `string` | `"history"` | Key used to store and retrieve memory variables in LangChain | | `humanPrefix` | `string` | `"Human"` | Prefix label for human utterances in formatted buffer strings | | `aiPrefix` | `string` | `"AI"` | Prefix label for assistant responses in formatted buffer strings | | `separateMessages` | `boolean` | `false` | Return separate messages for chat history versus a single condensed `HumanMessage` | Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:152-170](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L152-L170) ## Storing Conversational Context ### Conversation Context Capture and Extraction ### LangChain Integration and Message Condensation ```typescript import { Mem0Memory } from "./mem0"; import { ChatOpenAI } from "@langchain/openai"; import { ConversationChain } from "langchain/chains"; const memory = new Mem0Memory({ sessionId: "user123", apiKey: process.env.MEM0_API_KEY!, memoryOptions: { user_id: "user123", }, }); const model = new ChatOpenAI({ modelName: "gpt-4o-mini", temperature: 0, }); const chain = new ConversationChain({ llm: model, memory }); ``` Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:131-149](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L131-L149) ```typescript export const condenseMem0MemoryIntoHumanMessage = ( memory: Memory[], ): HumanMessage => { const basePrompt = "These are the memories I have stored. Give more weightage to the question by users and try to answer that first. You have to modify your answer based on the memories I have provided. If the memories are irrelevant you can ignore them. Also don't reply to this section of the prompt, or the memories, they are only for your reference. The MEMORIES of the USER are: \n\n"; const systemPrompt = mem0MemoryContextToSystemPrompt(memory); return new HumanMessage(`${basePrompt}\n${systemPrompt}`); }; ``` Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:50-58](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L50-L58) > [!NOTE] > `condenseMem0MemoryIntoHumanMessage` formats retrieved memories into a single instruction-prefixed `HumanMessage` by default, instructing the model to prioritize direct user questions while referencing stored facts. Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:50-58](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L50-L58) ### Memory Formatting Utilities ```typescript export const mem0MemoryToMessages = (memories: Memory[]): BaseMessage[] => { if (!memories || !Array.isArray(memories)) { return []; } const messages: BaseMessage[] = []; const memoryContent = memories .filter((m) => m?.memory) .map((m) => m.memory) .join("\n"); if (memoryContent) { messages.push(new SystemMessage(memoryContent)); } memories.forEach((memory) => { if (memory.messages) { memory.messages.forEach((msg) => { const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content); if (msg.role === "user") { messages.push(new HumanMessage(content)); } else if (msg.role === "assistant") { messages.push(new AIMessage(content)); } else if (content) { messages.push(new ChatMessage(content, msg.role)); } }); } }); return messages; }; ``` Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:65-102](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L65-L102) > [!TIP] > Setting `separateMessages: true` in `Mem0MemoryInput` causes `loadMemoryVariables` to return distinct `SystemMessage` and chat history objects via `mem0MemoryToMessages` rather than a unified prompt block. Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:123-124](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L123-L124), [mem0-ts/src/community/src/integrations/langchain/mem0.ts:240-246](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L240-L246) ## Querying Memories Across Scopes ### Semantic Retrieval and Scoped Parameters Memories can be queried dynamically based on conversation input or retrieved entirely via scoped identifiers. When input values are present during memory loading, `Mem0Memory` executes a semantic search query against the client; otherwise, it retrieves all stored records for the target entity. ```typescript async loadMemoryVariables(values: InputValues): Promise { const searchType = values.input ? "search" : "get_all"; let memories: Memory[] = []; try { if (searchType === "get_all") { memories = await this.mem0Client.getAll({ user_id: this.sessionId, ...this.memoryOptions, }); } else { memories = await this.mem0Client.search(values.input, { user_id: this.sessionId, ...this.memoryOptions, }); } } catch (error) { console.error("Error loading memories:", error); return this.returnMessages ? { [this.memoryKey]: [] } : { [this.memoryKey]: "" }; } if (this.returnMessages) { return { [this.memoryKey]: this.separateMessages ? mem0MemoryToMessages(memories) : [condenseMem0MemoryIntoHumanMessage(memories)], }; } return { [this.memoryKey]: this.separateMessages ? getBufferString( mem0MemoryToMessages(memories), this.humanPrefix, this.aiPrefix, ) : (condenseMem0MemoryIntoHumanMessage(memories).content ?? ""), }; } ``` Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:217-257](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L217-L257) > [!WARNING] > If a query execution throws an error during `loadMemoryVariables`, the method catches the failure and returns an empty array or empty string depending on `returnMessages` configuration rather than interrupting the chat chain. Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:233-238](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L233-L238) ### Agent Harness Filtering and Tool Parameters Agent harnesses partition memory access using granular identifiers such as user IDs, agent IDs, and run IDs. For instance, the DeepSeek integration registers a `search_memory` tool that validates user overrides and resolves filters prior to querying the vector client. ```typescript const scopeParams = { userId: { type: "string", description: "Entity that owns the memory. Defaults to the plugin's configured userId; cross-user overrides require allowUserOverride in plugin configuration.", }, agentId: { type: "string", description: "Optional agent scope, to partition memories by agent.", }, runId: { type: "string", description: "Optional run/session scope, to partition memories by session.", }, } as const; ``` Sources: [integrations/deepseek-plugin/src/index.ts:80-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L80-L94) > [!CAUTION] > The platform rejects top-level entity parameters during search operations; search scopes must be nested inside the `filters` property, unlike memory addition calls which accept top-level parameters. Sources: [integrations/deepseek-plugin/src/index.ts:200-202](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L200-L202) ## Configuring Framework and SDK Adapters ### LangChain Integration Architecture The LangChain integration package maps Mem0 persistent memory storage into LangChain memory management abstractions through the `Mem0Memory` class. This class extends `BaseChatMemory` to load, format, and save conversational turns and retrieved facts using `MemoryClient` from `mem0ai`. ```typescript export class Mem0Memory extends BaseChatMemory implements Mem0MemoryInput { memoryKey = "history"; apiKey: string; sessionId: string; humanPrefix = "Human"; aiPrefix = "AI"; mem0Client: InstanceType; memoryOptions: AddMemoryOptions | SearchMemoryOptions | GetAllMemoryOptions; mem0Options: ClientOptions; separateMessages?: boolean; ``` Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:151-170](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L151-L170) Formatting utilities transform retrieved memory items into structured message formats or consolidated prompt strings. The `condenseMem0MemoryIntoHumanMessage` function builds a `HumanMessage` incorporating stored facts with explicit instructions for the model to prioritize user questions and treat retrieved memory strictly as reference context. ```typescript export const condenseMem0MemoryIntoHumanMessage = ( memory: Memory[], ): HumanMessage => { const basePrompt = "These are the memories I have stored. Give more weightage to the question by users and try to answer that first. You have to modify your answer based on the memories I have provided. If the memories are irrelevant you can ignore them. Also don't reply to this section of the prompt, or the memories, they are only for your reference. The MEMORIES of the USER are: \n\n"; const systemPrompt = mem0MemoryContextToSystemPrompt(memory); return new HumanMessage(`${basePrompt}\n${systemPrompt}`); }; ``` Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:50-58](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L50-L58) > [!TIP] > Setting `separateMessages: true` in `Mem0MemoryInput` causes `loadMemoryVariables` to return distinct `SystemMessage` instances and chat history objects via `mem0MemoryToMessages` rather than collapsing all context into a single unified prompt block. Sources: [mem0-ts/src/community/src/integrations/langchain/mem0.ts:116-124](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L116-L124), [mem0-ts/src/community/src/integrations/langchain/mem0.ts:240-246](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts#L240-L246) ### Vercel AI SDK Utilities The Vercel AI SDK utilities module provides functions for adding, updating, retrieving, and searching memories within language model pipelines. The `updateMemories` function dispatches POST requests to the Mem0 API endpoint, transmitting conversation messages alongside optional configuration fields with specific attribution headers. ```typescript const updateMemories = async (messages: Array, config?: Mem0ConfigSettings) => { try { const apiKey = loadApiKey({ apiKey: config?.mem0ApiKey, environmentVariableName: "MEM0_API_KEY", description: "Mem0", }); const body: Record = { messages }; if (config?.user_id) body.user_id = config.user_id; if (config?.app_id) body.app_id = config.app_id; if (config?.agent_id) body.agent_id = config.agent_id; if (config?.run_id) body.run_id = config.run_id; if (config?.metadata) body.metadata = config.metadata; if (config?.infer != null) body.infer = config.infer; const options = { method: 'POST', headers: { Authorization: `Token ${apiKey}`, 'Content-Type': 'application/json', // Surface attribution. Set-once by contract: this wrapper is the // outermost layer on these raw fetch calls. 'X-Mem0-Source': 'VERCEL_AI_SDK', 'X-Mem0-Client': `mem0-vercel-ai-provider/${PROVIDER_VERSION}` }, body: JSON.stringify(body), }; const baseUrl = config?.host || 'https://api.mem0.ai'; const response = await fetch(`${baseUrl}/v3/memories/add/`, options); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); return data; } catch (error) { console.error("Error in updateMemories:", error); throw error; } } ``` Sources: [integrations/vercel-ai-sdk/src/mem0-utils.ts:325-365](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L325-L365) Memory retrieval routines in the [Vercel AI SDK integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/vercel-ai-sdk-integration) normalize varying API response structures into standard formats. The `getMemories` function guarantees a flat array output whether the underlying data payload returns as a raw array or an object containing a results key. ```typescript const getMemories = async (prompt: LanguageModelV3Prompt | string, config?: Mem0ConfigSettings) => { try { const message = typeof prompt === 'string' ? prompt : flattenPrompt(prompt); const data = await searchInternalMemories(message, config); // Normalize: always return a flat array regardless of API response shape return Array.isArray(data) ? data : (data?.results ?? []); } catch (error) { console.error("Error in getMemories:", error); throw error; } } ``` Sources: [integrations/vercel-ai-sdk/src/mem0-utils.ts:391-401](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L391-L401) ## Executing Transparent Memory Proxying ### Proxy Architecture and Interception The transparent memory proxy intercepts chat completion requests through an OpenAI-compatible interface powered by `litellm`. The proxy client initializes via the `Mem0` class, selecting either a hosted `MemoryClient` when an API key is supplied or a local `Memory` instance configured with user settings. Incoming chat requests hit the `Completions.create` method, which enforces that at least one identifier among `user_id`, `agent_id`, or `run_id` is present before processing. ```python class Mem0: def __init__( self, config: Optional[dict] = None, api_key: Optional[str] = None, host: Optional[str] = None, ): if api_key: self.mem0_client = MemoryClient(api_key, host) else: self.mem0_client = Memory.from_config(config) if config else Memory() self.chat = Chat(self.mem0_client) ``` Sources: [mem0/proxy/main.py:23-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/proxy/main.py#L23-L36) > [!WARNING] > Omitting all three scope identifiers (`user_id`, `agent_id`, and `run_id`) in a proxy completion call triggers a `ValueError` during validation. At least one scope parameter must be provided for memory persistence and retrieval. Sources: [mem0/proxy/main.py:92-93](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/proxy/main.py#L92-L93) ### Automatic Memory Augmentation When a completion request includes a user-role final message, the proxy performs background ingestion and semantic search concurrently. The user message is dispatched to storage asynchronously via a daemon thread running `_async_add_to_memory`, preventing blocking on write operations. Simultaneously, `_fetch_relevant_memories` queries the underlying client using the last six conversation turns, bounded by the configured `top_k` parameter. ```python prepared_messages = self._prepare_messages(messages) if prepared_messages[-1]["role"] == "user": self._async_add_to_memory(messages, user_id, agent_id, run_id, metadata, filters) relevant_memories = self._fetch_relevant_memories(messages, user_id, agent_id, run_id, filters, top_k) logger.debug(f"Retrieved {len(relevant_memories)} relevant memories") prepared_messages[-1]["content"] = self._format_query_with_memories(messages, relevant_memories) ``` Sources: [mem0/proxy/main.py:100-106](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/proxy/main.py#L100-L106) The retrieved facts and associated relational entities are formatted directly into the final user prompt using `MEMORY_ANSWER_PROMPT` as a system message prefix. Telemetry events (`mem0.chat.create`) are captured upon completion based on whether the client instance is local or remote. ```python def _format_query_with_memories(self, messages, relevant_memories): # Check if self.mem0_client is an instance of Memory or MemoryClient entities = [] if isinstance(self.mem0_client, mem0.memory.main.Memory): memories_text = "\n".join(memory["memory"] for memory in relevant_memories["results"]) if relevant_memories.get("relations"): entities = [entity for entity in relevant_memories["relations"]] elif isinstance(self.mem0_client, mem0.client.main.MemoryClient): memories_text = "\n".join(memory["memory"] for memory in relevant_memories) return f"- Relevant Memories/Facts: {memories_text}\n\n- Entities: {entities}\n\n- User Question: {messages[-1]['content']}" ``` Sources: [mem0/proxy/main.py:176-186](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/proxy/main.py#L176-L186) > [!TIP] > The proxy automatically inspects the model for function calling compatibility using `litellm.supports_function_calling(model)` and rejects unsupported models immediately before executing storage or retrieval steps. Sources: [mem0/proxy/main.py:95-98](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/proxy/main.py#L95-L98) ## Managing Agent Plugin Embeddings ### Plugin Architecture and Cordis Bindings The DeepSeek agent plugin integrates Mem0 long-term memory into the Cordis harness architecture by defining the plugin module name as `mem0` and declaring dependencies on `tools` and `systemPrompt` services. During bootstrap, the `apply` function validates configuration inputs, enforcing that an API key is present via `config.apiKey` or the `MEM0_API_KEY` environment variable and that `config.userId` is a non-empty string. ```typescript export const name = "mem0"; export const inject = ["tools", "systemPrompt"]; ``` Sources: [integrations/deepseek-plugin/src/index.ts:30-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L30-L31) ```typescript export function apply(ctx: Context, config: Config): void { const apiKey = config.apiKey ?? process.env.MEM0_API_KEY; if (!apiKey) { throw new Error("deepseek-plugin: set config.apiKey or the MEM0_API_KEY env var"); } const userId = config.userId?.trim(); if (!userId || /^\*+$/.test(userId)) { throw new Error("deepseek-plugin: config.userId is required"); } ``` Sources: [integrations/deepseek-plugin/src/index.ts:96-104](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L96-L104) | Configuration Option | Type | Default | Purpose | | :--- | :--- | :--- | :--- | | `apiKey` | `string` | `process.env.MEM0_API_KEY` | Mem0 API authentication token. | | `userId` | `string` | *required* | Default entity owning stored memories. | | `allowUserOverride` | `boolean` | `false` | Explicitly permits model-selected cross-user access. | | `host` | `string` | `api.mem0.ai` | Optional Mem0 Platform base-URL override. | | `autoRecall` | `boolean` | `true` | Recalls relevant memory before each model request. | | `autoCapture` | `boolean` | `true` | Stores completed interaction turns automatically. | Sources: [integrations/deepseek-plugin/src/index.ts:53-66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L53-L66) > [!WARNING] > Providing an asterisk wildcard (`*`) or leaving `userId` empty causes initialization to throw an immediate error during plugin application. Sources: [integrations/deepseek-plugin/src/index.ts:101-104](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L101-L104) ### Session Lifecycle and Event Hooks The plugin utilizes a weak map to maintain per-session states, tracking both conversation messages and local lifecycle handlers. When `autoRecall` is active, the system prompt assembly hook intercepts context requests, extracts the latest user prompt, and queries the memory client with an auto-recall limit of 5 results. ```typescript const sessionStates = new WeakMap(); const stateFor = (session: object): SessionState => { let state = sessionStates.get(session); if (!state) { const lifecycle = createMemoryLifecycle({ recallHeading: USER_RECALL_HEADING }); lifecycle.beginSession(); state = { lifecycle, messages: [] }; sessionStates.set(session, state); } return state; }; ``` Sources: [integrations/deepseek-plugin/src/index.ts:111-121](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L111-L121) ```typescript if (config.autoRecall !== false) { ctx.on("system-prompt/assemble", async (_input, context, next): Promise => { const assembly = await next(); const { agent, signal } = context; if (!agent || signal?.aborted) return assembly; const state = stateFor(agent.session); const prompt = state.lifecycle.prepareConversation( agent.session .deriveMessages() .filter((message) => message.role === "user" && message.source?.kind === "user"), ).at(-1)?.content; if (!prompt) return assembly; const memoryContext = await state.lifecycle.recall(prompt, true, async (query) => { const started = Date.now(); try { const result = await client.search(query, { filters: resolveSearchFilters({}, userId), topK: AUTO_RECALL_LIMIT, }); ``` Sources: [integrations/deepseek-plugin/src/index.ts:129-149](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L129-L149) ```mermaid flowchart TD A["Session Event"] --> B{Event Type?} B -->|turn/start| C["Reset message buffer"] B -->|user/message| D["Push user message"] B -->|assistant/message| E["Push assistant message"] B -->|turn/end| F{Reason completed?} F -->|Yes| G["Add conversation to Mem0 client"] F -->|No| H["Skip storage"] ``` Sources: [integrations/deepseek-plugin/src/index.ts:174-196](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L174-L196) > [!TIP] > The python agent core modules across integration plugins share common harness configuration parameters, exposing environment prefix mapping, data directory naming, and source tagging helpers. Sources: [integrations/antigravity-plugin/core/memory_core.py:40-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L40-L60) ## Related - [Overview](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/orientation/overview) - [Project Structure](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/orientation/project-structure) --- ## Technical docs: Project Structure URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/orientation/project-structure
Relevant source files The following files were used as context for generating this wiki page: - [cli/python/src/mem0_cli/commands/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/__init__.py) - [pyproject.toml](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/pyproject.toml) - [cli/python/pyproject.toml](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/pyproject.toml) - [mem0-ts/package.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/package.json) - [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts) - [cli/python/src/mem0_cli/app.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py) - [cli/python/src/mem0_cli/backend/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/__init__.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/mem0-strands/python/pyproject.toml](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/pyproject.toml) - [cli/node/package.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/package.json) - [mem0-ts/src/oss/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/index.ts) - [mem0-ts/src/community/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/index.ts) - [integrations/pi-agent-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/index.ts) - [server/dashboard/package.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/__init__.py) - [mem0-ts/src/community/package.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/package.json) - [mem0-ts/src/oss/package.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/package.json) - [cli/python/src/mem0_cli/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/__init__.py) - [integrations/agent-plugin-core/typescript/package.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/package.json) - [integrations/vercel-ai-sdk/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/index.ts) - [mem0/client/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/__init__.py) - [integrations/pi-agent-plugin/package.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/package.json) - [integrations/mem0-strands/python/src/mem0_strands/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/__init__.py) - [mem0/configs/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/__init__.py) - [cli/python/src/mem0_cli/__main__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/__main__.py) - [cli/cli-spec.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json) - [Makefile](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/Makefile) - [mem0-ts/src/community/src/integrations/langchain/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/index.ts)
## Overview The repository is structured as a multi-language monorepo that organizes core memory engines, cross-platform SDKs, dual command-line interfaces, server tooling, and integrated agent plugins into discrete, cohesive subdirectories. At the root level, build automation via Makefiles and Python Hatchling configurations (`pyproject.toml`) coordinate packaging, formatting, linting, and multi-version test environments for python artifacts. Sources: [Makefile:1-52](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/Makefile#L1-L52), [pyproject.toml:1-174](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/pyproject.toml#L1-L174) TypeScript modules are separated into standalone packages such as the main client library (`mem0-ts`), open-source memory store implementations (`mem0-ts/src/oss`), and community extensions (`mem0-ts/src/community`), while dashboard services reside within dedicated web application directories (`server/dashboard`). Parallel Python and Node.js command-line tool packages (`cli/python`, `cli/node`) implement the standard command interface defined by shared command-line specifications (`cli/cli-spec.json`). Furthermore, specialized agent plugins (`integrations/openclaw`, `integrations/pi-agent-plugin`, `integrations/vercel-ai-sdk`, `integrations/mem0-strands`) tie into shared agent cores (`integrations/agent-plugin-core`) to provide background capture, auto-recall, and lifecycle hooks across diverse AI frameworks. Sources: [mem0-ts/package.json:1-150](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/package.json#L1-L150), [server/dashboard/package.json:1-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L1-L84), [cli/cli-spec.json:1-562](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L1-L562), [integrations/openclaw/index.ts:1-58](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L1-L58) ## Monorepo Build and Packaging Layout ### Root Build Configuration and Packaging The repository root uses Hatchling as its build backend to package the core Python library under project name `mem0ai` version `2.2.1`. The root `pyproject.toml` defines required core runtime dependencies including vector store clients, Pydantic, OpenAI, httpx, posthog, pytz, SQLAlchemy, and protobuf, alongside extensive optional feature groups. | Optional Feature Group | Target / Purpose | | :--- | :--- | | `nlp` | spaCy natural language processing support (`spacy>=3.7.0`) | | `vector-stores` | Integrations for vector backends like Chroma, Weaviate, Pinecone, FAISS, Qdrant variants, and relational extensions | | `llms` | Large language model provider integrations including Groq, Together, LiteLLM, Ollama, and Google Generative AI | | `extras` | Additional utilities, AWS boto3, LangChain ecosystems, and transformers | | `test` | Test execution frameworks (`pytest`, `pytest-mock`, `pytest-asyncio`) | | `dev` | Developer tools including Ruff and pre-commit hooks | Sources: [pyproject.toml:1-87](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/pyproject.toml#L1-L87) > [!NOTE] > Hatchling build targets explicitly scope packaged files to the `mem0` source directory and config assets while filtering out non-source files via exclusion rules. Sources: [pyproject.toml:92-106](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/pyproject.toml#L92-L106) ### Tooling Automation and Makefiles The root `Makefile` exposes automation commands for formatting, sorting imports, building, publishing, and running multi-version test matrices via Hatch environments. ``` .PHONY: format sort lint # Variables ISORT_OPTIONS = --profile black PROJECT_NAME := mem0ai # Default target all: format sort lint install: hatch env create install_all: pip install ruff==0.16.0 groq together boto3 'litellm>=1.83.7,<1.98.0' ollama chromadb weaviate weaviate-client sentence_transformers vertexai \ google-generativeai elasticsearch opensearch-py vecs "pinecone<7.0.0" pinecone-text faiss-cpu langchain-community \ upstash-vector azure-search-documents langchain-memgraph langchain-neo4j langchain-aws rank-bm25 pymochow pymongo psycopg kuzu databricks-sdk valkey # Format code with ruff format: hatch run format # Sort imports with isort sort: hatch run isort mem0/ # Lint code with ruff lint: hatch run lint docs: cd docs && mintlify dev build: hatch build publish: hatch publish clean: rm -rf dist test: hatch run test test-py-3.10: hatch run dev_py_3_10:test test-py-3.11: hatch run dev_py_3_11:test test-py-3.12: hatch run dev_py_3_12:test ``` Sources: [Makefile:1-52](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/Makefile#L1-L52) > [!WARNING] > Running `make install_all` executes a broad `pip install` covering diverse vector database connectors and LLM libraries simultaneously, which requires compatible native compilation dependencies on the host system. Sources: [Makefile:13-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/Makefile#L13-L17) ### Subpackage Layouts and Tool Configurations Subpackages within the monorepo maintain independent `pyproject.toml` manifests with dedicated linting and testing parameters. The Strands integration package (`integrations/mem0-strands/python/pyproject.toml`) configures `mem0-strands` with required dependencies on `strands-agents>=1.45.0` and `mem0ai>=2.0.11`, while defining MyPy type-checking rules. ``` [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "mem0-strands" version = "0.1.1" description = "Persistent long-term memory for Strands agents, backed by Mem0." readme = "README.md" requires-python = ">=3.10" license = "Apache-2.0" authors = [ {name = "Mem0", email = "founders@mem0.ai"} ] keywords = ["strands", "strands-agents", "agents", "ai", "memory", "mem0", "vector-search", "personalization"] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: Scientific/Engineering :: Artificial Intelligence", ] # 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", ] [project.urls] Homepage = "https://mem0.ai" Documentation = "https://github.com/mem0ai/mem0/tree/main/integrations/mem0-strands#readme" Repository = "https://github.com/mem0ai/mem0" Issues = "https://github.com/mem0ai/mem0/issues" [project.optional-dependencies] dev = [ "pytest>=8.0.0,<9.0.0", "pytest-asyncio>=0.25.0,<1.0.0", "ruff>=0.11.0,<1.0.0", "mypy>=1.15.0,<2.0.0", "hatch", ] ``` Sources: [integrations/mem0-strands/python/pyproject.toml:1-48](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/pyproject.toml#L1-L48) Similarly, the Python CLI package (`cli/python/pyproject.toml`) declares `mem0-cli` using Typer and Rich for console interaction, defining `mem0 = "mem0_cli.app:main"` as its primary executable script entry point. ``` dependencies = [ "typer>=0.9.0", "rich>=13.0.0", "httpx>=0.24.0", ] [project.optional-dependencies] oss = ["mem0ai>=0.1.0"] dev = [ "pytest>=7.0", "pytest-asyncio>=0.21", "ruff>=0.1.0", ] [project.scripts] mem0 = "mem0_cli.app:main" ``` Sources: [cli/python/pyproject.toml:27-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/pyproject.toml#L27-L43) ## Python Core Engine Architecture ### Entry Modules and Version Resolution The core Python library is initialized via the package entry module `mem0/__init__.py`. This module dynamically resolves the package version string using `importlib.metadata.version("mem0ai")` to query the installed distribution metadata. ```python import importlib.metadata __version__ = importlib.metadata.version("mem0ai") from mem0.client.main import AsyncMemoryClient, MemoryClient # noqa from mem0.memory.main import AsyncMemory, Memory # noqa ``` Sources: [mem0/__init__.py:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/__init__.py#L1-L6) > [!NOTE] > The package version is retrieved dynamically from distribution metadata at runtime rather than being hardcoded in the source tree, ensuring that editable and bundled installations reflect the precise version declared by Hatchling. Sources: [mem0/__init__.py:1-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/__init__.py#L1-L3) ### Core Component Exports The package init module exposes primary memory engine interfaces and remote client classes for both synchronous and asynchronous execution workflows. These symbols are imported directly from their respective internal implementation modules with `# noqa` directives silencing unused import linters. | Export Symbol | Module Path | Purpose | | :--- | :--- | :--- | | `Memory` | `mem0.memory.main` | Synchronous core memory engine for storage, search, and history | | `AsyncMemory` | `mem0.memory.main` | Asynchronous core memory engine for non-blocking I/O operations | | `MemoryClient` | `mem0.client.main` | Synchronous remote platform API client | | `AsyncMemoryClient` | `mem0.client.main` | Asynchronous remote platform API client | Sources: [mem0/__init__.py:5-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/__init__.py#L5-L6) > [!IMPORTANT] > The sub-package initialization files `mem0/client/__init__.py` and `mem0/configs/__init__.py` are intentionally left blank as structural namespace containers, delegating concrete initialization to submodule imports. Sources: [mem0/client/__init__.py:1-1](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/__init__.py#L1-L1), [mem0/configs/__init__.py:1-1](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/__init__.py#L1-L1) ## TypeScript SDK and OSS Packages ### Package Manifest and Root Export Structure The TypeScript monorepo workspace under `mem0-ts` splits its architecture into two primary packages: the core `mem0ai` package and the community-contributed `@mem0/community` package. The root `mem0-ts/package.json` configures bundling automation using `tsup`, emitting CommonJS (`.js`) and ESM (`.mjs`) builds alongside bundled declaration maps while defining conditional export paths for `.` and `./oss`. Sources: [mem0-ts/package.json:1-70](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/package.json#L1-L70) The core OSS entry point `mem0-ts/src/oss/src/index.ts` re-exports the entire memory subsystem, covering core memory definitions, vector stores, embedding providers, LLM backends, and rerankers. | Export Category | Representative Modules | Sources | | :--- | :--- | :--- | | Memory & Core | `./memory`, `./memory/memory.types`, `./types`, `./utils/factory` | [mem0-ts/src/oss/src/index.ts:1-3,60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/index.ts#L1-L3,60) | | Embeddings | `./embeddings/openai`, `./embeddings/ollama`, `./embeddings/aws_bedrock`, `./embeddings/fastembed` | [mem0-ts/src/oss/src/index.ts:4-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/index.ts#L4-L15) | | LLMs | `./llms/openai`, `./llms/anthropic`, `./llms/ollama`, `./llms/litellm` | [mem0-ts/src/oss/src/index.ts:16-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/index.ts#L16-L28) | | Vector Stores | `./vector_stores/qdrant`, `./vector_stores/pinecone`, `./vector_stores/pgvector`, `./vector_stores/chromadb` | [mem0-ts/src/oss/src/index.ts:29-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/index.ts#L29-L54) | | Rerankers | `./rerankers/cohere`, `./rerankers/llm`, `./rerankers/cross_encoder` | [mem0-ts/src/oss/src/index.ts:55-59](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/index.ts#L55-L59) | Sources: [mem0-ts/src/oss/src/index.ts:1-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/index.ts#L1-L60) > [!NOTE] > The `tsup` build configuration marks `@mem0/community` as an external dependency while forcing internal inclusion of community files matching `src/community/**`, ensuring clean boundary separation between core OSS features and peripheral integrations. Sources: [mem0-ts/package.json:64-69](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/package.json#L64-L69) ### Community Package and Framework Integrations The `@mem0/community` package (`mem0-ts/src/community/package.json`) manages peripheral ecosystem integrations such as LangChain. It specifies dual export targets for the root package and the LangChain integration module, pinning required peer and runtime dependencies on `@langchain/core`, `@langchain/community`, and `mem0ai`. Sources: [mem0-ts/src/community/package.json:1-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/package.json#L1-L38) The community entry module `mem0-ts/src/community/src/index.ts` exposes framework bindings, delegating to submodule declarations like the LangChain integration entry point. ```typescript export * from "./integrations/langchain"; ``` Sources: [mem0-ts/src/community/src/index.ts:1-1](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/index.ts#L1-L1) > [!WARNING] > The community build configuration explicitly targets Node.js version 18 or greater (`"node": ">=18"`) and requires a strict public access publish configuration (`"access": "public"`). Sources: [mem0-ts/src/community/package.json:85-90](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/package.json#L85-L90) ## Dual CLI Implementations and Specifications ### Package Structure and Node.js Dependencies The Node.js CLI package residing at `cli/node/package.json` defines the official command-line interface under the package name `@mem0/cli` at version `0.2.14`. It targets Node.js version `18.0.0` or higher and configures its executable binary mapping under the `bin` field to point `./dist/index.js` to the `mem0` command. Sources: [cli/node/package.json:1-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/package.json#L1-L20) | Dependency | Version | Purpose | Sources | | :--- | :--- | :--- | :--- | | `commander` | `^12.0.0` | Command-line option parsing and routing | [cli/node/package.json:32-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/package.json#L32-L33) | | `chalk` | `^5.3.0` | Terminal string styling | [cli/node/package.json:34-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/package.json#L34-L34) | | `cli-table3` | `^0.6.4` | Tabular data formatting | [cli/node/package.json:35-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/package.json#L35-L35) | | `ora` | `^8.0.0` | Terminal spinners for asynchronous actions | [cli/node/package.json:36-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/package.json#L36-L36) | | `boxen` | `^7.1.0` | Boxed terminal banners | [cli/node/package.json:37-37](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/package.json#L37-L37) | Sources: [cli/node/package.json:32-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/package.json#L32-L38) > [!NOTE] > Development tooling in `cli/node/package.json` relies on `tsup` for bundling, `tsx` for direct execution during development, `vitest` for test runner execution, and `biome` for linting and code style checking. Sources: [cli/node/package.json:9-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/package.json#L9-L17), [cli/node/package.json:39-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/package.json#L39-L47) ### Python CLI Layout and Specifications The Python CLI package structures its commands and backend abstractions within `cli/python/src/mem0_cli/`, exposing version metadata and backend factory bindings through initializers. ```python """mem0 CLI — the command-line interface for the mem0 memory layer.""" ``` Sources: [cli/python/src/mem0_cli/__init__.py:1-2](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/__init__.py#L1-L2) The backend package initialization module `cli/python/src/mem0_cli/backend/__init__.py` imports and re-exports the `Backend` abstract base class and `get_backend` factory function from the backend subsystem. ```python """Backend abstraction layer for mem0 CLI.""" from mem0_cli.backend.base import Backend, get_backend __all__ = ["Backend", "get_backend"] ``` Sources: [cli/python/src/mem0_cli/backend/__init__.py:1-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/__init__.py#L1-L5) Command modules are organized as a submodule package namespace under `cli/python/src/mem0_cli/commands/__init__.py`. ```python """CLI command modules.""" ``` Sources: [cli/python/src/mem0_cli/commands/__init__.py:1-1](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/__init__.py#L1-L1) Unified behavior across both CLI implementations is governed by the master specification file `cli/cli-spec.json`, which defines configuration defaults, API endpoint mappings, error messages, and command options. | Configuration Field | Key / Setting | Default Value / Target | Sources | | :--- | :--- | :--- | :--- | | Config Directory | `configDir` | `~/.mem0` | [cli/cli-spec.json:35-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L35-L38) | | Config File | `configFile` | `config.json` | [cli/cli-spec.json:35-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L35-L38) | | Base URL | `defaultBaseUrl` | `https://api.mem0.ai` | [cli/cli-spec.json:39-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L39-L40) | | API Authentication | `authHeader` / `authScheme` | `Authorization` / `Token` | [cli/cli-spec.json:98-101](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L98-L101) | | Request Timeout | `timeout` | `30` seconds | [cli/cli-spec.json:98-102](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L98-L102) | Sources: [cli/cli-spec.json:35-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L35-L40), [cli/cli-spec.json:98-102](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L98-L102) > [!WARNING] > The specification mandates specific error handling mapping: an HTTP `401` status triggers `AuthError` with the message `"Authentication failed. Your API key may be invalid or expired."`, while missing API keys prompt the user to run `mem0 init` or set the `MEM0_API_KEY` environment variable. Sources: [cli/cli-spec.json:109-126](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json#L109-L126) ## Server Dashboard Application Layout ### Web Dashboard Workspace and Metadata The server web dashboard resides inside `server/dashboard/` and is managed as a private Next.js application using `pnpm` version `10.34.2`. Its project configuration specifies execution scripts for development, production builds, local serving, linting, formatting, and typechecking. | Script Name | Command | Purpose | Sources | | :--- | :--- | :--- | :--- | | `dev` | `next dev` | Start Next.js development server | [server/dashboard/package.json:6-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L6-L8) | | `build` | `next build` | Compile production application bundle | [server/dashboard/package.json:6-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L6-L8) | | `start` | `next start` | Launch production server instance | [server/dashboard/package.json:8-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L8-L10) | | `lint` | `prettier --check .` | Verify code style with Prettier | [server/dashboard/package.json:10-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L10-L11) | | `format` | `prettier --write .` | Format codebase files with Prettier | [server/dashboard/package.json:10-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L10-L12) | | `typecheck` | `tsc --noEmit` | Run TypeScript type validation without emitting files | [server/dashboard/package.json:11-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L11-L13) | Sources: [server/dashboard/package.json:6-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L6-L13) > [!NOTE] > The dashboard package enforces strict private visibility (`"private": true`) and pins Next.js to version `15.5.24` alongside React `19.0.0` within its dependency tree. Sources: [server/dashboard/package.json:4-4](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L4-L4), [server/dashboard/package.json:48-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L48-L50) ### UI Primitives and State Management Dependencies The dashboard application integrates component primitives from Radix UI alongside Redux Toolkit for state management, Tailwind CSS for styling utility, and React Hook Form with Zod validation. | Dependency Group | Key Packages | Purpose | Sources | | :--- | :--- | :--- | :--- | | Component Library | `@radix-ui/react-accordion`, `@radix-ui/react-dialog`, `@radix-ui/react-dropdown-menu`, `@radix-ui/react-tabs` | Accessible headless UI primitives | [server/dashboard/package.json:15-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L15-L17), [server/dashboard/package.json:20-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L20-L23), [server/dashboard/package.json:34-37](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L34-L37) | | State & Data | `@reduxjs/toolkit`, `react-redux`, `@tanstack/react-table`, `axios` | Global state, table rendering, and HTTP communication | [server/dashboard/package.json:38-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L38-L41), [server/dashboard/package.json:54-56](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L54-L56) | | Forms & Validation | `react-hook-form`, `@hookform/resolvers`, `zod` | Form state handling and schema validation | [server/dashboard/package.json:14-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L14-L16), [server/dashboard/package.json:54-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L54-L55), [server/dashboard/package.json:64-66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L64-L66) | | Styling & Motion | `tailwindcss`, `framer-motion`, `lucide-react`, `next-themes` | Layout utility, animations, icon sets, and dark mode support | [server/dashboard/package.json:44-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L44-L50), [server/dashboard/package.json:80-82](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L80-L82) | Sources: [server/dashboard/package.json:14-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L14-L38), [server/dashboard/package.json:44-66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L44-L66), [server/dashboard/package.json:80-82](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L80-L82) > [!WARNING] > Development tooling in `server/dashboard/package.json` relies on TypeScript `5.6.3`, Tailwind CSS Typography (`@tailwindcss/typography`), and ESLint configured with the Next.js rule set. Sources: [server/dashboard/package.json:67-69](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L67-L69), [server/dashboard/package.json:77-79](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L77-L79), [server/dashboard/package.json:81-83](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L81-L83) ## Agent Integration Plugins and Frameworks ### Ecosystem Organization and Package Structure The repository houses several ecosystem integrations and shared agent harnesses under the `integrations/` directory, spanning both Python and TypeScript runtimes. These plugins embed Mem0 functionality directly into popular coding agents, framework runtimes, and LLM orchestration layers such as OpenClaw, Pi Agent, Strands, and the Vercel AI SDK. Shared packages such as `@mem0/agent-plugin-core` provide common utility implementations across TypeScript agent harnesses. | Package Name | Language / Runtime | Manifest Path | Primary Purpose | Sources | | :--- | :--- | :--- | :--- | :--- | | `@mem0/pi-agent-plugin` | TypeScript / Node.js | `integrations/pi-agent-plugin/package.json` | Persistent, scoped, semantic memory extension for Pi Coding Agent across sessions and projects | [integrations/pi-agent-plugin/package.json:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/package.json#L1-L6) | | `@mem0/agent-plugin-core` | TypeScript / Node.js | `integrations/agent-plugin-core/typescript/package.json` | Private core utility and lifecycle management package for TypeScript agent plugins | [integrations/agent-plugin-core/typescript/package.json:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/package.json#L1-L6) | | `mem0-strands` | Python | `integrations/mem0-strands/python/pyproject.toml` | Persistent long-term memory store for Strands agents with automatic injection and server-side extraction | [integrations/mem0-strands/python/pyproject.toml:5-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/pyproject.toml#L5-L15) | | `mem0-agent-plugin` | Python | `integrations/mem0-agent-plugin/core/memory_core.py` | Shared Python core providing session hooks, extraction instructions, and secret redaction patterns | [integrations/mem0-agent-plugin/core/memory_core.py:2-88](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L2-L88) | | `openclaw-mem0` | TypeScript / Node.js | `integrations/openclaw/index.ts` | OpenClaw plugin providing dual-mode long-term memory, auto-recall, auto-capture, and 6 core tools | [integrations/openclaw/index.ts:1-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L1-L17) | | `vercel-ai-sdk` | TypeScript / Node.js | `integrations/vercel-ai-sdk/src/index.ts` | [Vercel AI SDK integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/vercel-ai-sdk-integration) facade providing provider wrappers and utility functions for memory operations | [integrations/vercel-ai-sdk/src/index.ts:1-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/index.ts#L1-L5) | Sources: [integrations/pi-agent-plugin/package.json:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/package.json#L1-L6), [integrations/agent-plugin-core/typescript/package.json:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/package.json#L1-L6), [integrations/mem0-strands/python/pyproject.toml:5-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/pyproject.toml#L5-L15), [integrations/mem0-agent-plugin/core/memory_core.py:2-88](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L2-L88), [integrations/openclaw/index.ts:1-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L1-L17), [integrations/vercel-ai-sdk/src/index.ts:1-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/index.ts#L1-L5) > [!NOTE] > The Python agent plugin core defines standardized instructions for repository and personal memory extraction alongside regular expression filters for test commands, build commands, and sensitive credential redaction. Sources: [integrations/mem0-agent-plugin/core/memory_core.py:74-88](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L74-L88), [integrations/mem0-agent-plugin/core/memory_core.py:128-161](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L128-L161) ### Plugin Runtime Exports and Capabilities Individual ecosystem integrations expose entry points, tool registrations, scoping handlers, and configuration schemas to hook into their respective agent runtimes. Pi Agent and OpenClaw packages bundle extension modules, skill loaders, and telemetry collectors, while the Vercel AI SDK wrapper exposes helper functions like `addMemories`, `retrieveMemories`, `searchMemories`, and `getMemories`. ```typescript export type { Scope, Mem0Config, ScopeContext, CustomCategory, } from "./types.ts"; export { DEFAULT_CUSTOM_CATEGORIES } from "./types.ts"; export { loadConfig, CONFIG_DIR } from "./config/index.ts"; export { registerMemoryTool, buildToolExecute } from "./memory/tools.ts"; export { detectAppId, detectRunId, resolveSearchFilters, resolveAddParams } from "./memory/scoping.ts"; export { formatAge, formatMemoryCompact, formatMemoryList, groupByCategory } from "./memory/formatting.ts"; export { setupAutoCapture, extractConversation } from "./capture/index.ts"; export { MEMORY_POLICY } from "./prompt.ts"; export { registerCommands } from "./commands.ts"; export { captureEvent, captureToolEvent, captureCommandEvent, _getEventQueue, _resetForTesting } from "./telemetry.ts"; ``` Sources: [integrations/pi-agent-plugin/src/index.ts:1-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/index.ts#L1-L22) > [!WARNING] > [Pi Agent plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/pi-agent-plugin) dependency overrides enforce specific minimum versions for security-critical packages including `form-data`, `uuid`, `esbuild`, `undici`, and `axios` within its pnpm configuration block. Sources: [integrations/pi-agent-plugin/package.json:70-83](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/package.json#L70-L83) ## Related - [Overview](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/orientation/overview) - [Quick Start](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/orientation/quick-start) --- ## Technical docs: POST Configure Mem0 URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/set-config ## Request Body ## Responses ## Try It --- ## Technical docs: Memory Lifecycle URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle
Relevant source files The following files were used as context for generating this wiki page: - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [mem0-ts/src/oss/src/prompts/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/prompts/index.ts) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [mem0/configs/prompts.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/prompts.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [server/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py) - [integrations/agent-plugin-core/typescript/src/lifecycle.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts) - [integrations/hermes-plugin-mem0/_backend.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py) - [integrations/vercel-ai-sdk/src/mem0-utils.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts) - [mem0/client/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py) - [mem0/configs/base.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/base.py) - [mem0/memory/setup.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/setup.py) - [mem0/memory/notices.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/notices.py) - [server/server_state.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/server_state.py)
## 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L1960-L2005), [mem0-ts/src/oss/src/memory/index.ts:1-82](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts#L1-L82), [mem0-ts/src/oss/src/prompts/index.ts:1-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/prompts/index.ts#L1-L43), [mem0/memory/notices.py:99-163](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/notices.py#L99-L163) ## 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. | Method | Parameters | Return Type | Purpose | | :--- | :--- | :--- | :--- | | `history` | `memory_id` | `list` | Get the history of changes for a memory by ID mem0/memory/main.py | | `_create_memory` | `data`, `existing_embeddings`, `metadata=None` | `str` | Internal helper to embed, store, and record audit history for new text mem0/memory/main.py | | `_create_procedural_memory` | `messages`, `metadata=None`, `prompt=None` | `dict` | Generate procedural memory summaries using an LLM prompt workflow mem0/memory/main.py | | `_update_memory` | `memory_id`, `data`, `existing_embeddings`, `metadata=None` | `str` | Modify existing memory text and refresh entity-store links mem0/memory/main.py | | `_delete_memory` | `memory_id`, `existing_memory=None` | `str` | Remove vector entries, log deletion audit history, and clear entity relations mem0/memory/main.py | | `reset` | None | `None` | Reset history database, close connections, and reinitialize vector store collections mem0/memory/main.py | Sources: [mem0/memory/main.py:1960-2162](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L1960-L2162) ### 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. ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/base.py#L16-L58) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L2144-L2162), [mem0/configs/base.py:42-45](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/base.py#L42-L45) ## 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/prompts.py#L63-L121) ### 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. ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L2007-L2050) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L2034-L2042) ## 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. ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L1975-L1994) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L1983-L1988) ### 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. ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L1995-L2005) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L2107-L2111) ## 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. ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L2052-L2112) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L2055-L2060) ### 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. ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L2114-L2162) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L2157-L2162) ## 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 / Constant | Value / Threshold | Meaning / 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_CAP` | `10` | Maximum temporal usage notices per window mem0/memory/notices.py | | `DECAY_USAGE_CAP` | `10` | Maximum decay usage notices per window mem0/memory/notices.py | | `DECAY_USAGE_DELETE_THRESHOLD` | `5` | Successful deletes required to trigger decay notice mem0/memory/notices.py | | `SCALE_MEMORY_COUNT_THRESHOLD` | `2000` | Vector count threshold triggering scale notices mem0/memory/notices.py | | `SCALE_TOP_K_THRESHOLD` | `50` | `top_k` query parameter threshold for scale notices mem0/memory/notices.py | | `PERFORMANCE_SLOW_QUERY_THRESHOLD_SECONDS` | `2.0` | Query execution time limit for slow query warnings mem0/memory/notices.py | ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/notices.py#L99-L127) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/notices.py#L130-L134) ### 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. ```python _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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/notices.py#L136-L150) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/notices.py#L152-L163) ## Related - [Multi-Signal Retrieval](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/multi-signal-retrieval) - [Storage and History](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/storage-and-history) - [Typescript Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/typescript-core/typescript-core) --- ## Technical docs: POST Generate custom instructions from a use case URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/generate-instructions ## Request Body ## Responses ## Try It --- ## Technical docs: POST Create memories URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/add-memory ## Request Body ## Responses ## Try It --- ## Technical docs: Multi-Signal Retrieval URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/multi-signal-retrieval
Relevant source files The following files were used as context for generating this wiki page: - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [mem0/utils/scoring.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py) - [integrations/openclaw/recall.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [mem0-ts/src/oss/src/utils/scoring.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/scoring.ts) - [mem0/proxy/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/proxy/main.py) - [mem0-ts/src/oss/src/vector_stores/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/memory.ts) - [mem0-ts/src/oss/src/vector_stores/qdrant.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/qdrant.ts) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [mem0-ts/src/oss/src/utils/bm25.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/bm25.ts) - [mem0/vector_stores/valkey.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/valkey.py) - [mem0/reranker/huggingface_reranker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/huggingface_reranker.py) - [mem0/vector_stores/vertex_ai_vector_search.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/vertex_ai_vector_search.py) - [mem0/vector_stores/redis.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/redis.py) - [mem0/configs/prompts.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/prompts.py) - [mem0/vector_stores/milvus.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/milvus.py) - [mem0-ts/src/oss/src/vector_stores/milvus.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts) - [mem0/reranker/llm_reranker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/llm_reranker.py) - [mem0/reranker/zero_entropy_reranker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/zero_entropy_reranker.py) - [mem0/utils/lemmatization.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/lemmatization.py)
## Overview Multi-Signal Retrieval combines vector similarity, BM25 keyword matching, entity boosting, and threshold filtering to surface relevant memories. By integrating multiple scoring dimensions, the retrieval engine resolves semantic nuance that single-pass vector search often misses. Sources: [mem0/memory/main.py:1648-1701](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L1648-L1701), [mem0/utils/scoring.py:60-139](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L60-L139) ## Hybrid Search Architecture ### BM25 Parameter Selection and Score Normalization Query-length-adaptive sigmoid parameters are retrieved via `get_bm25_params`, which adjusts midpoint and steepness based on the term count of the lemmatized query to account for higher raw BM25 score distributions in longer inputs. ```python def get_bm25_params(query: str, *, lemmatized: Optional[str] = None) -> tuple: """Get BM25 sigmoid parameters based on query length. Longer queries tend to have higher raw BM25 scores, so we adjust the sigmoid midpoint and steepness accordingly. Returns: (midpoint, steepness) for sigmoid normalization. """ if lemmatized is None: from mem0.utils.lemmatization import lemmatize_for_bm25 lemmatized = lemmatize_for_bm25(query) num_terms = len(lemmatized.split()) if lemmatized else 1 if num_terms <= 3: return 5.0, 0.7 elif num_terms <= 6: return 7.0, 0.6 elif num_terms <= 9: return 9.0, 0.5 elif num_terms <= 15: return 10.0, 0.5 else: return 12.0, 0.5 ``` Sources: [mem0/utils/scoring.py:16-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L16-L41) Raw unbounded BM25 scores are converted to a normalized range of `[0, 1]` via a logistic sigmoid function using `normalize_bm25`. ```python def normalize_bm25(raw_score: float, midpoint: float, steepness: float) -> float: """Normalize BM25 score to [0, 1] using logistic sigmoid. Args: raw_score: Raw BM25 score (unbounded, typically 0-20+). midpoint: Score at which sigmoid outputs 0.5. steepness: Controls how quickly sigmoid transitions. Returns: Normalized score in range [0, 1]. """ return 1.0 / (1.0 + math.exp(-steepness * (raw_score - midpoint))) ``` Sources: [mem0/utils/scoring.py:43-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L43-L55) ### Additive Scoring and Threshold Gates Candidate memories are ranked and combined in `score_and_rank`. The threshold parameter gates the semantic score prior to combining signals, excluding candidates below the threshold even if lexical or entity boosts would otherwise raise them. ```python def score_and_rank( semantic_results: List[Dict[str, Any]], bm25_scores: Dict[str, float], entity_boosts: Dict[str, float], threshold: float, top_k: int, explain: bool = False, ) -> List[Dict[str, Any]]: """Score candidates additively and return top-k results. For each candidate: semantic_score is taken from the result's score field. combined = (semantic + bm25 + entity_boost) / max_possible Threshold gates the semantic score BEFORE combining -- candidates below the threshold are excluded even if BM25/entity would boost them. The divisor adapts based on which signals are active: - Semantic only: max_possible = 1.0 - Semantic + BM25: max_possible = 2.0 - Semantic + BM25 + entity: max_possible = 2.5 - Semantic + entity (no BM25): max_possible = 1.5 Args: semantic_results: Candidate memories from vector search. bm25_scores: Normalized keyword scores keyed by memory ID. entity_boosts: Entity-link boosts keyed by memory ID. threshold: Minimum semantic score required before hybrid scoring. top_k: Maximum number of results to return. explain: Include score_details in each result when true. Returns: List of scored result dicts sorted by combined score descending. """ has_bm25 = bool(bm25_scores) has_entity = bool(entity_boosts) max_possible = 1.0 if has_bm25: max_possible += 1.0 if has_entity: max_possible += ENTITY_BOOST_WEIGHT ``` Sources: [mem0/utils/scoring.py:60-102](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L60-L102) The divisor (`max_possible`) adapts dynamically depending on which active signals contribute to the score calculation: | Active Signals | Max Possible Divisor (`max_possible`) | | :--- | :--- | | Semantic only | `1.0` | | Semantic + BM25 | `2.0` | | Semantic + entity (no BM25) | `1.5` | | Semantic + BM25 + entity | `2.5` | Sources: [mem0/utils/scoring.py:77-101](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L77-L101) ### Vector Store Delegation and Chat Retrieval Vector search backends such as `GoogleMatchingEngine` handle similarity searches by embedding the query vector and executing `search`, subsequently wrapping payloads into `Document` and score pairs. ```python def similarity_search_with_score( self, query: str, k: int = 5, filter: Optional[Dict] = None, ) -> List[Tuple[Document, float]]: """Return documents most similar to query with scores.""" logger.debug("Starting similarity search with score") logger.debug("Query: %s", query) logger.debug("k: %d", k) logger.debug("Filter: %s", filter) embedding = self.embedder.embed_query(query) results = self.search(query=query, vectors=embedding, top_k=k, filters=filter) docs_and_scores = [ (Document(page_content=result.payload.get("text", ""), metadata=result.payload), result.score) for result in results ] logger.debug("Found %d results", len(docs_and_scores)) return docs_and_scores ``` Sources: [mem0/vector_stores/vertex_ai_vector_search.py:609-630](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/vertex_ai_vector_search.py#L609-L630) > [!NOTE] > `GoogleMatchingEngine` explicitly does not support index reset operations, logging a warning and returning immediately when `reset` is called. ```python def reset(self): """ Reset the Google Matching Engine index. """ logger.warning("Reset operation is not supported for Google Matching Engine") pass ``` Sources: [mem0/vector_stores/vertex_ai_vector_search.py:642-647](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/vertex_ai_vector_search.py#L642-L647) Chat proxy completions utilize `_fetch_relevant_memories` to pass the last 6 message turns from the conversation history into the client's `search` API. ```python def _fetch_relevant_memories(self, messages, user_id, agent_id, run_id, filters, top_k): # Currently, only pass the last 6 messages to the search API to prevent long query message_input = [f"{message['role']}: {message['content']}" for message in messages][-6:] # TODO: Make it better by summarizing the past conversation return self.mem0_client.search( query="\n".join(message_input), user_id=user_id, agent_id=agent_id, run_id=run_id, filters=filters, top_k=top_k, ) ``` Sources: [mem0/proxy/main.py:163-174](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/proxy/main.py#L163-L174) ## Lexical Lemmatization Pipeline ### spaCy-Based Text Normalization Query strings and payload texts are processed by `lemmatize_for_bm25` in `mem0/utils/lemmatization.py` to standardize token forms for BM25 matching. The pipeline loads a spaCy model via `get_nlp_lemma` and converts the input text to lowercase before tokenization. ```python def lemmatize_for_bm25(text: str) -> str: """Lemmatize text for BM25 matching. Returns space-joined lemmas for full-text search. Falls back to the original text if spaCy is unavailable. """ from mem0.utils.spacy_models import get_nlp_lemma nlp = get_nlp_lemma() if nlp is None: return text doc = nlp(text.lower()) ``` Sources: [mem0/utils/lemmatization.py:22-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/lemmatization.py#L22-L34) Tokens identified as punctuation (`token.is_punct`) or stopwords (`token.is_stop`) are filtered out. For remaining tokens, the lemmatizer extracts `token.lemma_` and verifies that it is alphanumeric via `lemma.isalnum()`. > [!NOTE] > If the spaCy lemma model returns `None`, `lemmatize_for_bm25` skips tokenization entirely and returns the raw input text unmodified. ```python if nlp is None: return text ``` Sources: [mem0/utils/lemmatization.py:31-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/lemmatization.py#L31-L32) ### Ambiguity Handling for -ing Forms To prevent context-dependent lemmatization discrepancies (such as treating "meeting" as a noun versus a verb), the pipeline inspects the raw token text. If `token.text` ends with `"ing"`, differs from the computed lemma, and is alphanumeric, the original surface form is appended to the token list alongside its lemma. ```python # This handles noun/verb ambiguity (meeting/meet, attending/attend). if token.text.endswith("ing") and token.text != lemma and token.text.isalnum(): tokens.append(token.text) ``` Sources: [mem0/utils/lemmatization.py:46-48](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/lemmatization.py#L46-L48) The resulting filtered tokens are joined with single spaces and returned as a unified string for full-text keyword indexing and search evaluation. ```python return " ".join(tokens) ``` Sources: [mem0/utils/lemmatization.py:50-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/lemmatization.py#L50-L50) ## BM25 Scoring and Normalization ### Query-Length-Adaptive Sigmoid Parameters Raw BM25 scores are unbounded and typically range from zero past twenty depending on corpus characteristics and term frequency distributions. To scale these lexical values into a standardized range, Mem0 applies a logistic sigmoid normalization function. Because longer search queries inherently accumulate higher raw term frequency scores, the engine adapts its normalization midpoint and steepness dynamically based on the term count of the lemmatized query string. ```python def get_bm25_params(query: str, *, lemmatized: Optional[str] = None) -> tuple: """Get BM25 sigmoid parameters based on query length. Longer queries tend to have higher raw BM25 scores, so we adjust the sigmoid midpoint and steepness accordingly. Returns: (midpoint, steepness) for sigmoid normalization. """ if lemmatized is None: from mem0.utils.lemmatization import lemmatize_for_bm25 lemmatized = lemmatize_for_bm25(query) num_terms = len(lemmatized.split()) if lemmatized else 1 if num_terms <= 3: return 5.0, 0.7 elif num_terms <= 6: return 7.0, 0.6 elif num_terms <= 9: return 9.0, 0.5 elif num_terms <= 15: return 10.0, 0.5 else: return 12.0, 0.5 ``` Sources: [mem0/utils/scoring.py:16-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L16-L41) The query term count determines the sigmoid configuration thresholds according to specific length boundaries: | Query Term Count (`num_terms`) | Midpoint | Steepness | | :--- | :--- | :--- | | `1` to `3` | `5.0` | `0.7` | | `4` to `6` | `7.0` | `0.6` | | `7` to `9` | `9.0` | `0.5` | | `10` to `15` | `10.0` | `0.5` | | Greater than `15` | `12.0` | `0.5` | Sources: [mem0/utils/scoring.py:31-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L31-L40) > [!NOTE] > If the optional `lemmatized` query parameter is omitted when calling `get_bm25_params`, the function invokes `lemmatize_for_bm25` directly on the raw query string to compute term lengths accurately. ```python if lemmatized is None: from mem0.utils.lemmatization import lemmatize_for_bm25 lemmatized = lemmatize_for_bm25(query) ``` Sources: [mem0/utils/scoring.py:25-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L25-L28) ### Logistic Sigmoid Transformation Once the midpoint and steepness parameters are resolved for a given query, `normalize_bm25` transforms the raw score into a bounded interval between zero and one using the logistic function formula. ```python def normalize_bm25(raw_score: float, midpoint: float, steepness: float) -> float: """Normalize BM25 score to [0, 1] using logistic sigmoid. Args: raw_score: Raw BM25 score (unbounded, typically 0-20+). midpoint: Score at which sigmoid outputs 0.5. steepness: Controls how quickly sigmoid transitions. Returns: Normalized score in range [0, 1]. """ return 1.0 / (1.0 + math.exp(-steepness * (raw_score - midpoint))) ``` Sources: [mem0/utils/scoring.py:43-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L43-L54) ```python def normalize_bm25(raw_score: float, midpoint: float, steepness: float) -> float: return 1.0 / (1.0 + math.exp(-steepness * (raw_score - midpoint))) ``` Sources: [mem0/utils/scoring.py:43-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L43-L54) ## Entity Boosting Mechanisms ### Entity Weight Configuration and Additive Scoring Candidate memories passing the semantic threshold can be reinforced through entity-linking boosts keyed by memory identifier strings. The scoring engine defines a constant weight modifier for entity associations, setting `ENTITY_BOOST_WEIGHT` to `0.5` to calibrate how strongly matched entity links influence the final aggregated score alongside semantic and BM25 signals. ```python ENTITY_BOOST_WEIGHT = 0.5 ``` Sources: [mem0/utils/scoring.py:57-57](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L57-L57) During the `score_and_rank` execution, the engine dynamically determines the normalization divisor (`max_possible`) based on which active signals are present in the current retrieval pass. This divisor ensures that combined scores remain bounded correctly between zero and one regardless of whether lexical or entity signals are enabled. | Active Signals | Divisor (`max_possible`) Formula | Maximum Possible Score | | :--- | :--- | :--- | | Semantic only | `1.0` | `1.0` | | Semantic + BM25 | `1.0 + 1.0` | `2.0` | | Semantic + Entity | `1.0 + ENTITY_BOOST_WEIGHT` | `1.5` | | Semantic + BM25 + Entity | `1.0 + 1.0 + ENTITY_BOOST_WEIGHT` | `2.5` | Sources: [mem0/utils/scoring.py:77-82](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L77-L82), [mem0/utils/scoring.py:97-102](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L97-L102) > [!WARNING] > The threshold check is applied strictly to the vector search `semantic_score` *before* hybrid combining takes place. Candidates falling below the configured threshold are discarded immediately, preventing BM25 or entity boosts from rescuing low-similarity memories. ```python Threshold gates the semantic score BEFORE combining -- candidates below the threshold are excluded even if BM25/entity would boost them. ``` Sources: [mem0/utils/scoring.py:74-75](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L74-L75) ### Explanation and Score Auditing When the `explain` flag is enabled on `score_and_rank`, each returned result dictionary includes a `score_details` sub-dictionary that breaks down individual component contributions. This audit dictionary exposes the exact intermediate values computed during entity boosting and normalization. ```python if explain: scored_result["score_details"] = { "semantic_score": semantic_score, "bm25_score": bm25_score, "entity_boost": entity_boost, "raw_score": raw_combined, "max_possible_score": max_possible, "final_score": combined, "threshold": threshold, } ``` Sources: [mem0/utils/scoring.py:126-135](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L126-L135) ```python scored_result["score_details"] = { "semantic_score": semantic_score, "bm25_score": bm25_score, "entity_boost": entity_boost, "raw_score": raw_combined, "max_possible_score": max_possible, "final_score": combined, "threshold": threshold, } ``` Sources: [mem0/utils/scoring.py:127-134](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L127-L134) > [!TIP] > The final combined score is capped at `1.0` using `min(raw_combined / max_possible, 1.0)` to guarantee that score distributions do not violate upper bounds when multiple signals peak simultaneously. ```python raw_combined = semantic_score + bm25_score + entity_boost combined = min(raw_combined / max_possible, 1.0) ``` Sources: [mem0/utils/scoring.py:118-119](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L118-L119) ## Temporal Decay and Recency Weighting ### Additive Hybrid Scoring Components The hybrid scoring architecture in `mem0/utils/scoring.py` combines semantic similarity scores from vector search with normalized BM25 keyword matching scores and entity-linking boosts. Each candidate memory's identifier is checked against active signal dictionaries to aggregate raw component values before normalization. ```python mem_id_str = str(mem_id) bm25_score = bm25_scores.get(mem_id_str, 0.0) entity_boost = entity_boosts.get(mem_id_str, 0.0) raw_combined = semantic_score + bm25_score + entity_boost ``` Sources: [mem0/utils/scoring.py:114-118](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L114-L118) > [!NOTE] > Candidate memory entries lacking an `id` field or failing to meet the minimum semantic `threshold` are excluded from score aggregation entirely, regardless of their auxiliary keyword or entity boost values. ```python mem_id = result.get("id") if mem_id is None: continue semantic_score = result.get("score") or 0.0 if semantic_score < threshold: continue ``` Sources: [mem0/utils/scoring.py:106-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L106-L112) ### Score Auditing and Explanation Details When `explain=True` is passed to `score_and_rank`, the resulting dictionary contains a comprehensive breakdown of every individual contribution and scaling factor used during score computation. ```python if explain: scored_result["score_details"] = { "semantic_score": semantic_score, "bm25_score": bm25_score, "entity_boost": entity_boost, "raw_score": raw_combined, "max_possible_score": max_possible, "final_score": combined, "threshold": threshold, } ``` Sources: [mem0/utils/scoring.py:126-135](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L126-L135) ## Multi-Signal Rank Fusion ### Final Score Aggregation and Ranking The `score_and_rank` function aggregates the individual components by summing the semantic similarity score, the normalized BM25 keyword score, and the entity boost. The raw combined sum is divided by the dynamic `max_possible` denominator and clamped to an upper bound of `1.0`. ```python raw_combined = semantic_score + bm25_score + entity_boost combined = min(raw_combined / max_possible, 1.0) ``` Sources: [mem0/utils/scoring.py:118-119](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L118-L119) Once all valid candidates are processed, the collection of scored result dictionaries is sorted in descending order by their final normalized score, and truncated to the requested `top_k` limit. ```python scored.sort(key=lambda x: x["score"], reverse=True) return scored[:top_k] ``` Sources: [mem0/utils/scoring.py:138-139](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L138-L139) > [!CAUTION] > Candidates that do not contain an `id` attribute are skipped silently during iteration, and those falling below the configured similarity `threshold` are filtered out before combining occurs. ```python mem_id = result.get("id") if mem_id is None: continue semantic_score = result.get("score") or 0.0 if semantic_score < threshold: continue ``` Sources: [mem0/utils/scoring.py:106-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py#L106-L112) ## Related - [Memory Lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle) - [Reranking Models](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/reranking-models) --- ## Technical docs: Storage and History URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/storage-and-history
Relevant source files The following files were used as context for generating this wiki page: - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [mem0/memory/storage.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [mem0-ts/src/oss/src/storage/SQLiteManager.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/SQLiteManager.ts) - [mem0-ts/src/oss/src/storage/base.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/base.ts) - [mem0-ts/src/oss/src/storage/MemoryHistoryManager.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/MemoryHistoryManager.ts) - [mem0-ts/src/oss/src/storage/SupabaseHistoryManager.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/SupabaseHistoryManager.ts) - [mem0/memory/utils.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/utils.py)
## Overview The persistence and audit layer manages local relational storage across the Python core, TypeScript OSS package, and agent plugins. It initializes SQLite databases, enforces history and message table schemas, and provides structured transaction tracking for memory versioning and conversational audits. Sources: [mem0/memory/storage.py:11-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L11-L19), [mem0-ts/src/oss/src/storage/SQLiteManager.ts:6-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/SQLiteManager.ts#L6-L15) By recording state mutation logs, before-and-after values, and session scopes, the storage engine supports transactional rollback, incremental schema migrations, and reliable agent checkpointing across distributed plugin environments. Sources: [mem0/memory/storage.py:20-101](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L20-L101), [integrations/agent-plugin-core/python/memory_core.py:699-714](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L699-L714) ## SQLite Storage Architecture and Initialization ### Connection Setup and Thread Isolation The `SQLiteManager` class initializes a SQLite connection using the database path specified by `db_path`, which defaults to `":memory:"` if omitted. To support multi-threaded environments and asynchronous workers across client runtimes, `check_same_thread=False` is passed to `sqlite3.connect()`. A thread lock (`threading.Lock`) protects all transaction boundaries, schema operations, and query executions. Sources: [mem0/memory/storage.py:11-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L11-L19) > [!IMPORTANT] > When `check_same_thread=False` is enabled, concurrent access from multiple threads must be serialized explicitly. `SQLiteManager` wraps every state change, table creation, and migration step inside `self._lock` to prevent race conditions during transaction commits. Sources: [mem0/memory/storage.py:14-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L14-L19) ### Schema Generation and Table Definitions Upon instantiation, `SQLiteManager` invokes schema checks and table creation methods to guarantee that the required relational structures exist. It runs `_migrate_history_table()`, `_create_history_table()`, and `_create_messages_table()`, each wrapped in explicit `BEGIN`, `COMMIT`, and `ROLLBACK` blocks to ensure atomicity. Sources: [mem0/memory/storage.py:16-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L16-L18) | Table Name | Column Name | Data Type | Key Constraints / Purpose | | :--- | :--- | :--- | :--- | | `history` | `id` | TEXT | Primary Key, UUID string identifying the history event | | `history` | `memory_id` | TEXT | Identifier linking the mutation to a specific memory record | | `history` | `old_memory` | TEXT | Previous state value before an update or deletion | | `history` | `new_memory` | TEXT | Updated state value after a mutation | | `history` | `event` | TEXT | Category or type of mutation event | | `history` | `created_at` | DATETIME | Timestamp when the record was initially created | | `history` | `updated_at` | DATETIME | Timestamp when the record was last modified | | `history` | `is_deleted` | INTEGER | Deletion status flag (0 for active, 1 for deleted) | | `history` | `actor_id` | TEXT | Identifier of the entity or agent triggering the action | | `history` | `role` | TEXT | Role associated with the event or actor | | `messages` | `id` | TEXT | Primary Key, UUID string identifying the message | | `messages` | `session_scope` | TEXT | Scoping key grouping conversation turns by session | | `messages` | `role` | TEXT | Sender role (e.g., user, assistant, system) | | `messages` | `content` | TEXT | Raw textual body of the conversation turn | | `messages` | `name` | TEXT | Optional display name or handle for the message author | | `messages` | `created_at` | DATETIME | ISO timestamp recorded when the message was stored | Sources: [mem0/memory/storage.py:68-79](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L68-L79), [mem0/memory/storage.py:134-141](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L134-L141) ## History Mutation Records and Versioning ### Logging State Mutations and Before-and-After Values The `SQLiteManager` class records state transitions via `add_history` and `batch_add_history` methods. Each mutation captures the state prior to modification in `old_memory` and the subsequent state in `new_memory`, tied together by a unique `memory_id` and tracked under an `event` category. Transaction execution is safeguarded by an explicit thread lock, rolling back changes on any encountered exception. ```python def add_history( self, memory_id: str, old_memory: Optional[str], new_memory: Optional[str], event: str, *, created_at: Optional[str] = None, updated_at: Optional[str] = None, is_deleted: int = 0, actor_id: Optional[str] = None, role: Optional[str] = None, ) -> None: with self._lock: try: self.connection.execute("BEGIN") self.connection.execute( """ INSERT INTO history ( id, memory_id, old_memory, new_memory, event, created_at, updated_at, is_deleted, actor_id, role ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( str(uuid.uuid4()), memory_id, old_memory, new_memory, event, created_at, updated_at, is_deleted, actor_id, role, ), ) self.connection.execute("COMMIT") except Exception as e: self.connection.execute("ROLLBACK") logger.error(f"Failed to add history record: {e}") raise def batch_add_history(self, records: List[Dict[str, Any]]) -> None: with self._lock: try: self.connection.execute("BEGIN") self.connection.executemany( """ INSERT INTO history ( id, memory_id, old_memory, new_memory, event, created_at, updated_at, is_deleted, actor_id, role ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, [ ( str(uuid.uuid4()), record.get("memory_id"), record.get("old_memory"), record.get("new_memory"), record.get("event"), record.get("created_at"), record.get("updated_at"), record.get("is_deleted", 0), record.get("actor_id"), record.get("role"), ) for record in records ], ) self.connection.execute("COMMIT") except Exception as e: self.connection.execute("ROLLBACK") logger.error(f"Failed to batch add history records: {e}") raise ``` Sources: [mem0/memory/storage.py:150-226](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L150-L226) > [!NOTE] > When batch inserting mutation records through `batch_add_history`, each dictionary entry defaults `is_deleted` to `0` if not explicitly supplied, while optional fields such as `actor_id`, `role`, `created_at`, and `updated_at` are safely retrieved using `.get()`. Sources: [mem0/memory/storage.py:193-220](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L193-L220) ### History Retrieval Methods Version history for any tracked memory entry can be queried using the `get_history` method, which fetches all associated mutation records filtered by `memory_id`. The returned rows are ordered chronologically by their creation timestamp and updated datetime values, then mapped into dictionaries with boolean conversion applied to the `is_deleted` status flag. ```python def get_history(self, memory_id: str) -> List[Dict[str, Any]]: with self._lock: cur = self.connection.execute( """ SELECT id, memory_id, old_memory, new_memory, event, created_at, updated_at, is_deleted, actor_id, role FROM history WHERE memory_id = ? ORDER BY created_at ASC, DATETIME(updated_at) ASC """, (memory_id,), ) rows = cur.fetchall() return [ { "id": r[0], "memory_id": r[1], "old_memory": r[2], "new_memory": r[3], "event": r[4], "created_at": r[5], "updated_at": r[6], "is_deleted": bool(r[7]), "actor_id": r[8], "role": r[9], } for r in rows ] ``` Sources: [mem0/memory/storage.py:227-255](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L227-L255) > [!IMPORTANT] > The sort order in `get_history` relies on `ORDER BY created_at ASC, DATETIME(updated_at) ASC` to preserve the exact linear trajectory of edits performed on a given memory item over its lifecycle. Sources: [mem0/memory/storage.py:229-236](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L229-L236) ## Session Message Audit and Staging ### Storing Conversation Turns and Pruning Prerequisite Messages The `SQLiteManager` class records raw conversation turns in the `messages` table via the `save_messages` method. When messages are ingested under a given `session_scope`, each item is assigned a generated UUID, its respective role, content, and optional author name, and an ISO-formatted UTC timestamp. ```python def save_messages(self, messages: List[Dict[str, Any]], session_scope: str) -> None: if not messages: return with self._lock: try: self.connection.execute("BEGIN") now = datetime.now(timezone.utc).isoformat() for message in messages: self.connection.execute( """ INSERT INTO messages (id, session_scope, role, content, name, created_at) VALUES (?, ?, ?, ?, ?, ?) """, ( str(uuid.uuid4()), session_scope, message.get("role"), message.get("content"), message.get("name"), now, ), ) ``` Sources: [mem0/memory/storage.py:257-278](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L257-L278) > [!WARNING] > Immediately after saving new rows, `save_messages` executes an automated retention pruning query that deletes any message for that `session_scope` exceeding the ten most recent entries, determined by descending `created_at` ordering. Sources: [mem0/memory/storage.py:279-291](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L279-L291) > [!TIP] > The subquery within the pruning delete statement wraps the `LIMIT 10` selection inside a derived table. This forces SQLite to materialize the ordered temporary result set before evaluating the outer `NOT IN` clause against the table. Sources: [mem0/memory/storage.py:282-289](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L282-L289) ### Retrieving Bounded History and Session Contexts Recent conversation turns are queried using the `get_last_messages` method, which accepts a `session_scope` identifier and an optional `limit` parameter defaulting to 10. The underlying SQL query fetches the latest records in descending order of creation, then wraps them in a subquery to re-sort the results chronologically in ascending order before returning them to the caller. ```python def get_last_messages(self, session_scope: str, limit: int = 10) -> List[Dict[str, Any]]: with self._lock: # Subquery picks the latest N rows (DESC + LIMIT), outer query # re-sorts them chronologically (ASC) for the caller. cur = self.connection.execute( """ SELECT role, content, name, created_at FROM ( SELECT role, content, name, created_at FROM messages WHERE session_scope = ? ORDER BY created_at DESC LIMIT ? ) ORDER BY created_at ASC """, (session_scope, limit), ) rows = cur.fetchall() return [ { "role": r[0], "content": r[1], "name": r[2], "created_at": r[3], } for r in rows ] ``` Sources: [mem0/memory/storage.py:298-324](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L298-L324) ## Schema Migrations and Table Evolution ### History Table Schema Validation and Transformation The `SQLiteManager` class implements an automated migration routine inside `_migrate_history_table` to handle evolution of the `history` table schema across storage revisions. When initialized, the manager inspects SQLite metadata tables to determine whether a pre-existing `history` table exists. If present, it queries column information via `PRAGMA table_info(history)` to evaluate whether the existing column set matches the expected production schema. ```python cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='history'") if cur.fetchone() is None: self.connection.execute("COMMIT") return # nothing to migrate cur.execute("PRAGMA table_info(history)") old_cols = {row[1] for row in cur.fetchall()} expected_cols = { "id", "memory_id", "old_memory", "new_memory", "event", "created_at", "updated_at", "is_deleted", "actor_id", "role", } if old_cols == expected_cols: self.connection.execute("COMMIT") return ``` Sources: [mem0/memory/storage.py:32-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L32-L55) If column discrepancies are detected—such as legacy group-chat metadata columns—the migration procedure executes a safe table alteration wrapped inside an explicit transaction block. It drops any leftover staging tables from previous interrupted migrations, renames the active `history` table to `history_old`, provisions a new `history` table conforming to the current schema definition, copies intersecting columns, and drops the old table. ```python logger.info("Migrating history table to new schema (no convo columns).") # Clean up any existing history_old table from previous failed migration cur.execute("DROP TABLE IF EXISTS history_old") # Rename the current history table cur.execute("ALTER TABLE history RENAME TO history_old") # Create the new history table with updated schema cur.execute( """ CREATE TABLE history ( id TEXT PRIMARY KEY, memory_id TEXT, old_memory TEXT, new_memory TEXT, event TEXT, created_at DATETIME, updated_at DATETIME, is_deleted INTEGER, actor_id TEXT, role TEXT ) """ ) # Copy data from old table to new table intersecting = list(expected_cols & old_cols) if intersecting: cols_csv = ", ".join(intersecting) cur.execute(f"INSERT INTO history ({cols_csv}) SELECT {cols_csv} FROM history_old") # Drop the old table cur.execute("DROP TABLE history_old") # Commit the transaction self.connection.execute("COMMIT") logger.info("History table migration completed successfully.") ``` Sources: [mem0/memory/storage.py:57-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L57-L94) > [!WARNING] > If any exception occurs during the schema transformation, the transaction block immediately invokes `ROLLBACK` and propagates the error, preventing partial table migrations or data corruption. Sources: [mem0/memory/storage.py:96-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L96-L100) ### Expected Schema Definition The history migration and table initialization logic enforces a strict ten-column schema structure. The table attributes, data types, and constraints used during `_migrate_history_table` and `_create_history_table` are outlined below. | Column Name | Data Type | Constraint / Role | | :--- | :--- | :--- | | `id` | `TEXT` | Primary Key | | `memory_id` | `TEXT` | References target memory entry | | `old_memory` | `TEXT` | Previous state value | | `new_memory` | `TEXT` | Updated state value | | `event` | `TEXT` | Mutation category or event type | | `created_at` | `DATETIME` | Creation timestamp | | `updated_at` | `DATETIME` | Modification timestamp | | `is_deleted` | `INTEGER` | Deletion flag status | | `actor_id` | `TEXT` | Identifier of performing actor | | `role` | `TEXT` | Role associated with the record | Sources: [mem0/memory/storage.py:40-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L40-L51), [mem0/memory/storage.py:68-79](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L68-L79), [mem0/memory/storage.py:108-119](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L108-L119) ## Agent Checkpoint and Flush Isolation ### Tracking Inflight Flushes and Uncommitted Event IDs The plugin memory cores manage synchronization through explicit database schema cleanups and state tracking. During database initialization or cleanup sequences, legacy or deprecated staging tables—specifically `sidekick_calls`, `sidekick_snapshots`, `sidekick_state`, and `sidekick_packets`—are unconditionally dropped from the SQLite connection to prevent schema pollution across revisions. ```python DROP TABLE IF EXISTS sidekick_calls; DROP TABLE IF EXISTS sidekick_snapshots; DROP TABLE IF EXISTS sidekick_state; DROP TABLE IF EXISTS sidekick_packets; ``` Sources: [integrations/mem0-agent-plugin/core/memory_core.py:693-696](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L693-L696) To handle ongoing persistence tasks, the `prepare_flush` method utilizes an immediate transaction (`BEGIN IMMEDIATE`) to inspect pending memory flush packets for a given repository and session. It queries the `flushes` table for rows where the status is outside terminal states, enforcing retry thresholds. ```python def prepare_flush( self, repo: RepoContext, session_id: str, reason: str ) -> tuple[str, list[dict[str, Any]]] | None: with self.conn: self.conn.execute("BEGIN IMMEDIATE") existing = self.conn.execute( """SELECT * FROM flushes WHERE repo_id = ? AND session_id = ? AND status NOT IN ('semantic-succeeded', 'explicitly-stored', 'gave-up') ORDER BY created_at LIMIT 1""", (repo.identity, session_id), ).fetchone() ``` Sources: [integrations/mem0-agent-plugin/core/memory_core.py:797-808](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L797-L808) > [!CAUTION] > If a flush packet exceeds `MAX_FLUSH_ATTEMPTS` (fixed at 5), the engine transitions its status to `gave-up` and records a telemetry event, preventing infinite retry loops on failing synchronization payloads. Sources: [integrations/mem0-agent-plugin/core/memory_core.py:65-70](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L65-L70), [integrations/mem0-agent-plugin/core/memory_core.py:809-816](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L809-L816) ### Subagent State Markers and Repository Scopes Subagent and sidekick instances are tracked via the `sidekick_runs` table, capturing runtime execution lifecycle events. The `stop_subagent` method manages concurrent subagent termination by wrapping operations in an immediate transaction block, resolving missing agent identifiers, and upserting execution records with transcript paths and final messages. ```python def stop_subagent( self, repo: RepoContext, session_id: str, agent_id: str, agent_type: str, transcript_path: str, final_message: str, ) -> str: now = utc_now() self.conn.execute("BEGIN IMMEDIATE") ``` Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1068-1078](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1068-L1078) ```python self.conn.execute( """INSERT INTO sidekick_runs (repo_id, session_id, agent_id, agent_type, started_at, stopped_at, transcript_path, final_message) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(repo_id, session_id, agent_id) DO UPDATE SET stopped_at = excluded.stopped_at, transcript_path = excluded.transcript_path, final_message = excluded.final_message""", ( repo.identity, session_id, agent_id, agent_type, now, now, bounded(transcript_path, 2000), redact(final_message).strip(), ), ) ``` Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1089-1108](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1089-L1108) > [!NOTE] > When an `agent_id` is omitted during subagent stoppage, the store queries for active records without a stop timestamp (`stopped_at IS NULL`). If exactly one overlapping run exists, it correlates automatically; otherwise, it assigns a unique fallback identifier using nanosecond timestamps. Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1080-1088](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1080-L1088) Project context isolation is maintained by the `repo_for_session` method and the `session_scopes` table. Working directories and repository identities are pinned per session ID to guarantee that subsequent hooks within the same coding agent session reference consistent git metadata and root paths. | Table Name | Target Column | Purpose | | :--- | :--- | :--- | | `events` | `repo_id` | Stores incremental prompt, tool, and session event payloads | | `session_scopes` | `repo_id` | Maintains persistent working directory and git root bindings per session | | `flushes` | `repo_id` | Tracks batch synchronization attempts, payload identifiers, and error statuses | | `retrievals` | `repo_id` | Logs context retrieval scores, ranks, and injected memory snippets | | `operations` | `repo_id` | Records execution duration, success flags, and error traces for agent tools | | `sidekick_runs` | `repo_id` | Markers for native subagent execution lifecycles, transcripts, and final messages | Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1209-1216](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1209-L1216) ## Related - [Memory Lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle) - [Database Vector Stores](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/database-vector-stores) --- ## Technical docs: GET Get memories URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/get-all-memories ## Parameters ## Responses ## Try It --- ## Technical docs: Entity Extraction URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/entity-extraction
Relevant source files The following files were used as context for generating this wiki page: - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [mem0/utils/entity_extraction.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py) - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [mem0-ts/src/oss/src/utils/entity_extraction.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/entity_extraction.ts) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [mem0-ts/src/oss/src/prompts/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/prompts/index.ts) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [mem0/configs/prompts.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/prompts.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [mem0/memory/utils.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/utils.py) - [mem0/utils/spacy_models.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/spacy_models.py)
## Overview Entity Extraction powers the automated parsing and linking of structured knowledge units from unstructured conversation text and agent interaction episodes. By combining natural language processing models, regex-based heuristic filtering, and LLM-driven fact prompting, the module isolates significant proper nouns, quoted strings, technical identifiers, and multi-word topic phrases while discarding irrelevant filler. Sources: [mem0/utils/entity_extraction.py:1-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L1-L16) This subsystem bridges raw conversational streams with semantic vector storage by deduplicating candidate spans, resolving overlapping entity boundaries, and attaching normalized records to persistent memory IDs across user and agent scopes. Sources: [mem0/memory/main.py:1100-1115](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L1100-L1115) ## Natural Language Model Loading ### Shared Model Initialization The engine consolidates natural language processing initialization through mem0/utils/spacy_models.py, which shares model instances between entity extraction and BM25 text processing. Two distinct pipeline configurations are maintained globally: `_nlp_full` for full pipeline tasks including named entity recognition and dependency parsing, and `_nlp_lemma` with disabled components for lightweight lemmatization. Sources: [mem0/utils/spacy_models.py:1-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/spacy_models.py#L1-L15) Thread safety during model acquisition is enforced using a module-level `threading.Lock()`. If model loading fails once, subsequent calls short-circuit by returning `None` immediately via `_load_failed_full` and `_load_failed_lemma` flags without re-entering the locked block. Sources: [mem0/utils/spacy_models.py:16-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/spacy_models.py#L16-L19), [mem0/utils/spacy_models.py:44-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/spacy_models.py#L44-L55), [mem0/utils/spacy_models.py:69-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/spacy_models.py#L69-L80) > [!NOTE] > The spaCy model `en_core_web_sm` is automatically acquired via `spacy.cli.download` if `spacy.util.is_package` detects it missing, throwing an `ImportError` if spaCy itself is absent or a `RuntimeError` if download fails. > Sources: [mem0/utils/spacy_models.py:21-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/spacy_models.py#L21-L42) ### Pipeline Configuration Reference | Function | Pipeline Components | Disabled Components | Target Use Case | | :--- | :--- | :--- | :--- | | `get_nlp_full()` | Default full pipeline | None | Entity extraction (`_extract_entities_from_doc`) | | `get_nlp_lemma()` | Tokenizer, tagger, morpher, lemmatizer | `["ner", "parser"]` | BM25 text processing and lemma lookups | Sources: [mem0/utils/spacy_models.py:44-66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/spacy_models.py#L44-L66), [mem0/utils/spacy_models.py:69-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/spacy_models.py#L69-L91) ### Batch Processing and Fallbacks Entity extraction workflows consume the shared loader directly within `extract_entities` and `extract_entities_batch`. When processing multiple texts, `extract_entities_batch` leverages spaCy's `nlp.pipe` iterator with a configurable `batch_size` default of 32 items. If the spaCy package is missing or model loading encounters an unrecoverable exception, both functions return empty lists (`[]` or `[[] for _ in texts]`) to let higher-level extraction pipelines proceed gracefully without crashing. Sources: [mem0/utils/entity_extraction.py:751-772](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L751-L772), [mem0/utils/spacy_models.py:56-66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/spacy_models.py#L56-L66), [mem0/utils/spacy_models.py:81-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/spacy_models.py#L81-L91) > [!CAUTION] > Disabling components like `ner` and `parser` via `disable=["ner", "parser"]` in `get_nlp_lemma()` makes that instance unsuitable for dependency parsing or named entity recognition; attempting to extract entity candidates with it will yield empty results. > Sources: [mem0/utils/spacy_models.py:69-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/spacy_models.py#L69-L91) ## Candidate Entity Extraction Pipeline ### Entity Extraction Phases and Candidate Types The entity extraction engine parses a processed spaCy document through five specialized candidate generators. Each generator appends typed candidates into a shared `_EntityCandidate` dataclass collection, tracking priority, source type, character offsets, and confidence metrics before resolution. ```mermaid flowchart TD Doc["spaCy Doc / Tokens"] --> NER["_add_ner_candidates"] Doc --> Tech["_add_technical_identifier_candidates"] Doc --> Prop["_add_proper_name_candidates"] Doc --> Quote["_add_quoted_candidates"] Doc --> Topic["_add_topic_phrase_candidates"] NER --> Resolve["_resolve_candidates"] Tech --> Resolve Prop --> Resolve Quote --> Resolve Topic --> Resolve ``` Sources: [mem0/utils/entity_extraction.py:24-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L24-L33), [mem0/utils/entity_extraction.py:731-748](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L731-L748) The engine yields four distinct entity types categorized during parsing: `PROPER`, `QUOTED`, `TOPIC`, and `IDENTIFIER`. Sources: [mem0/utils/entity_extraction.py:13-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L13-L15) ### Recognized Entity Labels and Filters Named Entity Recognition (NER) candidate extraction relies on explicit lists of accepted and rejected spaCy entity labels. Accepted labels cover standard named entities while excluding numerical, temporal, and monetary categories. | Label Category | Included Entity Labels | | :--- | :--- | | `_ACCEPTED_NER_LABELS` | `PERSON`, `ORG`, `GPE`, `LOC`, `FAC`, `PRODUCT`, `WORK_OF_ART`, `EVENT`, `NORP`, `LAW`, `LANGUAGE` | | `_REJECTED_NER_LABELS` | `DATE`, `TIME`, `CARDINAL`, `ORDINAL`, `QUANTITY`, `MONEY`, `PERCENT` | Sources: [mem0/utils/entity_extraction.py:98-122](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L98-L122) > [!NOTE] > Technical identifiers are matched using a regular expression pattern requiring a letter or underscore, followed by word characters or hyphens, separated by at least one dot component (`[A-Za-z_][\w-]*(?:\.[A-Za-z_][\w-]*)+`). > Sources: [mem0/utils/entity_extraction.py:404-406](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L404-L406) ### Filtering Terminology and Formatting Markers To prevent generic terms from polluting memory records, the pipeline consults multiple suppression sets. Single-word proper names, noun compound heads, and trailing words are filtered against predefined exclusion lists. | Suppression Set | Sample Entries | Purpose | | :--- | :--- | :--- | | `_GENERIC_HEADS` | `thing`, `stuff`, `way`, `time`, `experience`, `situation`, `case`, `fact` | Prevents generic nouns from serving as entity heads | | `_GENERIC_SINGLE_ENTITY_TERMS` | `user`, `assistant`, `agent`, `customer`, `client`, `memory`, `chat` | Rejects common role words and single-token pseudo-entities | | `_CIRCUMSTANTIAL_MODS` | `solo`, `individual`, `team`, `group`, `joint`, `first`, `last`, `main` | Filters modifiers describing circumstance rather than content | | `_GENERIC_ENDINGS` | `work`, `job`, `task`, `info`, `data`, `content`, `results`, `item` | Strips generic trailing words from compound token sequences | | `_FORMATTING_MARKERS` | `*`, `-`, `+`, `•`, `–`, `—`, `#`, `##`, `###`, `**`, `__` | Markdown and formatting symbols skipped during extraction | Sources: [mem0/utils/entity_extraction.py:35-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L35-L96), [mem0/utils/entity_extraction.py:124-162](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L124-L162), [mem0/utils/entity_extraction.py:283-317](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L283-L317), [mem0/utils/entity_extraction.py:353](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L353) ## Candidate Filtering and Heuristic Resolution ### Conflict Resolution and Deduplication Flow The engine resolves overlapping and duplicate entity candidates inside the `_resolve_candidates` routine. Candidates are first normalized by their text representation and deduplicated by selecting the entry with the highest priority and confidence score. Surviving candidates are then sorted and scanned for overlapping character spans, filtering out redundant matches unless a specific multi-word topic spans across a proper noun boundary. ```mermaid flowchart TD Candidates["Raw Candidates List"] --> Dedup["Normalize Text & Deduplicate by Priority/Confidence"] Dedup --> Sort["Sort by (priority, -confidence, -length, start)"] Sort --> Iter["Iterate Sorted Candidates"] Iter --> Check{"Spans Overlap & Not (TOPIC with space & PROPER)?"} Check -- Yes --> Skip["Drop Candidate"] Check -- No --> Accept["Append to Accepted List"] Accept --> FinalSort["Sort Final List by Start Offset & Priority"] FinalSort --> Output["Return (entity_type, text) Tuples"] ``` Sources: [mem0/utils/entity_extraction.py:708-728](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L708-L728) > [!NOTE] > During span overlap resolution, multi-word topic phrases (`TOPIC` with spaces) are permitted to coexist with overlapping proper noun entities (`PROPER`), whereas other overlapping span conflicts drop the lower-priority candidate. > Sources: [mem0/utils/entity_extraction.py:719-724](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L719-L724) ### Sorting and Acceptance Criteria Sorting keys applied during candidate resolution combine four distinct prioritization attributes. The sorting tuple `(c.priority, -c.confidence, -(c.end - c.start), c.start)` ensures that higher-priority, higher-confidence, and longer span matches take precedence during the linear overlap check. Accepted candidates are subsequently resorted by their character start positions before output tuple generation. ```python source mem0/utils/entity_extraction.py:713-727 ``` Sources: [mem0/utils/entity_extraction.py:713-727](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L713-L727) > [!WARNING] > Candidates with negative or unassigned start offsets default to index `10**9` during final position sorting, placing them at the end of the extracted sequence. > Sources: [mem0/utils/entity_extraction.py:727](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/entity_extraction.py#L727) ## Prompt Orchestration for Memory Extraction ### Additive Extraction Prompt Architecture The TypeScript OSS package orchestrates prompt generation for memory extraction through the `generateAdditiveExtractionPrompt` builder and static prompt definitions in `mem0-ts/src/oss/src/prompts/index.ts`. The orchestration framework coordinates context sections including conversation summaries, past messages, existing memories, new conversational input, and temporal anchor points. ```mermaid flowchart TD InputOptions["Options: existingMemories, newMessages, lastKMessages, dates"] --> DateSetup["Compute Current & Observation Dates"] DateSetup --> PushSummary["Push ## Summary"] PushSummary --> PushLastK["Push ## Last k Messages (Truncated)"] PushLastK --> PushRecent["Push ## Recently Extracted Memories ([] )"] PushRecent --> PushExisting["Push ## Existing Memories (JSON)"] PushExisting --> PushNew["Push ## New Messages"] PushNew --> PushDates["Push ## Observation Date & Current Date"] PushDates --> CheckCustom{"Custom Instructions Provided?"} CheckCustom -- Yes --> PushCustom["Push ## Custom Instructions"] CheckCustom -- No --> PushOutput["Push # Output:"] PushCustom --> PushOutput PushOutput --> FinalPrompt["Return Joined Prompt String"] ``` Sources: [mem0-ts/src/oss/src/prompts/index.ts:822-863](https://github.com/blade47/mem0-ts/blob/HEAD/src/oss/src/prompts/index.ts#L822-L863) > [!NOTE] > The builder function `generateAdditiveExtractionPrompt` defaults both `currentDate` and `observationDate` to today's ISO date string (`YYYY-MM-DD`) derived from `new Date().toISOString().split("T")[0]` if they are not explicitly supplied in the options parameter. > Sources: [mem0-ts/src/oss/src/prompts/index.ts:830-832](https://github.com/blade47/mem0-ts/blob/HEAD/src/oss/src/prompts/index.ts#L830-L832) ### Conversation History Serialization and Truncation Prior message arrays are serialized via `formatConversationHistory`, which iterates through message objects, extracts role and content attributes, and truncates individual messages exceeding `PAST_MESSAGE_TRUNCATION_LIMIT` characters. ```typescript source mem0-ts/src/oss/src/prompts/index.ts:791-820 ``` Sources: [mem0-ts/src/oss/src/prompts/index.ts:791-820](https://github.com/blade47/mem0-ts/blob/HEAD/src/oss/src/prompts/index.ts#L791-L820) > [!WARNING] > The truncation constant `PAST_MESSAGE_TRUNCATION_LIMIT` is hardcoded to `300` characters; any past message content longer than 300 characters is sliced and appended with an ellipsis (`...`) before being formatted into the `## Last k Messages` section. > Sources: [mem0-ts/src/oss/src/prompts/index.ts:791-799](https://github.com/blade47/mem0-ts/blob/HEAD/src/oss/src/prompts/index.ts#L791-L799) ## Agent Session Episode Assembly ### Episode Structure and Semantic Evidence Coding session episodes are assembled into structured text content that incorporates task metadata, file modification lists, file read lists, observed tool outputs, and repository provenance identifiers. The episode assembly routine formats tasks, paths, commands, searches, conclusions, and outcomes into a bounded string format. Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1619-1654](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1619-L1654) Semantic evidence extraction processes modified paths and observed commands from the structured session data. Test and build results are deliberately filtered out of semantic evidence so that transient verification details do not steer memory extraction away from core repository knowledge. Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1657-1673](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1657-L1673) | Component | Content Filter | Bound Limit | Purpose | | :--- | :--- | :--- | :--- | | `task` | Raw string | None | Identifies the objective of the session | | `modified_paths` | Sliced to first 50 items | 500 chars per path | Records changed files in the repository | | `read_paths` | Sliced to first 50 items | None | Records inspected code files | | `commands` | Sliced to last 30 items, status check | None | Captures observed execution commands | | `searches` | Sliced to last 20 items, JSON dump | None | Captures query patterns and file searches | | `conclusion` | Raw string | None | Summarizes the final state of the task | | `outcome` | Raw string | None | Records success or failure description | Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1620-1654](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1620-L1654) > [!NOTE] > Command logs within `build_semantic_evidence` are filtered to retain only entries where the command status equals `failed`, ensuring routine passing test executions are pruned from persistent semantic memory. > Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1667-1669](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1667-L1669) ### Message Batching and Token Budget Enforcement Session extraction messages are filtered to retain only `user` and `assistant` roles with non-empty content, after which semantic evidence is appended to the final assistant message or added as a new assistant record. Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1688-1704](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1688-L1704) Tokens are conservatively estimated via `_estimated_tokens`, which computes an ASCII-weighted byte and character length ratio without introducing a heavy tokenizer dependency. Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1707-1710](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1707-L1710) ```mermaid flowchart TD Start[Receive Messages] --> CheckBudget{Token count <= max_tokens?} CheckBudget -- Yes --> ReturnSingle[Return [messages]] CheckBudget -- No --> SplitExchanges[Group into user-led exchanges] SplitExchanges --> CheckUnits{Exchange tokens <= max_tokens?} CheckUnits -- Yes --> AddUnit[Append Exchange Unit] CheckUnits -- No --> SplitSubagents[Keep Subagent Assignment & Response paired] SplitSubagents --> CheckMessageSize{Message tokens <= max_tokens?} CheckMessageSize -- Yes --> AddBounded[Append Bounded Message] CheckMessageSize -- No --> SliceMessage[Binary search slice message content] SliceMessage --> AddBounded AddBounded --> FormBatches[Accumulate batches within token budget] FormBatches --> ReturnBatches[Return Batched Message Lists] ``` Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1735-1798](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1735-L1798) > [!WARNING] > Subagent interactions identified by `_is_agent_assignment` and `_is_agent_response` are explicitly kept paired together during token budget splitting to prevent splitting context across extraction batch boundaries. > Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1717-1727, 1756-1762](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1717-L1727) ## Related - [Memory Lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle) - [Multi-Signal Retrieval](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/multi-signal-retrieval) --- ## Technical docs: GET Get a memory URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/get-memory ## Parameters ## Responses ## Try It --- ## Technical docs: Python Client SDK URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/python-client-sdk
Relevant source files The following files were used as context for generating this wiki page: - [mem0/client/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [mem0-ts/src/client/mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/mem0.ts) - [integrations/mem0-strands/python/src/mem0_strands/client.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py) - [cli/python/src/mem0_cli/app.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py) - [integrations/antigravity-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_cli.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/agent-plugin-core/python/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py) - [mem0/client/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/__init__.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [cli/python/src/mem0_cli/backend/platform.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/platform.py) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [cli/python/src/mem0_cli/__main__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/__main__.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [mem0/client/project.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/project.py) - [server/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py) - [mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/__init__.py) - [integrations/mem0-strands/python/src/mem0_strands/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/__init__.py) - [integrations/agent-plugin-core/python/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py) - [integrations/hermes-plugin-mem0/_backend.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py) - [integrations/antigravity-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py) - [mem0-ts/src/community/src/integrations/langchain/mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts) - [cli/python/src/mem0_cli/commands/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/__init__.py) - [cli/python/src/mem0_cli/backend/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/__init__.py) - [cli/python/src/mem0_cli/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/__init__.py) - [mem0/client/types.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/types.py)
## Overview The Python Client SDK (`MemoryClient` and `AsyncMemoryClient`) provides a structured interface for communicating with the Mem0 Platform API, handling authentication, request routing, telemetry instrumentation, and error translation. It supports synchronous and asynchronous CRUD workflows, batch operations, and search filters through Pydantic request models. Sources: [mem0/client/main.py:1-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py#L1-L32), [mem0/__init__.py:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/__init__.py#L1-L6), [mem0/client/types.py:1-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/types.py#L1-L12) ## Client Initialization and Configuration ### Client Setup and Header Management The Mem0 Python client SDK provides public client classes (`MemoryClient` and `AsyncMemoryClient`) designed to interface with the Mem0 Platform API. During initialization, the client configures host routing, validates credentials, and constructs custom HTTP headers for request tracing. The header composition mechanism relies on a set-once and append-only contract to preserve caller identity across wrapped execution layers. Headers such as `X-Mem0-Source` and `X-Application` follow a set-once rule where outer layers retain their values, whereas `X-Mem0-Client` accumulates client stack entries up to bounded character and entry limits. ```python MAX_STACK_ENTRIES = 4 MAX_STACK_CHARS = 200 ``` Sources: [mem0/client/main.py:152-153](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py#L152-L153) > [!NOTE] > The `_bounded_stack` function limits client trace entries by dropping whole entries rather than slicing strings in the middle, ensuring that partial identifiers do not corrupt client stack parsing. Sources: [mem0/client/main.py:156-175](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py#L156-L175) ### Configuration and Telemetry Constants Client initialization automatically invokes setup routines and reads runtime variables to govern telemetry behavior and identifier aliasing. The following table outlines core configuration parameters and routing paths defined in the client module. | Constant Name | Value / Type | Purpose | | :--- | :--- | :--- | | `ENTITY_PARAMS` | `frozenset` (`"user_id"`, `"agent_id"`, `"app_id"`, `"run_id"`) | Entity parameters that must be passed via filters rather than top-level arguments. | | `PROFILE_JOBS_PATH` | `"/v2/profiles/jobs/"` | API endpoint path for profile generation jobs. | | `PROFILE_SETTINGS_PATH` | `"/v2/profiles/settings/"` | API endpoint path for profile settings synchronization. | | `MAX_STACK_ENTRIES` | `4` | Maximum number of entries permitted in the `X-Mem0-Client` tracking stack. | | `MAX_STACK_CHARS` | `200` | Maximum character length allocated for the `X-Mem0-Client` header string. | Sources: [mem0/client/main.py:41-45](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py#L41-L45), [mem0/client/main.py:152-153](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py#L152-L153) > [!WARNING] > Sending `schema` or `custom_instructions` flat in profile settings payloads is rejected by the API with an `Unsupported settings` error; they must be nested under entities. Sources: [mem0/client/main.py:51-78](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py#L51-L78) ## Remote Memory Operations ### CRUD Workflows and Pydantic Request Models The client SDK supports comprehensive synchronous and asynchronous CRUD workflows for managing memory records through Pydantic request models defined in `mem0/client/types.py`. Operations such as `add`, `search`, `get_all`, `delete_all`, and `update` accept structured option classes (`AddMemoryOptions`, `SearchMemoryOptions`, `GetAllMemoryOptions`, `DeleteAllMemoryOptions`, and `UpdateMemoryOptions`) alongside arbitrary keyword arguments to ensure runtime validation and IDE autocompletion. > [!IMPORTANT] > Identity parameters such as `user_id`, `agent_id`, `app_id`, and `run_id` must be encapsulated within the `filters` dictionary across remote memory operations. Transmitting them at the top level is rejected by the API. Sources: [mem0/client/types.py:6-99](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/types.py#L6-L99) ### Memory Option Parameters Reference The following table details the primary Pydantic options models and parameters used across remote memory operations to constrain, filter, and structure payloads. | Model Name | Parameter Field | Type | Description | | :--- | :--- | :--- | :--- | | `AddMemoryOptions` | `filters` | `Optional[Dict[str, Any]]` | Filters containing entity IDs (e.g. `{'user_id': '...'}`). | | `AddMemoryOptions` | `infer` | `Optional[bool]` | Whether to infer memories from the input. | | `SearchMemoryOptions` | `top_k` | `Optional[int]` | Number of results to return during search retrieval. | | `SearchMemoryOptions` | `threshold` | `Optional[float]` | Minimum similarity score threshold for search results. | | `GetAllMemoryOptions` | `page` | `Optional[int]` | Page number for paginated retrieval. | | `GetAllMemoryOptions` | `page_size` | `Optional[int]` | Number of items per page for retrieval. | | `UpdateMemoryOptions` | `text` | `Optional[str]` | New text content for updating an existing memory. | | `UpdateMemoryOptions` | `expiration_date` | `Optional[str]` | Expiration date in `YYYY-MM-DD` format, or `None` to clear. | Sources: [mem0/client/types.py:15-107](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/types.py#L15-L107) ### Query Validation and Payload Serialization Prior to executing remote requests, search queries undergo strict validation and trimming via helper functions in `mem0/client/main.py`. The `_validate_and_trim_search_query` function checks that incoming queries are non-empty strings and strips leading or trailing whitespace, raising a `ValueError` if validation fails. Path segments are encoded safely using `_encode_path_segment`. ```python def _validate_and_trim_search_query(query: str) -> str: if not isinstance(query, str): raise ValueError("Invalid query: must be a non-empty string.") trimmed = query.strip() if not trimmed: raise ValueError("Invalid query: cannot be empty or whitespace-only.") return trimmed def _encode_path_segment(value: Any) -> str: return quote(str(value), safe="") ``` Sources: [mem0/client/main.py:81-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py#L81-L91) > [!CAUTION] > Passing non-string or whitespace-only query strings to search operations triggers an immediate `ValueError` before any HTTP request is dispatched to the backend platform. Sources: [mem0/client/main.py:81-87](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py#L81-L87) ## Project and Organization Scoping ### Project Configuration and Base Interfaces Multi-tenant project bindings within the Python client SDK are governed by the `ProjectConfig` Pydantic model and the `BaseProject` abstract base class defined in `mem0/client/project.py`. `ProjectConfig` handles configuration validation for project management operations with `extra="forbid"` and assignment validation enabled, encapsulating organization identifiers, project identifiers, and user emails. ```python class ProjectConfig(BaseModel): """ Configuration for project management operations. """ org_id: Optional[str] = Field(default=None, description="Organization ID") project_id: Optional[str] = Field(default=None, description="Project ID") user_email: Optional[str] = Field(default=None, description="User email") model_config = ConfigDict(validate_assignment=True, extra="forbid") class BaseProject(ABC): """ Abstract base class for project management operations. """ def __init__( self, client: Any, config: Optional[ProjectConfig] = None, org_id: Optional[str] = None, project_id: Optional[str] = None, user_email: Optional[str] = None, ): ``` Sources: [mem0/client/project.py:16-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/project.py#L16-L40) > [!WARNING] > Initializing `ProjectConfig` with unrecognized extra fields triggers an immediate validation error because `extra="forbid"` is strictly enforced on the configuration model. Sources: [mem0/client/project.py:16-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/project.py#L16-L26) ### Project Configuration Fields Reference The following table summarizes the configuration fields and types accepted by `ProjectConfig` for project and organization scoping. | Field Name | Type | Default | Description | | :--- | :--- | :--- | :--- | | `org_id` | `Optional[str]` | `None` | Organization ID for multi-tenant isolation. | | `project_id` | `Optional[str]` | `None` | Project ID for scoping memory records. | | `user_email` | `Optional[str]` | `None` | User email associated with the project scope. | Sources: [mem0/client/project.py:16-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/project.py#L16-L26) ## Client-Side Request Models and Typing ### Pydantic Request Parameter Structures Type enforcement and runtime validation across SDK request endpoints are implemented through Pydantic base models defined in `mem0/client/types.py`. These models structure payloads for memory addition, retrieval, search, updating, and project configuration adjustments, offering IDE autocompletion and type safety while supporting `**kwargs` fallback for backward compatibility. > [!NOTE] > Identity fields including `user_id`, `agent_id`, `app_id`, and `run_id` must be encapsulated within the `filters` dictionary parameter rather than passed at the top level of request options, complying with v2 and v3 API specifications. Sources: [mem0/client/types.py:6-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/types.py#L6-L8) ### Request Options Reference The following table details the Pydantic request models, parameter fields, types, and descriptions implemented for client-side type enforcement across SDK methods. | Model Name | Parameter Field | Type | Description | | :--- | :--- | :--- | :--- | | `AddMemoryOptions` | `filters` | `Optional[Dict[str, Any]]` | Filters containing entity IDs (e.g. `{'user_id': '...'}`). | | `AddMemoryOptions` | `metadata` | `Optional[Dict[str, Any]]` | Additional metadata for the memory. | | `AddMemoryOptions` | `infer` | `Optional[bool]` | Whether to infer memories from the input. | | `AddMemoryOptions` | `custom_categories` | `Optional[List[Dict[str, Any]]]` | Custom categories for memory classification. | | `AddMemoryOptions` | `custom_instructions` | `Optional[str]` | Custom instructions for fact extraction. | | `AddMemoryOptions` | `timestamp` | `Optional[int]` | Unix timestamp for the memory. | | `AddMemoryOptions` | `expiration_date` | `Optional[str]` | Expiration date in `YYYY-MM-DD` format. | | `SearchMemoryOptions` | `top_k` | `Optional[int]` | Number of results to return during search. | | `SearchMemoryOptions` | `rerank` | `Optional[bool]` | Whether to rerank search results. | | `SearchMemoryOptions` | `threshold` | `Optional[float]` | Minimum similarity score threshold. | | `SearchMemoryOptions` | `show_expired` | `Optional[bool]` | Whether to include expired memories. | | `SearchMemoryOptions` | `latest_only` | `Optional[bool]` | Whether to only return the latest memory version. | | `GetAllMemoryOptions` | `page` | `Optional[int]` | Page number for pagination. | | `GetAllMemoryOptions` | `page_size` | `Optional[int]` | Number of items per page. | | `GetAllMemoryOptions` | `start_date` | `Optional[str]` | Filter memories created on or after this date (ISO 8601). | | `UpdateMemoryOptions` | `text` | `Optional[str]` | New text content for the memory. | | `UpdateMemoryOptions` | `timestamp` | `Optional[Union[int, float, str]]` | Updated timestamp value. | | `ProjectUpdateOptions` | `memory_depth` | `Optional[str]` | Memory depth configuration setting. | | `ProjectUpdateOptions` | `multilingual` | `Optional[bool]` | Whether to enable multilingual support. | Sources: [mem0/client/types.py:15-121](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/types.py#L15-L121) ## Error Handling and Telemetry Capture ### Exception Wrappers and Status Translation SDK request execution relies on API error handlers and status translators to catch HTTP failures and convert responses into structured Python exceptions. When requests encounter validation errors, payload rejections, or server faults, the underlying client utility decorators parse response status codes and bodies to raise descriptive client-side exceptions. ```python def _validate_and_trim_search_query(query: str) -> str: if not isinstance(query, str): raise ValueError("Invalid query: must be a non-empty string.") trimmed = query.strip() if not trimmed: raise ValueError("Invalid query: cannot be empty or whitespace-only.") return trimmed ``` ```python def _validate_and_trim_search_query(query: str) -> str: if not isinstance(query, str): raise ValueError("Invalid query: must be a non-empty string.") trimmed = query.strip() if not trimmed: raise ValueError("Invalid query: cannot be empty or whitespace-only.") return trimmed ``` Sources: [mem0/client/main.py:81-88](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py#L81-L88) ### Telemetry Instrumentation and User Alias Tracking Client instrumentation monitors operational events, version properties, and user identity mapping through PostHog telemetry hooks. The client captures anonymous IDs and maps them to authenticated email addresses when user sessions are initialized, operating in a best-effort, failure-tolerant mode that prevents telemetry errors from disrupting core memory operations. ```python def _maybe_alias_anon_to_email(user_email): """Fire $identify per prior anon ID so PostHog merges them into email. Idempotent via telemetry.aliased_pairs: only writes markers when telemetry is actually enabled, so disabling/re-enabling MEM0_TELEMETRY still works. Best-effort: never raises. """ if client_telemetry.posthog is None: return if not user_email or "@" not in user_email: return try: anon_ids = read_anon_ids() seen = set() for anon_id in (anon_ids.get("oss"), anon_ids.get("cli")): if not anon_id or anon_id == user_email or anon_id in seen: continue seen.add(anon_id) if is_aliased(anon_id, user_email): continue if client_telemetry.capture_identify(anon_id, user_email): mark_aliased(anon_id, user_email) except Exception as e: logger.debug("Failed to alias anon telemetry to %r: %s", user_email, e) ``` ```python def _maybe_alias_anon_to_email(user_email): """Fire $identify per prior anon ID so PostHog merges them into email. Idempotent via telemetry.aliased_pairs: only writes markers when telemetry is actually enabled, so disabling/re-enabling MEM0_TELEMETRY still works. Best-effort: never raises. """ if client_telemetry.posthog is None: return if not user_email or "@" not in user_email: return try: anon_ids = read_anon_ids() seen = set() for anon_id in (anon_ids.get("oss"), anon_ids.get("cli")): if not anon_id or anon_id == user_email or anon_id in seen: continue seen.add(anon_id) if is_aliased(anon_id, user_email): continue if client_telemetry.capture_identify(anon_id, user_email): mark_aliased(anon_id, user_email) except Exception as e: logger.debug("Failed to alias anon telemetry to %r: %s", user_email, e) ``` Sources: [mem0/client/main.py:94-118](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py#L94-L118) > [!TIP] > Telemetry aliasing relies on `telemetry.aliased_pairs` tracking markers to maintain idempotency across sessions, ensuring `$identify` events are dispatched exactly once per unique anonymous identifier without duplicating tracking overhead. Sources: [mem0/client/main.py:94-98](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py#L94-L98) ## Related - [FastAPI Application](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application) - [Typescript Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/typescript-core/typescript-core) --- ## Technical docs: POST Search memories URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/search-memories ## Request Body ## Responses ## Try It --- ## Technical docs: PUT Update a memory URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/update-memory ## Parameters ## Request Body ## Responses ## Try It --- ## Technical docs: Typescript Core URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/typescript-core/typescript-core
Relevant source files The following files were used as context for generating this wiki page: - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [mem0-ts/src/oss/src/prompts/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/prompts/index.ts) - [mem0-ts/src/community/src/integrations/langchain/mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts) - [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts) - [integrations/openclaw/providers.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/providers.ts) - [integrations/agent-plugin-core/typescript/src/lifecycle.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [integrations/pi-agent-plugin/src/memory/tools.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts) - [integrations/openclaw/recall.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts) - [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts) - [mem0/proxy/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/proxy/main.py) - [integrations/vercel-ai-sdk/src/mem0-utils.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts) - [mem0-ts/src/client/mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/mem0.ts) - [integrations/openclaw/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/backend/platform.ts) - [mem0-ts/src/client/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/config.ts) - [mem0-ts/src/client/telemetry.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/telemetry.ts) - [mem0-ts/src/client/utils.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/utils.ts) - [mem0-ts/src/oss/src/config/defaults.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/config/defaults.ts) - [mem0-ts/src/oss/src/config/manager.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/config/manager.ts) - [mem0-ts/src/oss/src/memory/memory.types.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/memory.types.ts) - [mem0-ts/src/oss/src/storage/base.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/base.ts) - [mem0-ts/src/oss/src/storage/DummyHistoryManager.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/DummyHistoryManager.ts) - [mem0-ts/src/oss/src/storage/MemoryHistoryManager.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/MemoryHistoryManager.ts) - [mem0-ts/src/oss/src/storage/SQLiteManager.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/SQLiteManager.ts) - [mem0-ts/src/oss/src/storage/SupabaseHistoryManager.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/SupabaseHistoryManager.ts) - [mem0-ts/src/oss/src/utils/bm25.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/bm25.ts) - [mem0-ts/src/oss/src/utils/entity_extraction.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/entity_extraction.ts) - [mem0-ts/src/oss/src/utils/expiration.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/expiration.ts)
## Overview The TypeScript OSS package provides the fundamental architecture for local and self-hosted memory management within the Mem0 ecosystem. At its center stands the core `Memory` class, which orchestrates semantic embedding models, Large Language Model (LLM) fact extraction pipelines, vector stores, and relational audit logging. Rather than relying entirely on remote hosted endpoints, the TypeScript core implements a standalone execution engine capable of parsing conversation turns, deduping facts via content hashes, linking related memories through graph references, and enforcing expiration policies. Sources: [mem0-ts/src/oss/src/memory/index.ts:205-211](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts#L205-L211) This subsystem solves the core problem of extracting persistent, contextually rich facts from volatile multi-turn dialogues and maintaining them across agent sessions. By decoupling storage adapters—such as in-memory maps, SQLite databases via `better-sqlite3`, and Supabase managers—from the memory coordination logic, the engine provides pluggable durability layers. Adjacent components like agent plugins, workflow nodes, and proxy models leverage these APIs to inject dynamic user and agent state into active prompt contexts. Sources: [mem0-ts/src/oss/src/storage/SQLiteManager.ts:1-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/SQLiteManager.ts#L1-L15) ```mermaid flowchart TD A["Incoming Messages / Query"] --> B["Memory Class orchestration"] B --> C["ConfigManager & Factory"] C --> D["Embedder (OpenAI / FastEmbed)"] C --> E["LLM Extraction (V3 Additive Pipeline)"] C --> F["Vector Store Backend"] C --> G["History Manager (SQLite / Supabase)"] E --> H["JSON Extraction & Deduplication"] H --> F H --> G ``` Sources: [mem0-ts/src/oss/src/memory/index.ts:1-211](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts#L1-L211), [mem0-ts/src/oss/src/config/manager.ts:1-222](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/config/manager.ts#L1-L222) --- ## TypeScript Client SDK The TypeScript Client SDK acts as the remote interface layer for communicating with hosted Mem0 platform endpoints or self-hosted HTTP proxy instances. It encapsulates network requests, payload transformations, and client-side telemetry tracking. When interacting with remote services, the client automatically translates user-facing camelCase options into snake_case API payloads while protecting opaque records like `metadata` and `customCategories` from unwanted key mutations. Sources: [mem0-ts/src/client/mem0.ts:395-413](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/mem0.ts#L395-L413) ```typescript const client = new MemoryClient({ apiKey: "your-api-key" }); const searchResults = await client.search("recent project decisions", { filters: { userId: "user123" }, topK: 5, }); ``` Sources: [mem0-ts/src/client/mem0.ts:495-521](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/mem0.ts#L495-L521), [mem0-ts/src/client/utils.ts:28-58](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/utils.ts#L28-L58) The client exposes explicit methods for lifecycle management, including `add()`, `update()`, `get()`, `getAll()`, `search()`, `delete()`, and `deleteAll()`. To prevent structural errors, top-level entity parameters such as `user_id`, `agent_id`, and `run_id` are rejected if passed directly to search or list methods; instead, callers must nest them inside a `filters` object. Telemetry events are dispatched asynchronously via PostHog-compatible node fetch wrappers using anonymous distinct IDs initialized from local configuration files stored in `~/.mem0/config.json`. Sources: [mem0-ts/src/client/mem0.ts:470-556](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/mem0.ts#L470-L556), [mem0-ts/src/client/config.ts:1-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/config.ts#L1-L96) | Method | HTTP Action | Endpoint Path | Purpose | | :--- | :--- | :--- | :--- | | `add` | `POST` | `/v3/memories/add/` | Ingests new messages and triggers extraction | | `update` | `PUT` | `/v1/memories/{memoryId}/` | Modifies existing memory text or metadata | | `get` | `GET` | `/v1/memories/{memoryId}/` | Retrieves a single memory item by identifier | | `getAll` | `POST` | `/v3/memories/` | Paginated retrieval under specified filters | | `search` | `POST` | `/v3/memories/search/` | Semantic and keyword vector store query | | `delete` | `DELETE` | `/v1/memories/{memoryId}/` | Removes a specific memory record | | `deleteAll` | `DELETE` | `/v1/memories/` | Purges all memories matching entity scopes | Sources: [mem0-ts/src/client/mem0.ts:395-556](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/mem0.ts#L395-L556), [mem0-ts/src/client/config.ts:1-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/config.ts#L1-L96) --- ## TypeScript Core Engine The core engine revolves around the `Memory` class, which manages configuration state, component factories, and vector operations. Initialization begins through `ConfigManager.mergeConfig()`, which merges user-supplied overrides with `DEFAULT_MEMORY_CONFIG`, normalizing parameters across embedders, vector stores, and LLM providers. Entity parameters are strictly validated using helper functions like `validateAndTrimEntityId()`, ensuring identifiers contain no internal whitespace or empty strings. Sources: [mem0-ts/src/oss/src/memory/index.ts:137-178](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts#L137-L178), [mem0-ts/src/oss/src/config/manager.ts:1-222](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/config/manager.ts#L1-L222) ```mermaid sequenceDiagram participant Caller participant Memory participant LLM participant VectorStore participant HistoryManager Caller->>Memory: add(messages, options) Memory->>VectorStore: search existing results for dedup Memory->>LLM: generate_response(system_prompt, user_prompt) LLM-->>Memory: JSON extracted memories Memory->>VectorStore: insert batch text embeddings & payloads Memory->>HistoryManager: addHistory() audit log entry Memory-->>Caller: returned memories list ``` Sources: [mem0-ts/src/oss/src/memory/index.ts:881-1040](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts#L881-L1040) When memories are added with `infer: true`, the engine executes the V3 Additive Extraction pipeline. It first queries existing vector store entries to establish an anti-hallucination UUID-to-integer mapping, retrieves recent session messages from storage, and prompts the LLM using `ADDITIVE_EXTRACTION_PROMPT`. The response text is cleaned via `removeCodeBlocks()` and parsed using `extractJson()`, which handles robust brace and bracket boundary detection for less deterministic local models. Sources: [mem0-ts/src/oss/src/memory/index.ts:918-987](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts#L918-L987), [mem0-ts/src/oss/src/prompts/index.ts:901-1042](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/prompts/index.ts#L901-L1042) > [!WARNING] > Caller metadata must never overwrite or inject identity scopes. The engine invokes `stripIdentityKeys()` on incoming metadata blobs to strip protected keys (`user_id`, `agent_id`, `run_id`, `actor_id`) before persisting payloads. Sources: [mem0-ts/src/oss/src/memory/index.ts:108-127](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts#L108-L127) --- ## TypeScript History Storage Persistence of audit logs and conversation session buffers is delegated to implementations of the `HistoryManager` interface. The core package ships with three primary history managers: `DummyHistoryManager` (a no-op stub for disabled persistence), `MemoryHistoryManager` (an in-memory Map implementation), and `SQLiteManager` (a robust relational manager built on `better-sqlite3`). Sources: [mem0-ts/src/oss/src/storage/base.ts:1-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/base.ts#L1-L13), [mem0-ts/src/oss/src/storage/MemoryHistoryManager.ts:14-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/MemoryHistoryManager.ts#L14-L15) ```typescript export interface HistoryManager { addHistory( memoryId: string, previousValue: string | null, newValue: string | null, action: string, createdAt?: string, updatedAt?: string, isDeleted?: number, ): Promise; getHistory(memoryId: string): Promise; reset(): Promise; close(): void; saveMessages?(messages: Array<{ role: string; content: string; name?: string }>, sessionScope: string): Promise; getLastMessages?(sessionScope: string, limit?: number): Promise>; } ``` Sources: [mem0-ts/src/oss/src/storage/base.ts:1-37](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/base.ts#L1-L37) The `SQLiteManager` automatically initializes local database directories via `ensureSQLiteDirectory()` and provisions two core tables: `memory_history` for tracking mutations (`ADD`, `UPDATE`, `DELETE`) and `messages` for maintaining bounded conversation buffers per session scope. When saving messages, an eviction query automatically prunes older rows exceeding a sliding window limit of the last 10 messages per session scope inside an atomic transaction. A cloud-backed alternative, `SupabaseHistoryManager`, provides identical audit capabilities over remote Supabase PostgreSQL instances with automatic table verification. Sources: [mem0-ts/src/oss/src/storage/SQLiteManager.ts:11-108](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/SQLiteManager.ts#L11-L108), [mem0-ts/src/oss/src/storage/SupabaseHistoryManager.ts:31-74](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/SupabaseHistoryManager.ts#L31-L74) --- ## TypeScript Search Fusion Retrieval and search ranking within the core package combine dense vector similarity with lexical matching, entity boosting, and temporal expiration filtering. The `BM25` utility computes term frequency-inverse document frequency scoring across tokenized memory documents using configurable parameters $k_1$ ($1.5$) and $b$ ($0.75$). Sources: [mem0-ts/src/oss/src/utils/bm25.ts:1-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/bm25.ts#L1-L20) ```typescript export class BM25 { private documents: string[][]; private k1: number; private b: number; private avgDocLength: number; private docFreq: Map; private docLengths: number[]; private idf: Map; constructor(documents: string[][], k1 = 1.5, b = 0.75) { ... } } ``` Sources: [mem0-ts/src/oss/src/utils/bm25.ts:1-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/bm25.ts#L1-L20) [Entity extraction](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/entity-extraction) heuristics implemented in `extractEntities()` parse text into four distinct categories in order of precedence: `PROPER` (capitalized multi-word sequences), `IDENTIFIER` (dotted technical paths like `person.properties.email`), `QUOTED` (explicitly quoted strings), and `TOPIC` (noun phrase compounds). Extracted entities are filtered against generic stop lists (`GENERIC_HEADS`, `NON_SPECIFIC_ADJ`) and deduplicated. Furthermore, `payloadIsExpired()` validates memory expiration dates against UTC time, ensuring expired records are omitted from search results unless `showExpired` is explicitly enabled. Sources: [mem0-ts/src/oss/src/utils/entity_extraction.ts:725-818](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/entity_extraction.ts#L725-L818), [mem0-ts/src/oss/src/utils/expiration.ts:39-52](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/expiration.ts#L39-L52) ## Related - [Memory Lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle) - [Multi-Signal Retrieval](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/multi-signal-retrieval) - [Storage and History](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/storage-and-history) - [Python Client SDK](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/python-client-sdk) - [FastAPI Application](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application) --- ## Technical docs: Python LLM Adapters URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/python-llm-adapters
Relevant source files The following files were used as context for generating this wiki page: - [mem0-ts/src/oss/src/utils/factory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts) - [mem0/llms/aws_bedrock.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/aws_bedrock.py) - [integrations/hermes-plugin-mem0/_backend.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py) - [mem0/utils/factory.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/factory.py) - [mem0/llms/deepseek.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/deepseek.py) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [mem0/llms/base.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/base.py) - [mem0/llms/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/__init__.py) - [mem0-ts/src/oss/src/llms/aws_bedrock.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/aws_bedrock.ts) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/openclaw/cli/oss-wizard.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts) - [mem0/llms/groq.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/groq.py) - [mem0-ts/src/oss/src/llms/langchain.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/langchain.ts) - [integrations/kimi-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py) - [mem0/llms/gemini.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/gemini.py) - [mem0/llms/lmstudio.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/lmstudio.py) - [integrations/hermes-plugin-mem0/_openai_llm.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_openai_llm.py) - [mem0/configs/llms/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/llms/__init__.py) - [mem0/llms/azure_openai_structured.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/azure_openai_structured.py) - [mem0/llms/ollama.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/ollama.py) - [mem0-ts/src/oss/src/llms/deepseek.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/deepseek.ts) - [mem0/llms/xai.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/xai.py) - [mem0/llms/openai_structured.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/openai_structured.py) - [mem0-ts/src/oss/src/llms/ollama.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/ollama.ts) - [mem0/llms/openai.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/openai.py) - [mem0/llms/langchain.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/langchain.py) - [mem0/llms/litellm.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/litellm.py) - [mem0/llms/vllm.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/vllm.py) - [mem0-ts/src/oss/src/llms/openai_structured.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/openai_structured.ts) - [mem0-ts/src/oss/src/llms/groq.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/groq.ts)
## Overview Python language model adapters power the memory extraction, context evaluation, and tool execution pipelines across the memory engine. By providing a uniform interface through [Memory Lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle) integrations, these adapters handle parameter filtering, reasoning model detection, and provider-specific payload formatting for diverse backend providers. Sources: [mem0/llms/base.py:7-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/base.py#L7-L11), [mem0/memory/main.py:958-964](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py#L958-L964) ## LLMBase abstraction and lifecycle ### Parameter Filtering and Reasoning Model Detection The `LLMBase` abstract base class provides the foundational interface and configuration lifecycle for all language model providers in the engine. When an adapter is instantiated, the `__init__` method accepts either a `BaseLlmConfig` instance or a raw configuration dictionary, falling back to a default `BaseLlmConfig` if no argument is provided, and subsequently executes `_validate_config` to guarantee that the required `model` attribute is present on the configuration object. Sources: [mem0/llms/base.py:7-37](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/base.py#L7-L37) The base class implements name-based heuristics and explicit configuration overrides to classify reasoning models and adjust parameter payloads accordingly via `_is_reasoning_model` and `_get_supported_params`. An explicit `is_reasoning_model` boolean on the configuration takes precedence over string matching, allowing custom or versioned model deployments to opt in or out directly. When this configuration attribute is `None`, the model name is evaluated against a predefined set of base identifiers and prefix families. > [!NOTE] > The reasoning classification strips provider prefixes (such as `openai/o3-mini`) using `rsplit("/", 1)[-1]` before matching against the base set. The `o1` and `o3` family prefixes match variants such as `o1-` and `o3-`, whereas `gpt-5.x` variants like `gpt-5.4-mini` are explicitly excluded from the reasoning prefix match so that they retain standard parameter support. Sources: [mem0/llms/base.py:38-75](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/base.py#L38-L75) ```python reasoning_models = { "o1", "o1-preview", "o3-mini", "o3", "gpt-5", "gpt-5o", "gpt-5o-mini", "gpt-5o-micro", } model_lower = model.lower() # Strip provider prefixes (e.g. "openai/o3-mini" -> "o3-mini") base_model = model_lower.rsplit("/", 1)[-1] if base_model in reasoning_models: return True # Match o1/o3 family with prefixes (o1-2024-12-17, o3-2025-04-16) # but NOT gpt-5.x variants (gpt-5.4-mini supports temperature) if any(base_model.startswith(prefix) for prefix in ["o1-", "o1.", "o3-", "o3."]): return True return False ``` Sources: [mem0/llms/base.py:58-75](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/base.py#L58-L75) When parameter filtering executes in `_get_supported_params`, reasoning models and the `gpt-5` family restrict outbound parameters to a strict subset of keys, while regular models incorporate common parameters such as `temperature`, `top_p`, and token limits through `_get_common_params`. Furthermore, `_uses_max_completion_tokens` checks whether the model starts with `gpt-5` to substitute `max_tokens` with `max_completion_tokens` on the chat completions payload. | Model Category | Included Parameters / Keys | Token Limit Parameter | | :--- | :--- | :--- | | **Reasoning Models** (`o1`, `o3`, `gpt-5` base) | `messages`, `response_format`, `tools`, `tool_choice`, `reasoning_effort` | *None* | | **Regular Models** (`gpt-4.x`, `gpt-3.5`, etc.) | `temperature`, `top_p`, plus provider `kwargs` | `max_tokens` or `max_completion_tokens` | Sources: [mem0/llms/base.py:77-170](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/base.py#L77-L170) ## Structured output adapters ### Native OpenAI and Azure OpenAI Structured Classes The structured output adapters implement strict schema parsing and specialized API interactions for OpenAI and Azure OpenAI deployments. `OpenAIStructuredLLM` inherits from `LLMBase` and initializes an OpenAI client using `api_key` and `base_url` resolution, falling back to `"gpt-5-mini"` if the configuration model is empty. Its `generate_response` method delegates payload assembly to `_get_supported_params`, injects `response_format` and tool parameters when present, and executes parsed extractions using `client.beta.chat.completions.parse`. Sources: [mem0/llms/openai_structured.py:10-49](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/openai_structured.py#L10-L49) `AzureOpenAIStructuredLLM` provides an equivalent interface tailored for Azure deployments, resolving credentials through `DefaultAzureCredential` and bearer token providers when the `api_key` is absent, empty, or set to the `"your-api-key"` placeholder. It manages Azure endpoints, deployments, and API versions through `self.config.azure_kwargs` or matching environment variables (`LLM_AZURE_OPENAI_API_KEY`, `LLM_AZURE_DEPLOYMENT`, `LLM_AZURE_ENDPOINT`, `LLM_AZURE_API_VERSION`). > [!WARNING] > Azure's indirect attack content filters can flag the literal word `assistant` in prompt message histories. `AzureOpenAIStructuredLLM` automatically runs `_rewrite_assistant_keyword` on deep-copied messages before sending requests, replacing `"assistant"` with `"ai"` exclusively in string-based content properties while leaving multimodal list structures untouched. Sources: [mem0/llms/azure_openai_structured.py:16-124](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/azure_openai_structured.py#L16-L124) ```python @staticmethod def _rewrite_assistant_keyword(messages): """ Return a copy of ``messages`` with the word "assistant" replaced by "ai" in the last message's textual content. Azure's content management policy can flag the literal word "assistant", which makes ``add`` fail (see issue #2636). The rewrite targets that trigger without mutating the caller's messages and without assuming the content is a string, so multimodal (list) content passes through untouched. """ if not messages: return messages messages = copy.deepcopy(messages) last_content = messages[-1].get("content") if isinstance(last_content, str): messages[-1]["content"] = last_content.replace("assistant", "ai") return messages ``` Sources: [mem0/llms/azure_openai_structured.py:105-124](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/azure_openai_structured.py#L105-L124) ### Parameter Formatting and Response Parsing When generating responses under `AzureOpenAIStructuredLLM`, reasoning models bypass standard sampling parameters like `temperature` and `top_p`, while `max_tokens` is dynamically mapped to `max_completion_tokens` for reasoning models and `gpt-5` series variants. Optional `reasoning_effort` configurations are appended when specified on the configuration object. Sources: [mem0/llms/azure_openai_structured.py:76-101](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/azure_openai_structured.py#L76-L101) | Parameter Key | Reasoning / GPT-5 Models | Standard Models | | :--- | :--- | :--- | | **model** | `self.config.model` | `self.config.model` | | **temperature / top_p** | Excluded | Included (`self.config.temperature`, `self.config.top_p`) | | **max_tokens** | `max_completion_tokens` | `max_tokens` | | **reasoning_effort** | Included if defined | Excluded | | **response_format** | Optional | Optional | Sources: [mem0/llms/azure_openai_structured.py:77-101](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/azure_openai_structured.py#L77-L101) Response parsing in `AzureOpenAIStructuredLLM` branches based on whether tools were supplied in the request. If tools are active, `_parse_response` extracts content from `response.choices[0].message.content` and iterates over any returned `tool_calls`, parsing function arguments safely using `extract_json` and `json.loads`. Without tools, it returns the raw message content string directly. Sources: [mem0/llms/azure_openai_structured.py:125-153](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/azure_openai_structured.py#L125-L153) ## OpenAI and drop-in compatible providers ### OpenAI API Client and Drop-In Integrations The OpenAI adapter (`OpenAILLM`) extends `LLMBase` to manage completions and function calling through the standard OpenAI Python client. If `OPENROUTER_API_KEY` is present in the environment, it routes requests through OpenRouter using custom base URLs and headers (`HTTP-Referer`, `X-Title`). Otherwise, it resolves credentials via `self.config.api_key` or `OPENAI_API_KEY`, defaulting to `"gpt-5-mini"` when no model is specified. Sources: [mem0/llms/openai.py:14-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/openai.py#L14-L54) > [!WARNING] > OpenAI-compatible backends reject unrecognized parameters. `OpenAILLM` ensures that the `store` field is only transmitted when explicitly configured by the user, preventing rejections from third-party endpoints. Sources: [mem0/llms/openai.py:129-135](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/openai.py#L129-L135) Compatible providers implement identical initialization and parsing patterns while targeting custom endpoints. `DeepSeekLLM` defaults to the `"deepseek-chat"` model, resolving its API key and base URL from `DEEPSEEK_API_KEY` and `DEEPSEEK_API_BASE` (falling back to `https://api.deepseek.com`). `XAILLM` defaults to `"grok-4.3"`, utilizing `XAI_API_KEY` and `XAI_API_BASE` with a default URL of `https://api.x.ai/v1`. `VllmLLM` defaults to `Qwen/Qwen2.5-32B-Instruct` and requires explicit `vllm_base_url` configuration with a fallback API key of `"vllm-api-key"`. `LMStudioLLM` defaults to a local GGUF model path and uses `"lm-studio"` as its default API key. Sources: [mem0/llms/deepseek.py:13-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/deepseek.py#L13-L42), [mem0/llms/xai.py:13-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/xai.py#L13-L42), [mem0/llms/vllm.py:13-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/vllm.py#L13-L40), [mem0/llms/lmstudio.py:12-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/lmstudio.py#L12-L42) ```python if self.config.lmstudio_response_format: params["response_format"] = self.config.lmstudio_response_format elif response_format: params["response_format"] = response_format else: params["response_format"] = {"type": "json_object"} ``` Sources: [mem0/llms/lmstudio.py:102-108](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/lmstudio.py#L102-L108) ### Response Parsing and Tool Execution All adapters in this family share a common `_parse_response` implementation. When tools are provided in the generation request, the method constructs a dictionary containing the message content and an array of executed `tool_calls`, where function arguments are parsed from JSON strings using `extract_json`. When no tools are requested, it returns the raw response content string directly. Sources: [mem0/llms/deepseek.py:43-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/deepseek.py#L43-L71), [mem0/llms/lmstudio.py:43-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/lmstudio.py#L43-L71), [mem0/llms/xai.py:43-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/xai.py#L43-L71), [mem0/llms/openai.py:55-83](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/openai.py#L55-L83), [mem0/llms/vllm.py:42-70](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/vllm.py#L42-L70) | Provider Class | Default Model | Default Base URL / Environment Variable | | :--- | :--- | :--- | | **OpenAILLM** | `gpt-5-mini` | `https://api.openai.com/v1` (`OPENAI_BASE_URL`) | | **DeepSeekLLM** | `deepseek-chat` | `https://api.deepseek.com` (`DEEPSEEK_API_BASE`) | | **XAILLM** | `grok-4.3` | `https://api.x.ai/v1` (`XAI_API_BASE`) | | **VllmLLM** | `Qwen/Qwen2.5-32B-Instruct` | `vllm_base_url` config parameter | | **LMStudioLLM** | `lmstudio-community/Meta-Llama-3.1-70B-Instruct-GGUF/...` | `lmstudio_base_url` config parameter | Sources: [mem0/llms/openai.py:39-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/openai.py#L39-L51), [mem0/llms/deepseek.py:36-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/deepseek.py#L36-L40), [mem0/llms/xai.py:36-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/xai.py#L36-L40), [mem0/llms/vllm.py:36-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/vllm.py#L36-L39), [mem0/llms/lmstudio.py:35-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/lmstudio.py#L35-L41) ## Cloud provider abstractions ### AWS Bedrock Multi-Model Input Formatting and Parameter Mappings AWS Bedrock supports diverse model families through `BedrockLLM`, which normalizes generation requests and translates configuration parameters based on the target provider. Provider parameter mapping dictionaries explicitly map generic configuration keys to model-specific keys: | Provider Identifier | Parameter Mapping | | :--- | :--- | | `meta` | `max_tokens` $\rightarrow$ `max_gen_len` | | `ai21` | `max_tokens` $\rightarrow$ `maxTokens`, `top_p` $\rightarrow$ `topP` | | `mistral` | `max_tokens` $\rightarrow$ `max_tokens` | | `cohere` | `max_tokens` $\rightarrow$ `max_tokens`, `top_p` $\rightarrow$ `p` | | `amazon` | `max_tokens` $\rightarrow$ `maxTokenCount`, `top_p` $\rightarrow$ `topP` | | `anthropic` | `max_tokens` $\rightarrow$ `max_tokens`, `top_p` $\rightarrow$ `top_p` | Sources: [mem0/llms/aws_bedrock.py:261-276](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/aws_bedrock.py#L261-L276) Depending on the specific AWS model family selected, `_prepare_input` constructs distinct body structures: - **Amazon Nova Models:** Uses the Converse API format containing a `messages` array, `max_tokens` (defaulting to 5000), `temperature` (defaulting to 0.1), and optional nucleus sampling (`top_p`). - **Legacy Amazon Models:** Populates `inputText` alongside a `textGenerationConfig` object containing `maxTokenCount` and `temperature`. - **Anthropic Models:** Formats messages into a structured block with `anthropic_version` set to `"bedrock-2023-05-31"`, `max_tokens` defaulting to 2000, and `temperature` to 0.1. - **Cohere Command Models:** Replaces the standard `prompt` key with `message` when `"cohere.command"` is present in the model identifier. Sources: [mem0/llms/aws_bedrock.py:277-324](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/aws_bedrock.py#L277-L324) > [!NOTE] > When converting standard function tools for AWS Bedrock via `_convert_tool_format`, the engine extracts required parameters, constructs an `inputSchema` wrapping a JSON object type with properties, and stores the resulting tool specification under the `toolSpec` key. Sources: [mem0/llms/aws_bedrock.py:326-361](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/aws_bedrock.py#L326-L361) ### Gemini Message Reformatting and Tool Integration `GeminiLLM` instantiates clients using either the standard Google GenAI client or Vertex AI (`vertexai=True`), defaulting to `"gemini-2.0-flash"` when no model name is supplied. Sources: [mem0/llms/gemini.py:36-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/gemini.py#L36-L44) The adapter separates incoming chat history via `_reformat_messages`, isolating system prompts into `system_instruction` while converting remaining entries into `types.Content` structures with part lists and role assignments. Sources: [mem0/llms/gemini.py:92-115](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/gemini.py#L92-L115) ```python def remove_additional_properties(data): """Recursively removes 'additionalProperties' from nested dictionaries.""" if isinstance(data, dict): filtered_dict = { key: remove_additional_properties(value) for key, value in data.items() if not (key == "additionalProperties") } return filtered_dict else: return data ``` Sources: [mem0/llms/gemini.py:128-139](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/gemini.py#L128-L139) Tool definitions undergo recursive purging of `additionalProperties` constraints before being wrapped into `types.FunctionDeclaration` arrays and encapsulated within `types.Tool` objects. Sources: [mem0/llms/gemini.py:117-156](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/gemini.py#L117-L156) Tool choice configurations map API parameters directly to Gemini execution modes using `types.FunctionCallingConfigMode` options: - `auto` maps to `types.FunctionCallingConfigMode.AUTO` - `any` maps to `types.FunctionCallingConfigMode.ANY`, restricting execution via `allowed_function_names` - Any other value defaults to `types.FunctionCallingConfigMode.NONE` Sources: [mem0/llms/gemini.py:204-221](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/gemini.py#L204-L221) ## Local and universal runtime bridges ### Local Deployment via Ollama `OllamaLLM` integrates local models by wrapping the `ollama` Python library and accepting configuration variants including `BaseLlmConfig`, `OllamaConfig`, or raw dictionaries, defaulting to `"llama3.1:70b"` when model selection is omitted. Sources: [mem0/llms/ollama.py:16-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/ollama.py#L16-L42) ```python # Build parameters for Ollama params = { "model": self.config.model, "messages": messages, } # Handle JSON response format by using Ollama's native format parameter if response_format and response_format.get("type") == "json_object": params["format"] = "json" messages = [dict(m) for m in messages] if messages and messages[-1]["role"] == "user": messages[-1]["content"] += "\n\nPlease respond with valid JSON only." else: messages.append({"role": "user", "content": "Please respond with valid JSON only."}) params["messages"] = messages # Add options for Ollama (temperature, num_predict, top_p) options = { "temperature": self.config.temperature, "num_predict": self.config.max_tokens, "top_p": self.config.top_p, } params["options"] = options # Remove OpenAI-specific parameters that Ollama doesn't support params.pop("max_tokens", None) # Ollama uses different parameter names if tools: params["tools"] = tools response = self.client.chat(**params) return self._parse_response(response, tools) ``` Sources: [mem0/llms/ollama.py:113-144](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/ollama.py#L113-L144) > [!WARNING] > Ollama does not support OpenAI's `max_tokens` argument name. `OllamaLLM` explicitly pops `max_tokens` from the parameter dictionary and maps configuration limits to `num_predict` inside the nested `options` block. Sources: [mem0/llms/ollama.py:129-138](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/ollama.py#L129-L138) ### Universal Proxying via LiteLLM `LiteLLM` routes chat completion requests across diverse provider APIs through the `litellm` proxy package, defaulting to `"gpt-5-mini"` if no model string is specified. Sources: [mem0/llms/litellm.py:16-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/litellm.py#L16-L22) ```python if tools and not litellm.supports_function_calling(self.config.model): raise ValueError(f"Model '{self.config.model}' in litellm does not support function calling.") params = { "model": self.config.model, "messages": messages, "temperature": self.config.temperature, "top_p": self.config.top_p, } if self._uses_max_completion_tokens(self.config.model): params["max_completion_tokens"] = self.config.max_tokens else: params["max_tokens"] = self.config.max_tokens if response_format: params["response_format"] = response_format if tools: # TODO: Remove tools if no issues found with new memory addition logic params["tools"] = tools params["tool_choice"] = tool_choice response = litellm.completion(**params) ``` Sources: [mem0/llms/litellm.py:72-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/litellm.py#L72-L91) > [!IMPORTANT] > Before executing generation calls with tools enabled, `LiteLLM` verifies function support via `litellm.supports_function_calling(self.config.model)` and raises a `ValueError` if the target model lacks function-calling capabilities. Sources: [mem0/llms/litellm.py:72-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/litellm.py#L72-L73) ### LangChain Model Wrapping `LangchainLLM` wraps an instantiated LangChain chat model (`BaseChatModel`), requiring the `model` attribute to be supplied directly within the configuration object. Sources: [mem0/llms/langchain.py:13-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/langchain.py#L13-L24) ```python # Convert the messages to LangChain's tuple format langchain_messages = [] for message in messages: role = message["role"] content = message["content"] if role == "system": langchain_messages.append(("system", content)) elif role == "user": langchain_messages.append(("human", content)) elif role == "assistant": langchain_messages.append(("ai", content)) if not langchain_messages: raise ValueError("No valid messages found in the messages list") langchain_model = self.langchain_model if tools: langchain_model = langchain_model.bind_tools(tools=tools, tool_choice=tool_choice) response: AIMessage = langchain_model.invoke(langchain_messages, **kwargs) ``` Sources: [mem0/llms/langchain.py:76-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/langchain.py#L76-L96) ## Related - [Memory Lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle) - [TypeScript LLM Adapters](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/typescript-llm-adapters) --- ## Technical docs: GET Get memory history URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/memory-history ## Parameters ## Responses ## Try It --- ## Technical docs: DELETE Delete a memory URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/delete-memory ## Parameters ## Responses ## Try It --- ## Technical docs: TypeScript LLM Adapters URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/typescript-llm-adapters
Relevant source files The following files were used as context for generating this wiki page: - [mem0-ts/src/oss/src/utils/factory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts) - [integrations/openclaw/cli/oss-wizard.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts) - [integrations/openclaw/providers.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/providers.ts) - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [integrations/vercel-ai-sdk/src/mem0-provider-selector.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider-selector.ts) - [mem0-ts/src/oss/src/llms/aws_bedrock.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/aws_bedrock.ts) - [integrations/deepseek-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts) - [integrations/vercel-ai-sdk/src/provider-response-provider.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/provider-response-provider.ts) - [mem0-ts/src/oss/src/llms/deepseek.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/deepseek.ts) - [mem0/utils/factory.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/factory.py) - [mem0-ts/src/oss/src/llms/mistral.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/mistral.ts) - [mem0-ts/src/oss/src/llms/vllm.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/vllm.ts) - [mem0-ts/src/oss/src/llms/groq.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/groq.ts) - [mem0-ts/src/oss/src/llms/ollama.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/ollama.ts) - [mem0-ts/src/oss/src/llms/xai.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/xai.ts) - [integrations/vercel-ai-sdk/src/mem0-provider.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider.ts) - [mem0-ts/src/oss/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/index.ts) - [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts) - [mem0-ts/src/oss/src/llms/lmstudio.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/lmstudio.ts) - [mem0-ts/src/oss/src/llms/anthropic.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/anthropic.ts) - [mem0-ts/src/oss/src/llms/google.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/google.ts) - [mem0-ts/src/oss/src/llms/sarvam.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/sarvam.ts) - [mem0/llms/deepseek.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/deepseek.py) - [mem0-ts/src/oss/src/llms/openai.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/openai.ts) - [integrations/vercel-ai-sdk/src/mem0-facade.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-facade.ts) - [mem0/llms/aws_bedrock.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/aws_bedrock.py) - [mem0-ts/src/oss/src/llms/azure.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/azure.ts) - [mem0-ts/src/oss/src/llms/langchain.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/langchain.ts) - [integrations/vercel-ai-sdk/config/test-config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/config/test-config.ts) - [mem0-ts/src/oss/src/llms/base.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/base.ts)
## Overview The [TypeScript core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/typescript-core/typescript-core) package provides a unified LLM abstraction layer for extracting structured facts, processing memories, and executing chat completions across diverse language model backends. Built around a core `LLM` interface and standardized `LLMResponse` structures, the architecture enables modular provider integration and flexible configuration handling via the `LLMFactory`. Sources: [mem0-ts/src/oss/src/llms/base.ts:1-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/base.ts#L1-L18), [mem0-ts/src/oss/src/utils/factory.ts:108-152](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L108-L152) ## LLM Abstraction and Types ### Core Interface and Response Structures The TypeScript core establishes a standard contract for all language model integrations via the `LLM` interface. Implementations must provide two primary asynchronous methods: `generateResponse`, which accepts an array of message objects along with optional response formatting and tools, and `generateChat`, which takes typed `Message` arrays and returns a standardized `LLMResponse`. ```typescript export interface LLMResponse { content: string; role: string; toolCalls?: Array<{ name: string; arguments: string; }>; } export interface LLM { generateResponse( messages: Array<{ role: string; content: string }>, response_format?: { type: string }, tools?: any[], ): Promise; generateChat(messages: Message[]): Promise; } ``` Sources: [mem0-ts/src/oss/src/llms/base.ts:3-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/base.ts#L3-L19) ## OpenAI and Compatible Subclasses ### Base OpenAI Client and Provider Configurations The `OpenAILLM` class serves as the foundational base adapter utilizing the official `openai` package to execute chat completions and tool calls [mem0-ts/src/oss/src/llms/openai.ts:1-75](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/openai.ts#L1-L75). It maps input messages to valid roles (`system`, `user`, or `assistant`), serializes complex content blocks, and handles response parsing for both direct string outputs and structured tool invocations [mem0-ts/src/oss/src/llms/openai.ts:23-53](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/openai.ts#L23-L53). Several subclassed providers extend `OpenAILLM` by supplying customized default environment variables, base URLs, and model identifiers [mem0-ts/src/oss/src/llms/deepseek.ts:1-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/deepseek.ts#L1-L43), [mem0-ts/src/oss/src/llms/vllm.ts:1-49](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/vllm.ts#L1-L49), [mem0-ts/src/oss/src/llms/xai.ts:1-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/xai.ts#L1-L50), [mem0-ts/src/oss/src/llms/lmstudio.ts:1-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/lmstudio.ts#L1-L41), [mem0-ts/src/oss/src/llms/sarvam.ts:1-52](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/sarvam.ts#L1-L52). | Provider Class | Default Model | Default Base URL | Key Resolution Order | | :--- | :--- | :--- | :--- | | `OpenAILLM` | `gpt-5-mini` | `config.baseURL` | `config.apiKey` | | `DeepSeekLLM` | `deepseek-chat` | `config.baseURL` \|\| `DEEPSEEK_API_BASE` \|\| `https://api.deepseek.com` | `config.apiKey` \|\| `DEEPSEEK_API_KEY` | | `VllmLLM` | `Qwen/Qwen2.5-32B-Instruct` | `config.baseURL` \|\| `config.vllmBaseURL` \|\| `config.vllm_base_url` \|\| `config.url` \|\| `VLLM_BASE_URL` \|\| `http://localhost:8000/v1` | `config.apiKey` \|\| `VLLM_API_KEY` \|\| `vllm-api-key` | | `XAILLM` | `grok-4.3` | `config.baseURL` \|\| `XAI_API_BASE` \|\| `https://api.x.ai/v1` | `config.apiKey` \|\| `XAI_API_KEY` | | `LMStudioLLM` | `lmstudio-community/Meta-Llama-3.1-70B-Instruct-GGUF/Meta-Llama-3.1-70B-Instruct-IQ2_M.gguf` | `config.baseURL` ?? `http://localhost:1234/v1` | `config.apiKey` \|\| `lm-studio` | | `SarvamLLM` | `sarvam-m` | `config.baseURL` \|\| `SARVAM_API_BASE` \|\| `https://api.sarvam.ai/v1` | `config.apiKey` \|\| `SARVAM_API_KEY` | Sources: [mem0-ts/src/oss/src/llms/deepseek.ts:5-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/deepseek.ts#L5-L20), [mem0-ts/src/oss/src/llms/vllm.ts:5-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/vllm.ts#L5-L26), [mem0-ts/src/oss/src/llms/xai.ts:14-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/xai.ts#L14-L27), [mem0-ts/src/oss/src/llms/lmstudio.ts:5-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/lmstudio.ts#L5-L18), [mem0-ts/src/oss/src/llms/sarvam.ts:14-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/sarvam.ts#L14-L29), [mem0-ts/src/oss/src/llms/openai.ts:5-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/openai.ts#L5-L16) > [!WARNING] > `DeepSeekLLM`, `XAILLM`, and `SarvamLLM` throw an explicit synchronous `Error` during initialization if an API key cannot be resolved from configuration parameters or their respective environment variables (`DEEPSEEK_API_KEY`, `XAI_API_KEY`, `SARVAM_API_KEY`). > Sources: [mem0-ts/src/oss/src/llms/deepseek.ts:7-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/deepseek.ts#L7-L10), [mem0-ts/src/oss/src/llms/xai.ts:16-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/xai.ts#L16-L19), [mem0-ts/src/oss/src/llms/sarvam.ts:16-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/sarvam.ts#L16-L19) ### Error Handling and Subclass Wrappers Each subclass wraps its execution methods (`generateResponse` and `generateChat`) in `try...catch` blocks to intercept runtime exceptions from the underlying OpenAI client and rethrow them with provider-prefixed context messages [mem0-ts/src/oss/src/llms/deepseek.ts:22-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/deepseek.ts#L22-L42), [mem0-ts/src/oss/src/llms/vllm.ts:28-48](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/vllm.ts#L28-L48), [mem0-ts/src/oss/src/llms/xai.ts:29-49](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/xai.ts#L29-L49), [mem0-ts/src/oss/src/llms/lmstudio.ts:20-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/lmstudio.ts#L20-L40), [mem0-ts/src/oss/src/llms/sarvam.ts:31-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/sarvam.ts#L31-L51). For example, `LMStudioLLM` catches any error during generation and formats it into an `LM Studio LLM failed: ${message}` exception [mem0-ts/src/oss/src/llms/lmstudio.ts:27-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/lmstudio.ts#L27-L30). Sources: [mem0-ts/src/oss/src/llms/deepseek.ts:22-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/deepseek.ts#L22-L42), [mem0-ts/src/oss/src/llms/vllm.ts:28-48](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/vllm.ts#L28-L48), [mem0-ts/src/oss/src/llms/xai.ts:29-49](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/xai.ts#L29-L49), [mem0-ts/src/oss/src/llms/lmstudio.ts:20-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/lmstudio.ts#L20-L40), [mem0-ts/src/oss/src/llms/sarvam.ts:31-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/sarvam.ts#L31-L51) ## Proprietary SDK Providers ### Anthropic, Google, Mistral, and Groq Integration The TypeScript engine implements direct SDK client wrappers for proprietary language model providers that do not rely on the OpenAI-compatible base class. Each provider manages its own lazy client initialization using `loadPeer` to dynamically import underlying SDK packages such as `@anthropic-ai/sdk`, `@google/genai`, `@mistralai/mistralai`, and `groq-sdk` upon first invocation [mem0-ts/src/oss/src/llms/anthropic.ts:34-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/anthropic.ts#L34-L42), [mem0-ts/src/oss/src/llms/google.ts:16-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/google.ts#L16-L24), [mem0-ts/src/oss/src/llms/mistral.ts:19-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/mistral.ts#L19-L27), [mem0-ts/src/oss/src/llms/groq.ts:20-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/groq.ts#L20-L28). | Provider Class | Default Model | Configuration Keys | SDK Peer Package | | :--- | :--- | :--- | :--- | | `AnthropicLLM` | `claude-sonnet-4-6` | `config.apiKey` \|\| `ANTHROPIC_API_KEY`, `config.baseURL`, `config.maxTokens`, `config.temperature`, `config.topP` | `@anthropic-ai/sdk` | | `GoogleLLM` | `gemini-2.0-flash` | `config.apiKey`, `config.model` | `@google/genai` | | `MistralLLM` | `mistral-tiny-latest` | `config.apiKey`, `config.model` | `@mistralai/mistralai` | | `GroqLLM` | `llama3-70b-8192` | `config.apiKey` \|\| `GROQ_API_KEY`, `config.model` | `groq-sdk` | Sources: [mem0-ts/src/oss/src/llms/anthropic.ts:14-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/anthropic.ts#L14-L32), [mem0-ts/src/oss/src/llms/google.ts:11-14](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/google.ts#L11-L14), [mem0-ts/src/oss/src/llms/mistral.ts:11-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/mistral.ts#L11-L17), [mem0-ts/src/oss/src/llms/groq.ts:11-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/groq.ts#L11-L18) > [!WARNING] > `AnthropicLLM`, `MistralLLM`, and `GroqLLM` throw an explicit synchronous `Error` during instantiation if the required API key is missing from configuration parameters and environment variables. `GoogleLLM` permits an undefined API key during construction to support ambient credentials. > Sources: [mem0-ts/src/oss/src/llms/anthropic.ts:15-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/anthropic.ts#L15-L18), [mem0-ts/src/oss/src/llms/mistral.ts:11-14](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/mistral.ts#L11-L14), [mem0-ts/src/oss/src/llms/groq.ts:11-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/groq.ts#L11-L15), [mem0-ts/src/oss/src/llms/google.ts:11-14](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/google.ts#L11-L14) ### Message Formatting and Response Parsing Each adapter adapts internal generic `Message` structures into provider-specific schemas. `AnthropicLLM` extracts system messages into a dedicated `system` property, forwards remaining messages with mapped roles, and iterates through response content blocks to handle text and `tool_use` blocks [mem0-ts/src/oss/src/llms/anthropic.ts:50-101](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/anthropic.ts#L50-L101). `GoogleLLM` formats message contents into part arrays and maps system roles to `model` roles while setting `responseMimeType` to `application/json` when `responseFormat.type` equals `json_object` [mem0-ts/src/oss/src/llms/google.ts:26-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/google.ts#L26-L38), [mem0-ts/src/oss/src/llms/google.ts:67-69](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/google.ts#L67-L69). > [!TIP] > `GoogleLLM` automatically strips leading markdown JSON fences (`^```json\n` and `\n```$`) from text completions when structured JSON output is returned without native mime-type enforcement. > Sources: [mem0-ts/src/oss/src/llms/google.ts:89-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/google.ts#L89-L91) `MistralLLM` includes a `contentToString` helper that processes text chunks and content arrays into strings, and maps tool calls when present in completion choices [mem0-ts/src/oss/src/llms/mistral.ts:30-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/mistral.ts#L30-L47), [mem0-ts/src/oss/src/llms/mistral.ts:78-90](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/mistral.ts#L78-L90). `GroqLLM` maps chat messages to standard completion inputs and returns message content strings or chat response structures directly [mem0-ts/src/oss/src/llms/groq.ts:30-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/groq.ts#L30-L68). Sources: [mem0-ts/src/oss/src/llms/mistral.ts:30-93](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/mistral.ts#L30-L93), [mem0-ts/src/oss/src/llms/groq.ts:30-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/groq.ts#L30-L68) ## AWS Bedrock Converse Integration ### Message Formatting and Provider Extraction The `AWSBedrockLLM` class implements the LLM interface using the AWS Bedrock **Converse API** (`ConverseCommand`). This provides a unified message and tool interface across multiple model families, including Anthropic, Amazon (Nova), Meta, Mistral, and Cohere. The optional `@aws-sdk/client-bedrock-runtime` package is loaded dynamically on first use via an `import()` statement to ensure the SDK remains optional. Credentials resolve via the standard AWS chain or explicit configuration properties (`awsRegion`, `awsAccessKeyId`, `awsSecretAccessKey`, `awsSessionToken`, or an injected `client`). ```typescript const PROVIDERS = [ "ai21", "amazon", "anthropic", "cohere", "meta", "mistral", "stability", "writer", "deepseek", "gpt-oss", "perplexity", "snowflake", "titan", "command", "j2", "llama", "minimax", ]; ``` Sources: [mem0-ts/src/oss/src/llms/aws_bedrock.ts:8-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/aws_bedrock.ts#L8-L26) > [!WARNING] > Dynamic SDK loading must use a dynamic `import()`, never `require()`. Bundlers like tsup and esbuild rewrite `require()` into a `__require` shim that throws an error when evaluating published ESM bundles (`dist/oss/index.mjs`), causing the provider to fail even when the AWS SDK is installed. > Sources: [mem0-ts/src/oss/src/llms/aws_bedrock.ts:119-125](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/aws_bedrock.ts#L119-L125) Model family identifiers are validated and extracted against a recognized provider list. If `providerOverride` is specified, it must match one of the recognized provider strings; otherwise, the adapter searches the model string using regular expressions. ```typescript export function extractProvider( model: string, providerOverride?: string, ): string { if (providerOverride) { if (!PROVIDERS.includes(providerOverride)) { throw new Error( `Unknown providerOverride '${providerOverride}'. Valid providers: ${PROVIDERS.join(", ")}`, ); } return providerOverride; } for (const provider of PROVIDERS) { const re = new RegExp( `\\b${provider.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, ); if (re.test(model)) return provider; } throw new Error(`Unknown provider in model: ${model}`); } ``` Sources: [mem0-ts/src/oss/src/llms/aws_bedrock.ts:32-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/aws_bedrock.ts#L32-L51) Messages are split into a top-level `system` block and role-tagged content blocks for the Converse API. System roles are accumulated into system parts, while assistant and user roles are mapped to `converseMessages`. If no messages are provided, a default empty user message is added. ```typescript /** * Split messages into a top-level `system` block (Converse passes system * prompts separately) and role-tagged content blocks for everything else. */ private formatMessages(messages: Message[]): { system?: { text: string }[]; converseMessages: { role: string; content: { text: string }[] }[]; } { const systemParts: string[] = []; const converseMessages: { role: string; content: { text: string }[] }[] = []; for (const msg of messages) { const role = msg.role; const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content); if (role === "system") { systemParts.push(content); } else { converseMessages.push({ role: role === "assistant" ? "assistant" : "user", content: [{ text: content }], }); } } if (converseMessages.length === 0) { converseMessages.push({ role: "user", content: [{ text: "" }] }); } return { system: systemParts.length ? [{ text: systemParts.join("\n") }] : undefined, converseMessages, }; } ``` Sources: [mem0-ts/src/oss/src/llms/aws_bedrock.ts:156-194](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/aws_bedrock.ts#L156-L194) ## Local Host and Framework Connectors ### Ollama Client Initialization and Model Validation The `OllamaLLM` class connects to local Ollama servers to manage local model execution. Host URLs default to `http://localhost:11434` if neither `config.url` nor `config.baseURL` is supplied, and model selection defaults to `llama3.1:8b`. The peer dependency for Ollama is dynamically loaded via `loadPeer`. ```typescript export class OllamaLLM implements LLM { private ollama!: Ollama; private model: string; private readonly host: string; // Using this variable to avoid calling the Ollama server multiple times private initialized: boolean = false; constructor(config: LLMConfig) { this.host = config.url || config.baseURL || "http://localhost:11434"; this.model = config.model || "llama3.1:8b"; this.ensureModelExists().catch((err) => { logger.error(`Error ensuring model exists: ${err}`); }); } private async ensureClient(): Promise { if (this.ollama) return; const sdk = await loadPeer("ollama", "Ollama LLM", () => import("ollama")); this.ollama = new sdk.Ollama({ host: this.host }); } ``` Sources: [mem0-ts/src/oss/src/llms/ollama.ts:7-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/ollama.ts#L7-L26) Before executing generations or chats, `ensureModelExists` checks whether the target model is present in the local server list using `this.ollama.list()`. If the model is absent, `this.ollama.pull` downloads it automatically while setting an internal flag to prevent redundant API checks across subsequent calls. ```typescript private async ensureModelExists(): Promise { if (this.initialized) { return true; } await this.ensureClient(); const local_models = await this.ollama.list(); if (!local_models.models.find((m: any) => m.name === this.model)) { logger.info(`Pulling model ${this.model}...`); await this.ollama.pull({ model: this.model }); } this.initialized = true; return true; } ``` Sources: [mem0-ts/src/oss/src/llms/ollama.ts:100-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/ollama.ts#L100-L112) > [!NOTE] > The `initialized` boolean flag on `OllamaLLM` ensures that `ensureModelExists` only queries and pulls models once during the instance lifecycle, avoiding redundant network roundtrips to the local Ollama daemon. > Sources: [mem0-ts/src/oss/src/llms/ollama.ts:11-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/ollama.ts#L11-L12), [mem0-ts/src/oss/src/llms/ollama.ts:100-103](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/ollama.ts#L100-L103) --- ### LangChain Message Bridging and Schema Selection The `LangchainLLM` adapter wraps existing LangChain language model instances (`BaseLanguageModel`). The constructor verifies that `config.model` is an object containing an `invoke` method. Model names are resolved from `modelId`, `model`, or defaulted to `langchain-model`. ```typescript export class LangchainLLM implements LLM { private llmInstance: BaseLanguageModel; private modelName: string; constructor(config: LLMConfig) { if (!config.model || typeof config.model !== "object") { throw new Error( "Langchain provider requires an initialized Langchain instance passed via the 'model' field in the LLM config.", ); } if (typeof (config.model as any).invoke !== "function") { throw new Error( "Provided Langchain 'instance' in the 'model' field does not appear to be a valid Langchain language model (missing invoke method).", ); } this.llmInstance = config.model as BaseLanguageModel; this.modelName = (this.llmInstance as any).modelId || (this.llmInstance as any).model || "langchain-model"; } ``` Sources: [mem0-ts/src/oss/src/llms/langchain.ts:37-57](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/langchain.ts#L37-L57) The helper function `convertToLangchainMessages` translates Mem0 message roles into LangChain message classes. Unrecognized roles log a warning and fall back to `HumanMessage`. | Mem0 Role | LangChain Message Class | | :--- | :--- | | `system` | `SystemMessage` | | `user`, `human` | `HumanMessage` | | `assistant`, `ai` | `AIMessage` | | *unknown* | `HumanMessage` (with console warning) | ```typescript const convertToLangchainMessages = async ( messages: Message[], ): Promise => { const { AIMessage, HumanMessage, SystemMessage } = await import("@langchain/core/messages"); return messages.map((msg) => { const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content); switch (msg.role?.toLowerCase()) { case "system": return new SystemMessage(content); case "user": case "human": return new HumanMessage(content); case "assistant": case "ai": return new AIMessage(content); default: console.warn( `Unsupported message role '${msg.role}' for Langchain. Treating as 'human'.`, ); return new HumanMessage(content); } }); }; ``` Sources: [mem0-ts/src/oss/src/llms/langchain.ts:9-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/langchain.ts#L9-L35) During response generation, `LangchainLLM` inspects system and user prompts to select schemas for structured extraction: - If the system prompt contains `"Personal Information Organizer"` and `"extract relevant pieces of information"`, `FactRetrievalSchema` is selected. - If the user prompt contains `"smart memory manager"` and `"Compare newly retrieved facts"`, `MemoryUpdateSchema` is selected. If a schema is matched and `withStructuredOutput` is available on the underlying instance, the adapter wraps the runnable using the schema. Tool bindings are applied via `bindTools` when available. ```typescript // --- Internal Schema Selection Logic (runs regardless of response_format) --- const systemPromptContent = (messages.find((m) => m.role === "system")?.content as string) || ""; const userPromptContent = (messages.find((m) => m.role === "user")?.content as string) || ""; // Check for memory prompts if ( systemPromptContent.includes("Personal Information Organizer") && systemPromptContent.includes("extract relevant pieces of information") ) { selectedSchema = FactRetrievalSchema; } else if ( userPromptContent.includes("smart memory manager") && userPromptContent.includes("Compare newly retrieved facts") ) { selectedSchema = MemoryUpdateSchema; } // --- Apply Structured Output if Schema Selected --- if ( selectedSchema && typeof (this.llmInstance as any).withStructuredOutput === "function" ) { try { runnable = (this.llmInstance as any).withStructuredOutput( selectedSchema, { name: tools?.[0]?.function.name }, ); isStructuredOutput = true; } catch (e) { isStructuredOutput = false; // Ensure flag is false on error // No fallback to response_format here unless explicitly passed if (response_format?.type === "json_object") { invokeOptions.response_format = { type: "json_object" }; } } } else if (selectedSchema && response_format?.type === "json_object") { // Schema selected, but no .withStructuredOutput. Try basic response_format only if explicitly requested. if ( (this.llmInstance as any)._identifyingParams?.response_format || (this.llmInstance as any).response_format ) { invokeOptions.response_format = { type: "json_object" }; } } else if (!selectedSchema && response_format?.type === "json_object") { // Explicit JSON request, but no schema inferred. Try basic response_format. if ( (this.llmInstance as any)._identifyingParams?.response_format || (this.llmInstance as any).response_format ) { invokeOptions.response_format = { type: "json_object" }; } } // --- Handle tool binding --- if (tools && tools.length > 0) { if (typeof (runnable as any).bindTools === "function") { try { runnable = (runnable as any).bindTools(tools); } catch (e) {} } else { } } ``` Sources: [mem0-ts/src/oss/src/llms/langchain.ts:70-132](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/langchain.ts#L70-L132) ## Related - [Python LLM Adapters](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/python-llm-adapters) - [Typescript Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/typescript-core/typescript-core) --- ## Technical docs: DELETE Delete all memories URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/delete-all-memories ## Parameters ## Responses ## Try It --- ## Technical docs: Python Embedding Adapters URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/python-embedding-adapters
Relevant source files The following files were used as context for generating this wiki page: - [mem0-ts/src/oss/src/utils/factory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts) - [mem0/embeddings/ollama.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/ollama.py) - [mem0/embeddings/aws_bedrock.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/aws_bedrock.py) - [mem0/embeddings/fastembed.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/fastembed.py) - [mem0/embeddings/gemini.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/gemini.py) - [mem0/utils/factory.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/factory.py) - [mem0/embeddings/lmstudio.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/lmstudio.py) - [mem0-ts/src/oss/src/embeddings/fastembed.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/fastembed.ts) - [mem0-ts/src/oss/src/embeddings/aws_bedrock.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/aws_bedrock.ts) - [mem0/embeddings/vertexai.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/vertexai.py) - [mem0/llms/aws_bedrock.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/aws_bedrock.py) - [mem0/embeddings/huggingface.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/huggingface.py) - [mem0-ts/src/oss/src/embeddings/vertexai.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/vertexai.ts) - [mem0/embeddings/azure_openai.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/azure_openai.py) - [mem0/embeddings/openai.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/openai.py) - [mem0/embeddings/base.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/base.py) - [mem0/embeddings/langchain.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/langchain.py) - [mem0-ts/src/oss/src/embeddings/ollama.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/ollama.ts) - [integrations/openclaw/cli/oss-wizard.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts) - [mem0-ts/src/oss/src/embeddings/lmstudio.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/lmstudio.ts) - [mem0/embeddings/together.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/together.py) - [mem0/configs/embeddings/base.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/embeddings/base.py) - [mem0-ts/src/oss/src/embeddings/langchain.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/langchain.ts) - [integrations/hermes-plugin-mem0/_backend.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py) - [mem0/configs/embeddings/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/embeddings/__init__.py) - [mem0-ts/src/oss/src/embeddings/azure.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/azure.ts) - [mem0/configs/vector_stores/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/vector_stores/__init__.py) - [mem0/vector_stores/vertex_ai_vector_search.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/vertex_ai_vector_search.py) - [mem0/embeddings/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/__init__.py) - [mem0-ts/src/oss/src/embeddings/google.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/google.ts)
## Overview Python embedding adapters provide a standardized architecture for generating vector representations of text across various cloud-managed and self-hosted machine learning providers. By implementing a common abstract contract with unified `embed` and `embed_batch` signatures, the framework allows memory subsystems to generate dense vectors interchangeably regardless of the underlying model provider. The architecture abstracts provider-specific connection management, credential handling, payload formatting, and dimension configuration into modular driver implementations. This design enables seamless integration with commercial endpoints such as OpenAI, Azure OpenAI, AWS Bedrock, Google GenAI, and Vertex AI, alongside local inference runtimes like Ollama, FastEmbed, LMStudio, and Hugging Face transformer pipelines. Sources: [mem0/embeddings/base.py:7-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/base.py#L7-L46), [mem0/utils/factory.py:152-178](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/factory.py#L152-L178) ## Abstract Embedding Base Contract ### Core Interface Definitions The Python core defines vector generation through the abstract base class `EmbeddingBase`, located in `mem0/embeddings/base.py`. This class provides initialization logic that accepts an optional `BaseEmbedderConfig` instance. If no configuration object is supplied during instantiation, it instantiates and assigns a default `BaseEmbedderConfig` instance to `self.config`. Sources: [mem0/embeddings/base.py:7-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/base.py#L7-L19) ```python class EmbeddingBase(ABC): """Initialized a base embedding class :param config: Embedding configuration option class, defaults to None :type config: Optional[BaseEmbedderConfig], optional """ def __init__(self, config: Optional[BaseEmbedderConfig] = None): if config is None: self.config = BaseEmbedderConfig() else: self.config = config ``` Sources: [mem0/embeddings/base.py:7-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/base.py#L7-L19) ### Method Signatures and Contracts Subclasses extending `EmbeddingBase` are required to implement the abstract method `embed`, which takes a text string and an optional memory action type. The `embed_batch` method provides a default implementation that iterates over a list of texts and calls `embed` sequentially for each item, though subclasses with native batch capabilities override this method for better performance. Sources: [mem0/embeddings/base.py:20-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/base.py#L20-L47) ```python @abstractmethod def embed(self, text, memory_action: Optional[Literal["add", "search", "update"]]): """ Get the embedding for the given text. Args: text (str): The text to embed. memory_action (optional): The type of embedding to use. Must be one of "add", "search", or "update". Defaults to None. Returns: list: The embedding vector. """ pass def embed_batch(self, texts, memory_action="add"): """Embed multiple texts. Override in subclasses for native batch support. Default implementation calls embed() sequentially for each text. Subclasses with native batch APIs (e.g., OpenAI) should override this for better performance. Args: texts: List of text strings to embed. memory_action: The action context ("add", "search", "update"). Returns: List of embedding vectors (list of floats), one per input text. """ return [self.embed(text, memory_action) for text in texts] ``` Sources: [mem0/embeddings/base.py:20-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/base.py#L20-L47) | Method | Parameters | Return Type | Description | | :--- | :--- | :--- | :--- | | `__init__` | `config: Optional[BaseEmbedderConfig] = None` | `None` | Initializes the embedder with a configuration object or defaults to `BaseEmbedderConfig`. | | `embed` | `text`, `memory_action: Optional[Literal["add", "search", "update"]]` | `list` | Abstract method to generate an embedding vector for a single text input. | | `embed_batch` | `texts`, `memory_action="add"` | `list` | Generates embedding vectors for multiple texts, defaulting to sequential `embed` calls. | Sources: [mem0/embeddings/base.py:14-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/base.py#L14-L47) ## Embedder Configuration Hierarchy ### Configuration Schema and Provider Parameters The configuration architecture for embedding adapters centers on the `BaseEmbedderConfig` abstract base class defined in `mem0/configs/embeddings/base.py`. This class provides a unified initialization schema supporting core properties, HTTP client proxies, and provider-specific parameters for OpenAI, Azure OpenAI, Ollama, Hugging Face, Vertex AI, Gemini, LM Studio, and AWS Bedrock. Sources: [mem0/configs/embeddings/base.py:1-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/embeddings/base.py#L1-L43) | Parameter | Type | Default Value | Provider / Purpose | | :--- | :--- | :--- | :--- | | `model` | `Optional[str]` | `None` | Embedding model identifier across providers. | | `api_key` | `Optional[str]` | `None` | API key credential for authenticated requests. | | `embedding_dims` | `Optional[int]` | `None` | Target dimension count for generated vectors. | | `ollama_base_url` | `Optional[str]` | `None` | Ollama API endpoint base URL. | | `openai_base_url` | `Optional[str]` | `None` | OpenAI API endpoint base URL. | | `model_kwargs` | `Optional[dict]` | `None` | Key-value arguments for Hugging Face transformer models. | | `huggingface_base_url` | `Optional[str]` | `None` | Hugging Face base URL endpoint. | | `azure_kwargs` | `Optional[AzureConfig]` | `None` | Configuration dictionary or `AzureConfig` instance for Azure OpenAI. | | `http_client_proxies` | `Optional[Union[Dict, str]]` | `None` | Proxy server settings used to construct `self.http_client`. | | `vertex_credentials_json` | `Optional[str]` | `None` | Filesystem path to Google Vertex AI credentials JSON. | | `memory_add_embedding_type` | `Optional[str]` | `None` | Embedding type variant for memory insertion actions. | | `memory_update_embedding_type` | `Optional[str]` | `None` | Embedding type variant for memory update actions. | | `memory_search_embedding_type` | `Optional[str]` | `None` | Embedding type variant for memory search queries. | | `output_dimensionality` | `Optional[str]` | `None` | Output dimensionality constraint for Gemini models. | | `lmstudio_base_url` | `Optional[str]` | `"http://localhost:1234/v1"` | LM Studio local server endpoint base URL. | | `aws_access_key_id` | `Optional[str]` | `None` | AWS access key identifier for Bedrock authentication. | | `aws_secret_access_key` | `Optional[str]` | `None` | AWS secret access key for Bedrock authentication. | | `aws_session_token` | `Optional[str]` | `None` | AWS session token for temporary credentials. | | `aws_region` | `Optional[str]` | `None` | AWS region; falls back to environment variable `AWS_REGION` or `"us-west-2"`. | Sources: [mem0/configs/embeddings/base.py:14-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/embeddings/base.py#L14-L43) > [!NOTE] > When `aws_region` is unassigned in the constructor call, the configuration resolver checks `os.environ.get("AWS_REGION")` before defaulting to `"us-west-2"`. Sources: [mem0/configs/embeddings/base.py:107-111](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/embeddings/base.py#L107-L111) ### Initialization and Client Resolution During `BaseEmbedderConfig.__init__`, attributes are assigned directly to instance variables while specific providers instantiate auxiliary objects. `http_client_proxies` is passed directly to `build_http_client` to establish `self.http_client`, `azure_kwargs` normalizes input dictionaries into `AzureConfig` instances, and `model_kwargs` defaults to an empty dictionary if omitted. Sources: [mem0/configs/embeddings/base.py:77-111](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/embeddings/base.py#L77-L111) > [!CAUTION] > Passing `azure_kwargs` as a raw dictionary requires correct nested keys matching `AzureConfig`; passing invalid keys or omitting required Azure parameters will cause instantiation failures during `AzureConfig` validation. Sources: [mem0/configs/embeddings/base.py:93-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/embeddings/base.py#L93-L94) ## OpenAI and Azure Embedding Integrations ### OpenAIEmbedding Adapter Mechanics The `OpenAIEmbedding` adapter implements vector generation via the `OpenAI` client SDK, defaulting to the `text-embedding-3-small` model with 1536 dimensions. Configuration resolution checks `api_key` against `self.config.api_key` or `OPENAI_API_KEY`, and resolves the base URL through `self.config.openai_base_url`, `OPENAI_API_BASE`, or `OPENAI_BASE_URL`, falling back to `https://api.openai.com/v1`. If `OPENAI_API_BASE` is detected in the environment, a `DeprecationWarning` is triggered directing users to `OPENAI_BASE_URL`. Sources: [mem0/embeddings/openai.py:11-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/openai.py#L11-L35) > [!WARNING] > The environment variable `OPENAI_API_BASE` is deprecated as of release 0.1.80 and will be removed; configurations must migrate to `OPENAI_BASE_URL`. Sources: [mem0/embeddings/openai.py:28-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/openai.py#L28-L33) Dimensions are conditionally passed to the API only when `embedding_dims` is explicitly configured by the user, preventing non-Matryoshka OpenAI-compatible backends such as vLLM or Voyage from rejecting requests due to an unsupported parameter. Both single `embed` and batch `embed_batch` methods replace newline characters with spaces in input texts, set `encoding_format` to `"float"`, and chunk batch inputs into maximum sizes of 100 to respect upstream API rate limits. Responses are sorted by index to maintain correct ordering, and a `ValueError` is raised if the returned count of embedding vectors does not match the input text count. Sources: [mem0/embeddings/openai.py:16-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/openai.py#L16-L80) ### AzureOpenAIEmbedding Adapter and Authentication The `AzureOpenAIEmbedding` adapter configures its client using parameters retrieved from `self.config.azure_kwargs` or environment variables: `EMBEDDING_AZURE_OPENAI_API_KEY`, `EMBEDDING_AZURE_DEPLOYMENT`, `EMBEDDING_AZURE_ENDPOINT`, and `EMBEDDING_AZURE_API_VERSION`. When `api_key` is unassigned, empty, or set to the literal placeholder `"your-api-key"`, the adapter authenticates via Azure Identity using `DefaultAzureCredential` and a bearer token provider scoped to `https://cognitiveservices.azure.com/.default`. Otherwise, token providers are omitted in favor of the provided API key. Sources: [mem0/embeddings/azure_openai.py:10-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/azure_openai.py#L10-L42) > [!IMPORTANT] > If `api_key` is explicitly set to `"your-api-key"`, it is treated as a placeholder and automatically overridden with `DefaultAzureCredential`. Sources: [mem0/embeddings/azure_openai.py:24-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/azure_openai.py#L24-L30) Similar to the standard OpenAI adapter, single and batch embedding methods strip newlines by replacing them with spaces and enforce a maximum batch size of 100 texts per API call. Response items are reordered using `sorted(response.data, key=lambda x: x.index)`, and length mismatches between incoming texts and returned embeddings trigger a `ValueError`. Sources: [mem0/embeddings/azure_openai.py:44-77](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/azure_openai.py#L44-L77) ## AWS Bedrock Embedding Implementation ### Bedrock Client Initialization and Request Serialization The `AWSBedrockEmbedding` class subclasses `EmbeddingBase` and initializes an AWS Bedrock runtime client via the `boto3` library. The model defaults to `amazon.titan-embed-text-v1` if not specified in the configuration. Credentials are resolved in order of precedence: first from explicit attributes on `self.config` (`aws_access_key_id`, `aws_secret_access_key`, and `aws_session_token`), falling back to the standard environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`. The AWS region defaults to `us-west-2` when `aws_region` is unset on the configuration. Sources: [mem0/embeddings/aws_bedrock.py:16-52](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/aws_bedrock.py#L16-L52) > [!NOTE] > `AWSBedrockEmbedding` explicitly honors a session token supplied via configuration (such as temporary credentials from STS assume-role workflows), falling back to the `AWS_SESSION_TOKEN` environment variable when the configuration value is empty. Sources: [mem0/embeddings/aws_bedrock.py:37-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/aws_bedrock.py#L37-L41) The adapter formats request payloads differently depending on the model provider extracted from the model prefix (`self.config.model.split(".")[0]`). When the provider is `cohere`, the payload sets `input_type` to `"search_document"` and passes texts under the `texts` key. For Amazon Titan and other providers, the text is assigned to `inputText`. Furthermore, if the model identifier contains `"v2"` and `embedding_dims` is configured, the `dimensions` field is included in the request body. Sources: [mem0/embeddings/aws_bedrock.py:63-78](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/aws_bedrock.py#L63-L78) | Provider / Model Pattern | Payload Field Key | Optional Configuration Keys | | :--- | :--- | :--- | | `cohere` | `texts` | `input_type` (`"search_document"`) | | Amazon & others (default) | `inputText` | `dimensions` (Titan Text Embeddings V2 when `embedding_dims` is set) | Sources: [mem0/embeddings/aws_bedrock.py:64-77](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/aws_bedrock.py#L64-L77) ### Model Invocation and Vector Normalization Model invocation occurs via `self.client.invoke_model`, passing the serialized JSON `body`, `modelId`, and specifying both `accept` and `contentType` as `"application/json"`. The binary response body is read and decoded from JSON. Depending on the model provider, vectors are extracted from either `response_body.get("embeddings")[0]` for Cohere or `response_body.get("embedding")` for Amazon and other providers. Any invocation errors are caught and raised as a `ValueError`. Sources: [mem0/embeddings/aws_bedrock.py:81-98](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/aws_bedrock.py#L81-L98) ```python def _normalize_vector(self, embeddings): """Normalize the embedding to a unit vector.""" emb = np.array(embeddings) norm_emb = emb / np.linalg.norm(emb) return norm_emb.tolist() ``` Sources: [mem0/embeddings/aws_bedrock.py:54-58](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/aws_bedrock.py#L54-L58) ## Google Gemini and Vertex AI ### GoogleGenAIEmbedding Generation Workflow The `GoogleGenAIEmbedding` adapter interacts with Google's Generative AI API using the `google-genai` client library. The class initializes with a default model of `models/gemini-embedding-001` and sets default embedding dimensions to `768`, preferring `self.config.output_dimensionality` or `self.config.embedding_dims`. API keys are resolved by checking `self.config.api_key` before falling back to the `GOOGLE_API_KEY` environment variable. ```python class GoogleGenAIEmbedding(EmbeddingBase): def __init__(self, config: Optional[BaseEmbedderConfig] = None): super().__init__(config) self.config.model = self.config.model or "models/gemini-embedding-001" self.config.embedding_dims = self.config.embedding_dims or self.config.output_dimensionality or 768 api_key = self.config.api_key or os.getenv("GOOGLE_API_KEY") self.client = genai.Client(api_key=api_key) ``` Sources: [mem0/embeddings/gemini.py:11-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/gemini.py#L11-L20) When embedding single strings, `embed()` normalizes newlines by replacing `\n` characters with spaces. It constructs an `EmbedContentConfig` object specifying `output_dimensionality` and invokes `self.client.models.embed_content`, extracting vector values from `response.embeddings[0].values`. ```python def embed(self, text, memory_action: Optional[Literal["add", "search", "update"]] = None): """ Get the embedding for the given text using Google Generative AI. Args: text (str): The text to embed. memory_action (optional): The type of embedding to use. Must be one of "add", "search", or "update". Defaults to None. Returns: list: The embedding vector. """ text = text.replace("\n", " ") # Create config for embedding parameters config = types.EmbedContentConfig(output_dimensionality=self.config.embedding_dims) # Call the embed_content method with the correct parameters response = self.client.models.embed_content(model=self.config.model, contents=text, config=config) return response.embeddings[0].values ``` Sources: [mem0/embeddings/gemini.py:22-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/gemini.py#L22-L40) Batch embeddings are processed in chunks of up to `100` texts using `embed_batch()`. If the total number of returned embeddings fails to match the count of input texts, a `ValueError` is raised. ```python def embed_batch(self, texts, memory_action="add"): if not texts: return [] config = types.EmbedContentConfig(output_dimensionality=self.config.embedding_dims) MAX_BATCH = 100 all_embeddings = [] for i in range(0, len(texts), MAX_BATCH): chunk = [t.replace("\n", " ") for t in texts[i : i + MAX_BATCH]] response = self.client.models.embed_content(model=self.config.model, contents=chunk, config=config) all_embeddings.extend(e.values for e in response.embeddings) if len(all_embeddings) != len(texts): raise ValueError( f"Gemini embed_batch() returned {len(all_embeddings)} embeddings for {len(texts)} texts " f"using model '{self.config.model}'" ) return all_embeddings ``` Sources: [mem0/embeddings/gemini.py:41-56](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/gemini.py#L41-L56) ### VertexAIEmbedding Enterprise Endpoints The `VertexAIEmbedding` adapter provides integration with GCP Vertex AI language models, defaulting to the `gemini-embedding-001` model and an embedding dimension size of `256`. It maps memory actions to distinct task types via `self.embedding_types`: `"add"` and `"update"` map to `RETRIEVAL_DOCUMENT` (or configured override keys), while `"search"` maps to `RETRIEVAL_QUERY`. ```python class VertexAIEmbedding(EmbeddingBase): def __init__(self, config: Optional[BaseEmbedderConfig] = None): super().__init__(config) self.config.model = self.config.model or "gemini-embedding-001" self.config.embedding_dims = self.config.embedding_dims or 256 self.embedding_types = { "add": self.config.memory_add_embedding_type or "RETRIEVAL_DOCUMENT", "update": self.config.memory_update_embedding_type or "RETRIEVAL_DOCUMENT", "search": self.config.memory_search_embedding_type or "RETRIEVAL_QUERY", } ``` Sources: [mem0/embeddings/vertexai.py:11-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/vertexai.py#L11-L22) ```python # Set up authentication using centralized GCP authenticator # This supports multiple authentication methods while preserving environment variable support try: GCPAuthenticator.setup_vertex_ai( service_account_json=getattr(self.config, 'google_service_account_json', None), credentials_path=self.config.vertex_credentials_json, project_id=getattr(self.config, 'google_project_id', None) ) except Exception: # Fall back to original behavior for backward compatibility credentials_path = self.config.vertex_credentials_json if credentials_path: os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = credentials_path elif not os.getenv("GOOGLE_APPLICATION_CREDENTIALS"): raise ValueError( "Google application credentials JSON is not provided. Please provide a valid JSON path or set the 'GOOGLE_APPLICATION_CREDENTIALS' environment variable." ) self.model = TextEmbeddingModel.from_pretrained(self.config.model) ``` Sources: [mem0/embeddings/vertexai.py:24-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/vertexai.py#L24-L43) > [!NOTE] > `VertexAIEmbedding` uses a centralized `GCPAuthenticator` to configure credentials via `google_service_account_json`, `vertex_credentials_json`, or `google_project_id`, falling back to standard `GOOGLE_APPLICATION_CREDENTIALS` environment variable checks. Sources: [mem0/embeddings/vertexai.py:24-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/vertexai.py#L24-L41) Single and batch text inputs are wrapped in `TextEmbeddingInput` objects paired with their respective task types and processed in batch chunks of up to `250` items. Unrecognized memory actions trigger an immediate `ValueError`. ```python def embed(self, text, memory_action: Optional[Literal["add", "search", "update"]] = None): """ Get the embedding for the given text using Vertex AI. Args: text (str): The text to embed. memory_action (optional): The type of embedding to use. Must be one of "add", "search", or "update". Defaults to None. Returns: list: The embedding vector. """ embedding_type = "SEMANTIC_SIMILARITY" if memory_action is not None: if memory_action not in self.embedding_types: raise ValueError(f"Invalid memory action: {memory_action}") embedding_type = self.embedding_types[memory_action] text_input = TextEmbeddingInput(text=text, task_type=embedding_type) embeddings = self.model.get_embeddings(texts=[text_input], output_dimensionality=self.config.embedding_dims) return embeddings[0].values def embed_batch(self, texts, memory_action="add"): if not texts: return [] embedding_type = "SEMANTIC_SIMILARITY" if memory_action is not None: if memory_action not in self.embedding_types: raise ValueError(f"Invalid memory action: {memory_action}") embedding_type = self.embedding_types[memory_action] all_embeddings = [] for i in range(0, len(texts), 250): chunk = texts[i : i + 250] inputs = [TextEmbeddingInput(text=t, task_type=embedding_type) for t in chunk] results = self.model.get_embeddings(texts=inputs, output_dimensionality=self.config.embedding_dims) all_embeddings.extend(r.values for r in results) if len(all_embeddings) != len(texts): raise ValueError( f"Vertex AI embed_batch() returned {len(all_embeddings)} embeddings for {len(texts)} texts" f" using model '{self.config.model}'" ) return all_embeddings ``` Sources: [mem0/embeddings/vertexai.py:44-85](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/vertexai.py#L44-L85) ## Local and Self-Hosted Embeddings ### Ollama, FastEmbed, and LMStudio Configurations The local embedding architecture supports three distinct execution backends: `OllamaEmbedding`, `FastEmbedEmbedding`, and `LMStudioEmbedding`. Each adapter inherits from `EmbeddingBase` and configures default local model names and vector dimensions upon initialization. | Adapter Class | Default Model | Default Dimensions | Client / Library | | :--- | :--- | :--- | :--- | | `OllamaEmbedding` | `nomic-embed-text` | `512` | `ollama.Client` | | `FastEmbedEmbedding` | `thenlper/gte-large` | Dynamic (`dense_model.embedding_size`) | `fastembed.TextEmbedding` (ONNX Runtime) | | `LMStudioEmbedding` | `nomic-ai/nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf` | `1536` | `openai.OpenAI` | Sources: [mem0/embeddings/ollama.py:12-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/ollama.py#L12-L19), [mem0/embeddings/fastembed.py:11-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/fastembed.py#L11-L20), [mem0/embeddings/lmstudio.py:9-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/lmstudio.py#L9-L17) ### Ollama Client Integration and Model Verification The `OllamaEmbedding` adapter initializes a local client using `self.config.ollama_base_url` and verifies model availability against running instances. It normalizes model specifiers by appending `:latest` when a colon is absent from the name. ```python @staticmethod def _normalize_model_name(name: str) -> str: return name if ":" in name else f"{name}:latest" def _ensure_model_exists(self): """ Ensure the specified model exists locally. If not, pull it from Ollama. """ local_models = self.client.list()["models"] target = self._normalize_model_name(self.config.model) if not any( self._normalize_model_name(model.get("name", "")) == target or self._normalize_model_name(model.get("model", "")) == target for model in local_models ): self.client.pull(self.config.model) ``` Sources: [mem0/embeddings/ollama.py:22-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/ollama.py#L22-L38) > [!WARNING] > If the normalized target model is missing from the local Ollama instance list, `_ensure_model_exists()` automatically issues a blocking `self.client.pull()` call for the model. Sources: [mem0/embeddings/ollama.py:26-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/ollama.py#L26-L38) Single and batch text embeddings are generated by calling `self.client.embed()`. If empty lists are returned or response counts fail to match input text lengths, a `ValueError` is raised. ```python def embed(self, text, memory_action: Optional[Literal["add", "search", "update"]] = None): """ Get the embedding for the given text using Ollama. Args: text (str): The text to embed. memory_action (optional): The type of embedding to use. Must be one of "add", "search", or "update". Defaults to None. Returns: list: The embedding vector. """ response = self.client.embed(model=self.config.model, input=text) embeddings = response.get("embeddings") or [] if not embeddings: raise ValueError(f"Ollama embed() returned no embeddings for model '{self.config.model}'") return embeddings[0] def embed_batch(self, texts, memory_action="add"): """Embed multiple texts in a single Ollama API call.""" if not texts: return [] response = self.client.embed(model=self.config.model, input=texts) embeddings = response.get("embeddings") or [] if len(embeddings) != len(texts): raise ValueError(f"Ollama embed() returned {len(embeddings)} embeddings for {len(texts)} texts using model '{self.config.model}'") return embeddings ``` Sources: [mem0/embeddings/ollama.py:39-63](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/ollama.py#L39-L63) ### FastEmbed and LMStudio In-Process Vectors The `FastEmbedEmbedding` adapter executes dense vector generation via the ONNX runtime through `TextEmbedding`. It replaces newline characters with spaces in input texts before generating embedding outputs. ```python def embed(self, text, memory_action: Optional[Literal["add", "search", "update"]] = None): """ Convert the text to embeddings using FastEmbed running in the Onnx runtime Args: text (str): The text to embed. memory_action (optional): The type of embedding to use. Must be one of "add", "search", or "update". Defaults to None. Returns: list: The embedding vector. """ text = text.replace("\n", " ") embeddings = list(self.dense_model.embed(text)) return embeddings[0].tolist() ``` Sources: [mem0/embeddings/fastembed.py:21-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/fastembed.py#L21-L32) The `LMStudioEmbedding` adapter communicates with local LM Studio instances through an OpenAI-compatible client interface, defaulting to an `api_key` value of `"lm-studio"`. ```python def embed(self, text, memory_action: Optional[Literal["add", "search", "update"]] = None): """ Get the embedding for the given text using LM Studio. Args: text (str): The text to embed. memory_action (optional): The type of embedding to use. Must be one of "add", "search", or "update". Defaults to None. Returns: list: The embedding vector. """ text = text.replace("\n", " ") return self.client.embeddings.create(input=[text], model=self.config.model).data[0].embedding ``` Sources: [mem0/embeddings/lmstudio.py:19-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/lmstudio.py#L19-L30) Batch inputs in `LMStudioEmbedding` have newline characters replaced, are sorted by their response index fields, and are validated to ensure output counts match the input batch length. ```python def embed_batch(self, texts, memory_action="add"): if not texts: return [] cleaned = [t.replace("\n", " ") for t in texts] response = self.client.embeddings.create(input=cleaned, model=self.config.model) sorted_data = sorted(response.data, key=lambda x: x.index) embeddings = [item.embedding for item in sorted_data] if len(embeddings) != len(texts): raise ValueError( f"LM Studio embed_batch() returned {len(embeddings)} embeddings for {len(texts)} texts" f" using model '{self.config.model}'" ) return embeddings ``` Sources: [mem0/embeddings/lmstudio.py:31-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/lmstudio.py#L31-L43) ## Hugging Face and Third-Party Bridges ### Hugging Face Transformer and TEI Integration The `HuggingFaceEmbedding` adapter supports two distinct operational modes determined by the presence of a base URL: local in-process execution using `SentenceTransformer` or remote API calls via an OpenAI-compatible client wrapper pointing to a Text Embeddings Inference (TEI) endpoint. When `huggingface_base_url` is configured, the client initializes using `self.config.api_key`, the `HUGGINGFACE_API_KEY` environment variable, or falls back to a default value of `"hf"`, defaulting the model identifier to `"tei"`. Without a base URL, the adapter loads local transformer weights using `SentenceTransformer` with a default model of `"multi-qa-MiniLM-L6-cos-v1"`, automatically resolving embedding dimensions via `model.get_sentence_embedding_dimension()`. ```python class HuggingFaceEmbedding(EmbeddingBase): def __init__(self, config: Optional[BaseEmbedderConfig] = None): super().__init__(config) if self.config.huggingface_base_url: api_key = self.config.api_key or os.getenv("HUGGINGFACE_API_KEY") or "hf" self.client = OpenAI(base_url=self.config.huggingface_base_url, api_key=api_key) self.config.model = self.config.model or "tei" else: self.config.model = self.config.model or "multi-qa-MiniLM-L6-cos-v1" self.model = SentenceTransformer(self.config.model, **self.config.model_kwargs) self.config.embedding_dims = self.config.embedding_dims or self.model.get_sentence_embedding_dimension() ``` Sources: [mem0/embeddings/huggingface.py:16-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/huggingface.py#L16-L30) During batch embedding operations, remote TEI responses sort items by their `index` attribute to align output vectors with input sequences. Both remote and local execution branches enforce validation checks that raise a `ValueError` if the returned embedding count diverges from the input text collection length. ```python def embed_batch(self, texts, memory_action="add"): if not texts: return [] if self.config.huggingface_base_url: response = self.client.embeddings.create(input=texts, model=self.config.model, **self.config.model_kwargs) sorted_data = sorted(response.data, key=lambda x: x.index) embeddings = [item.embedding for item in sorted_data] if len(embeddings) != len(texts): raise ValueError( f"HuggingFace embed_batch() returned {len(embeddings)} embeddings for {len(texts)} texts" f" using model '{self.config.model}'" ) return embeddings else: result = self.model.encode(texts, convert_to_numpy=True).tolist() if len(result) != len(texts): raise ValueError( f"HuggingFace embed_batch() returned {len(result)} embeddings for {len(texts)} texts" f" using model '{self.config.model}'" ) return result ``` Sources: [mem0/embeddings/huggingface.py:48-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/huggingface.py#L48-L68) > [!WARNING] > Library loggers for `transformers`, `sentence_transformers`, and `huggingface_hub` are explicitly restricted to the `WARNING` level during module initialization to suppress excessive console output. Sources: [mem0/embeddings/huggingface.py:11-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/huggingface.py#L11-L13) ### Together AI Embedding Adapter The `TogetherEmbedding` adapter interfaces with Together AI to generate dense vectors through an OpenAI-compatible SDK client. It defaults to the `"intfloat/multilingual-e5-large-instruct"` model with fixed embedding dimensions of `1024`, authenticating via `self.config.api_key` or the `TOGETHER_API_KEY` environment variable. ```python class TogetherEmbedding(EmbeddingBase): def __init__(self, config: Optional[BaseEmbedderConfig] = None): super().__init__(config) self.config.model = self.config.model or "intfloat/multilingual-e5-large-instruct" api_key = self.config.api_key or os.getenv("TOGETHER_API_KEY") self.config.embedding_dims = self.config.embedding_dims or 1024 self.client = Together(api_key=api_key) ``` Sources: [mem0/embeddings/together.py:10-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/together.py#L10-L18) Single text queries invoke `self.client.embeddings.create()`, returning the embedding vector from the first response data object. Batch requests sort response objects by their index before extracting vectors and validating length parity against the input array. ```python def embed(self, text, memory_action: Optional[Literal["add", "search", "update"]] = None): """ Get the embedding for the given text using OpenAI. Args: text (str): The text to embed. memory_action (optional): The type of embedding to use. Must be one of "add", "search", or "update". Defaults to None. Returns: list: The embedding vector. """ return self.client.embeddings.create(model=self.config.model, input=text).data[0].embedding def embed_batch(self, texts, memory_action="add"): if not texts: return [] response = self.client.embeddings.create(model=self.config.model, input=texts) sorted_data = sorted(response.data, key=lambda x: x.index) embeddings = [item.embedding for item in sorted_data] if len(embeddings) != len(texts): raise ValueError( f"Together embed_batch() returned {len(embeddings)} embeddings for {len(texts)} texts" f" using model '{self.config.model}'" ) return embeddings ``` Sources: [mem0/embeddings/together.py:19-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/together.py#L19-L43) ### LangChain Wrapper Bridge The `LangchainEmbedding` adapter wraps external LangChain embedding instances to conform to the core embedding interface. Initialization verifies that `config.model` is supplied and is a valid instance of the LangChain `Embeddings` base class, storing it internally as `self.langchain_model`. ```python class LangchainEmbedding(EmbeddingBase): def __init__(self, config: Optional[BaseEmbedderConfig] = None): super().__init__(config) if self.config.model is None: raise ValueError("`model` parameter is required") if not isinstance(self.config.model, Embeddings): raise ValueError("`model` must be an instance of Embeddings") self.langchain_model = self.config.model ``` Sources: [mem0/embeddings/langchain.py:12-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/langchain.py#L12-L23) Embedding generation delegates directly to the underlying LangChain model instance via `embed_query`. If the LangChain library is not present in the environment, an `ImportError` is raised during module import. ```python try: from langchain.embeddings.base import Embeddings except ImportError: raise ImportError("langchain is not installed. Please install it using `pip install langchain`") ``` Sources: [mem0/embeddings/langchain.py:6-9](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/langchain.py#L6-L9), [mem0/embeddings/langchain.py:24-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/langchain.py#L24-L35) ## Related - [TypeScript Embedding Adapters](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/typescript-embedding-adapters) - [Vector Store Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/vector-store-architecture) --- ## Technical docs: POST Reset all memories URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/endpoints/reset-memory ## Responses ## Try It --- ## Technical docs: TypeScript Embedding Adapters URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/typescript-embedding-adapters
Relevant source files The following files were used as context for generating this wiki page: - [mem0-ts/src/oss/src/utils/factory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts) - [mem0-ts/src/oss/src/embeddings/fastembed.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/fastembed.ts) - [mem0-ts/src/oss/src/embeddings/aws_bedrock.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/aws_bedrock.ts) - [integrations/vercel-ai-sdk/src/provider-response-provider.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/provider-response-provider.ts) - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [mem0-ts/src/oss/src/embeddings/vertexai.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/vertexai.ts) - [integrations/openclaw/providers.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/providers.ts) - [integrations/vercel-ai-sdk/src/mem0-provider-selector.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider-selector.ts) - [mem0-ts/src/oss/src/embeddings/lmstudio.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/lmstudio.ts) - [mem0-ts/src/oss/src/llms/aws_bedrock.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/aws_bedrock.ts) - [mem0-ts/src/oss/src/embeddings/huggingface.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/huggingface.ts) - [mem0-ts/src/oss/src/embeddings/langchain.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/langchain.ts) - [mem0/utils/factory.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/factory.py) - [mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts) - [mem0-ts/src/oss/src/embeddings/base.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/base.ts) - [integrations/openclaw/cli/oss-wizard.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts) - [mem0-ts/src/oss/src/embeddings/google.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/google.ts) - [mem0-ts/src/oss/src/embeddings/ollama.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/ollama.ts) - [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts) - [mem0-ts/src/oss/src/embeddings/openai.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/openai.ts) - [mem0-ts/src/oss/src/embeddings/azure.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/azure.ts) - [mem0-ts/src/oss/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/index.ts) - [mem0-ts/src/oss/src/embeddings/together.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/together.ts) - [integrations/vercel-ai-sdk/src/mem0-provider.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider.ts) - [mem0-ts/src/oss/src/vector_stores/qdrant.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/qdrant.ts) - [mem0-ts/src/oss/src/vector_stores/vertex_ai_vector_search.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/vertex_ai_vector_search.ts) - [mem0-ts/src/oss/src/vector_stores/weaviate.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts) - [mem0-ts/src/oss/src/llms/langchain.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/langchain.ts) - [mem0-ts/src/oss/src/llms/vllm.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/vllm.ts) - [mem0/embeddings/aws_bedrock.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/aws_bedrock.py)
## Overview TypeScript embedding adapters in Mem0 provide the foundational vector generation bridge across diverse AI providers, handling single and batch text transformations for memory storage and retrieval. Grounded in a core `Embedder` interface, these adapters resolve dynamically via the `EmbedderFactory` to support OpenAI, Azure OpenAI, Together AI, Hugging Face, LM Studio, AWS Bedrock, Google GenAI, Vertex AI, Ollama, FastEmbed, and LangChain instances. They address API divergence, credential management, dimensionality configuration, and optional peer dependency loading so that runtime vector generation remains modular and resilient. Sources: [mem0-ts/src/oss/src/utils/factory.ts:76-106](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L76-L106), [mem0-ts/src/oss/src/embeddings/fastembed.ts:24-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/fastembed.ts#L24-L96), [mem0-ts/src/oss/src/embeddings/aws_bedrock.ts:73-115](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/aws_bedrock.ts#L73-L115), [mem0-ts/src/oss/src/embeddings/vertexai.ts:41-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/vertexai.ts#L41-L94), [mem0-ts/src/oss/src/embeddings/base.ts:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/base.ts#L1-L10), [mem0-ts/src/oss/src/embeddings/openai.ts:5-56](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/openai.ts#L5-L56), [mem0-ts/src/oss/src/embeddings/azure.ts:5-61](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/azure.ts#L5-L61), [mem0-ts/src/oss/src/embeddings/huggingface.ts:18-77](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/huggingface.ts#L18-L77), [mem0-ts/src/oss/src/embeddings/langchain.ts:5-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/langchain.ts#L5-L28), [mem0-ts/src/oss/src/embeddings/ollama.ts:7-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/ollama.ts#L7-L22), [mem0-ts/src/oss/src/embeddings/together.ts:7-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/together.ts#L7-L30), [mem0-ts/src/oss/src/embeddings/lmstudio.ts:10-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/lmstudio.ts#L10-L19), [mem0-ts/src/oss/src/embeddings/google.ts:6-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/google.ts#L6-L16) ## Embedder interface and factory resolution ### Core Embedder contract The TypeScript package defines the fundamental contract for all vector generation backends through the `Embedder` interface. This contract specifies asynchronous methods for single text embedding and batch text transformations, both accepting an optional `memoryAction` parameter restricted to `"add"`, `"update"`, or `"search"`. ```typescript export interface Embedder { embed( text: string, memoryAction?: "add" | "update" | "search", ): Promise; embedBatch( texts: string[], memoryAction?: "add" | "update" | "search", ): Promise; } ``` Sources: [mem0-ts/src/oss/src/embeddings/base.ts:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/base.ts#L1-L10) > [!NOTE] > The `memoryAction` parameter on `embed` and `embedBatch` allows provider implementations to tailor tokenization, prefixing, or request routing based on whether a text is being ingested for storage or queried during retrieval. Sources: [mem0-ts/src/oss/src/embeddings/base.ts:2-9](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/base.ts#L2-L9) ### Exported adapter modules The core package exports all provider embeddings modules and utility resolvers from its entry point, making individual adapters and base interfaces available to consumers and factory routines. ```typescript export * from "./embeddings/base"; export * from "./embeddings/aws_bedrock"; export * from "./embeddings/huggingface"; export * from "./embeddings/openai"; export * from "./embeddings/ollama"; export * from "./embeddings/lmstudio"; export * from "./embeddings/together"; export * from "./embeddings/google"; export * from "./embeddings/azure"; export * from "./embeddings/langchain"; export * from "./embeddings/vertexai"; export * from "./embeddings/fastembed"; ``` Sources: [mem0-ts/src/oss/src/index.ts:4-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/index.ts#L4-L15) ## OpenAI compatible embedding adapters ### OpenAI and Azure OpenAI Adapters The `OpenAIEmbedder` class acts as the core implementation for OpenAI-compliant endpoints, leveraging the official `openai` SDK package. It accepts an `EmbeddingConfig` object, initializing an `OpenAI` client with `apiKey` and optional `baseURL` or `url`. The default model is `text-embedding-3-small`, and embedding dimensions can be explicitly configured via `embeddingDims`. For batch processing, it slices text inputs into chunks of maximum 100 items, sends parallel requests with `encoding_format: "float"`, and reconstructs the ordered list by sorting response data indices. ```typescript export class OpenAIEmbedder implements Embedder { private openai: OpenAI; private model: string; private embeddingDims: number | undefined; constructor(config: EmbeddingConfig) { this.openai = new OpenAI({ apiKey: config.apiKey, baseURL: config.baseURL || config.url, }); this.model = config.model || "text-embedding-3-small"; this.embeddingDims = config.embeddingDims; } async embed(text: string): Promise { const response = await this.openai.embeddings.create({ model: this.model, input: text, encoding_format: "float", ...(this.embeddingDims !== undefined && { dimensions: this.embeddingDims, }), }); return response.data[0].embedding; } async embedBatch(texts: string[]): Promise { const MAX_BATCH = 100; const allEmbeddings: number[][] = []; for (let i = 0; i < texts.length; i += MAX_BATCH) { const chunk = texts.slice(i, i + MAX_BATCH); const response = await this.openai.embeddings.create({ model: this.model, input: chunk, encoding_format: "float", ...(this.embeddingDims !== undefined && { dimensions: this.embeddingDims, }), }); allEmbeddings.push( ...response.data .sort((a, b) => a.index - b.index) .map((item) => item.embedding), ); } if (allEmbeddings.length !== texts.length) { throw new Error( `OpenAI embedBatch() returned ${allEmbeddings.length} embeddings for ${texts.length} texts using model '${this.model}'`, ); } return allEmbeddings; } ``` Sources: [mem0-ts/src/oss/src/embeddings/openai.ts:5-56](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/openai.ts#L5-L56) The `AzureOpenAIEmbedder` class extends this paradigm to Microsoft Azure OpenAI deployments by utilizing the specialized `AzureOpenAI` client class. It validates that both `config.apiKey` and `config.modelProperties?.endpoint` are provided during instantiation, throwing an error if either is missing. It parses endpoint properties and passes them along with the API key to the client constructor, defaulting to `text-embedding-3-small` when no model is specified. ```typescript export class AzureOpenAIEmbedder implements Embedder { private client: AzureOpenAI; private model: string; private embeddingDims: number | undefined; constructor(config: EmbeddingConfig) { if (!config.apiKey || !config.modelProperties?.endpoint) { throw new Error("Azure OpenAI requires both API key and endpoint"); } const { endpoint, ...rest } = config.modelProperties; this.client = new AzureOpenAI({ apiKey: config.apiKey, endpoint: endpoint as string, ...rest, }); this.model = config.model || "text-embedding-3-small"; this.embeddingDims = config.embeddingDims; } ``` Sources: [mem0-ts/src/oss/src/embeddings/azure.ts:5-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/azure.ts#L5-L24) > [!WARNING] > Both `OpenAIEmbedder` and `AzureOpenAIEmbedder` validate batch result lengths against input text lengths, throwing an explicit runtime error if the API returns a mismatched count during chunked embedding operations. Sources: [mem0-ts/src/oss/src/embeddings/openai.ts:50-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/openai.ts#L50-L54), [mem0-ts/src/oss/src/embeddings/azure.ts:55-59](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/azure.ts#L55-L59) ### Together AI, Hugging Face, and LM Studio Adapters Alternative OpenAI-compatible providers subclass or wrap the standard client architecture to accommodate provider-specific default URLs, authentication fallbacks, and payload normalizations. The `TogetherEmbedder` inherits from `OpenAIEmbedder`, strips out any configured `embeddingDims`, resolves API keys from configuration or `TOGETHER_API_KEY`, and falls back to `https://api.together.ai/v1` or `TOGETHER_API_BASE` with default model `intfloat/multilingual-e5-large-instruct`. ```typescript export class TogetherEmbedder extends OpenAIEmbedder { constructor(config: EmbeddingConfig) { const openAICompatibleConfig = { ...config }; delete openAICompatibleConfig.embeddingDims; const apiKey = config.apiKey || process.env.TOGETHER_API_KEY; if (!apiKey) { throw new Error("Together API key is required"); } super({ ...openAICompatibleConfig, apiKey, // Honor TOGETHER_API_BASE like the Together LLM does, so a user behind a // gateway who sets it gets it for embeddings too instead of silently // hitting api.together.ai. baseURL: config.baseURL || config.url || process.env.TOGETHER_API_BASE || DEFAULT_BASE_URL, model: config.model || DEFAULT_MODEL, }); } ``` Sources: [mem0-ts/src/oss/src/embeddings/together.ts:7-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/together.ts#L7-L30) The `HuggingFaceEmbedder` targets hosted Text Embeddings Inference (TEI) servers or OpenAI-compatible Hugging Face endpoints. It requires a base URL supplied via `config.huggingfaceBaseUrl`, `config.baseURL`, `config.url`, or `process.env.HUGGINGFACE_BASE_URL`, defaulting the model identifier to `tei`. ```typescript export class HuggingFaceEmbedder implements Embedder { private openai: OpenAI; private model: string; constructor(config: EmbeddingConfig) { const baseURL = config.huggingfaceBaseUrl || config.baseURL || config.url || process.env.HUGGINGFACE_BASE_URL; if (!baseURL) { throw new Error( "HuggingFace embedder requires an inference endpoint. Set " + "`huggingfaceBaseUrl` (or `baseURL`) in the embedder config, or the " + "HUGGINGFACE_BASE_URL environment variable (e.g. a TEI server at " + "http://localhost:8080/v1).", ); } this.openai = new OpenAI({ apiKey: config.apiKey || process.env.HUGGINGFACE_API_KEY || "hf", baseURL, }); // TEI ignores the model field; default mirrors the Python provider. this.model = config.model || "tei"; } ``` Sources: [mem0-ts/src/oss/src/embeddings/huggingface.ts:18-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/huggingface.ts#L18-L44) The `LMStudioEmbedder` provides local inference connectivity, defaulting to `http://localhost:1234/v1` for the base URL, `lm-studio` for the API key, and `nomic-ai/nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf` for the model. It automatically normalizes input texts by replacing newline characters with spaces prior to generating embeddings. ```typescript export class LMStudioEmbedder implements Embedder { private openai: OpenAI; private model: string; constructor(config: EmbeddingConfig) { const baseURL = config.baseURL ?? config.url ?? DEFAULT_BASE_URL; const apiKey = config.apiKey || DEFAULT_LMSTUDIO_API_KEY; this.openai = new OpenAI({ apiKey, baseURL: String(baseURL) }); this.model = config.model || DEFAULT_MODEL; } ``` Sources: [mem0-ts/src/oss/src/embeddings/lmstudio.ts:10-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/lmstudio.ts#L10-L19) | Adapter Class | Default Base URL | Default Model | Key Environment Variables | | :--- | :--- | :--- | :--- | | OpenAIEmbedder | OpenAI standard API | text-embedding-3-small | OPENAI_API_KEY | | AzureOpenAIEmbedder | Azure endpoint property | text-embedding-3-small | AZURE_OPENAI_API_KEY | | TogetherEmbedder | https://api.together.ai/v1 | intfloat/multilingual-e5-large-instruct | TOGETHER_API_KEY, TOGETHER_API_BASE | | HuggingFaceEmbedder | User-configured TEI URL | tei | HUGGINGFACE_BASE_URL, HUGGINGFACE_API_KEY | | LMStudioEmbedder | http://localhost:1234/v1 | nomic-ai/nomic-embed-text-v1.5-GGUF/... | None (defaults to lm-studio) | Sources: [mem0-ts/src/oss/src/embeddings/openai.ts:13-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/openai.ts#L13-L15), [mem0-ts/src/oss/src/embeddings/azure.ts:11-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/azure.ts#L11-L23), [mem0-ts/src/oss/src/embeddings/together.ts:4-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/together.ts#L4-L29), [mem0-ts/src/oss/src/embeddings/huggingface.ts:23-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/huggingface.ts#L23-L43), [mem0-ts/src/oss/src/embeddings/lmstudio.ts:5-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/lmstudio.ts#L5-L18) > [!TIP] > When integrating LM Studio locally, newline replacement in `LMStudioEmbedder` prevents formatting errors in local GGUF embedding runners that expect single-line payloads. Sources: [mem0-ts/src/oss/src/embeddings/lmstudio.ts:22-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/lmstudio.ts#L22-L23), [mem0-ts/src/oss/src/embeddings/lmstudio.ts:38-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/lmstudio.ts#L38-L40) ## Cloud managed embedding adapters ### AWS Bedrock Embedder The `AWSBedrockEmbedder` supports the Amazon Titan and Cohere embedding model families via `@aws-sdk/client-bedrock-runtime`. The package is lazily imported so that importing the module does not force the SDK dependency to be installed until an embedding request is executed. Credential validation enforces that if any explicit credential field (`awsAccessKeyId`, `awsSecretAccessKey`, or `awsSessionToken`) is supplied, both `awsAccessKeyId` and `awsSecretAccessKey` must be present; otherwise, an error is thrown to prevent silent fallback to the default credential chain. Omitting all credential fields allows the AWS SDK to resolve credentials from its default chain including environment variables, shared configuration files, SSO, or instance roles. Client initialization is memoized via `clientPromise` and cleared on rejection so transient failures do not permanently disable Bedrock for the embedder's lifetime. ```typescript * Supports the Amazon Titan and Cohere embedding model families. The * `@aws-sdk/client-bedrock-runtime` dependency is lazily imported so the * package stays optional: importing this module never forces the SDK to be * installed until a Bedrock embedder actually embeds something. */ export class AWSBedrockEmbedder implements Embedder { private readonly model: string; private readonly region: string; private readonly embeddingDims?: number; private readonly credentials?: BedrockCredentials; private clientPromise?: Promise<{ sdk: BedrockRuntimeModule; client: { send: (command: any) => Promise<{ body?: Uint8Array }> }; }>; constructor(config: EmbeddingConfig) { this.model = config.model || DEFAULT_MODEL; this.region = config.awsRegion || process.env.AWS_REGION || DEFAULT_REGION; this.embeddingDims = config.embeddingDims; const hasKeyPair = Boolean( config.awsAccessKeyId && config.awsSecretAccessKey, ); const hasAnyCredential = Boolean( config.awsAccessKeyId || config.awsSecretAccessKey || config.awsSessionToken, ); // Partially configured credentials would silently fall back to the default // chain, embedding under an identity the caller never chose. if (hasAnyCredential && !hasKeyPair) { throw new Error( "AWS Bedrock requires both awsAccessKeyId and awsSecretAccessKey when any explicit credential is configured. " + "Omit all credential fields to use the AWS default credential chain.", ); } // Leaving `credentials` unset lets the AWS SDK resolve them from its // default chain: environment, shared config, SSO, or the instance role. if (hasKeyPair) { this.credentials = { accessKeyId: config.awsAccessKeyId!, secretAccessKey: config.awsSecretAccessKey!, ...(config.awsSessionToken && { sessionToken: config.awsSessionToken }), }; } } ``` Sources: [mem0-ts/src/oss/src/embeddings/aws_bedrock.ts:68-115](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/aws_bedrock.ts#L68-L115) Request body construction distinguishes between Cohere and Titan model architectures. Cohere requests supply an `input_type` mapped from memory actions (`add`, `update`, or `search`), whereas Embed v4 models additionally support configurable output dimensions and embedding types. Titan models accept a single text input per invocation and restrict dimension configuration to Titan Text Embeddings V2 models. > [!WARNING] > Partially configured AWS credentials throw an explicit runtime error rather than falling back to the default chain under an unintended identity. Sources: [mem0-ts/src/oss/src/embeddings/aws_bedrock.ts:97-104](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/aws_bedrock.ts#L97-L104) ### Google GenAI Embedder The `GoogleEmbedder` interfaces with `@google/genai` to generate text embeddings using models such as `gemini-embedding-001`. The API key is resolved from configuration or the `GOOGLE_API_KEY` environment variable. The client instance is lazily instantiated via `loadPeer` upon the first call to `embed` or `embedBatch`. ```typescript export class GoogleEmbedder implements Embedder { private google!: GoogleGenAI; private model: string; private embeddingDims: number | undefined; private readonly apiKey: string | undefined; constructor(config: EmbeddingConfig) { this.apiKey = config.apiKey || process.env.GOOGLE_API_KEY; this.model = config.model || "gemini-embedding-001"; this.embeddingDims = config.embeddingDims; } private async ensureClient(): Promise { if (this.google) return; const sdk = await loadPeer( "@google/genai", "Google embedder", () => import("@google/genai"), ); this.google = new sdk.GoogleGenAI({ apiKey: this.apiKey }); } ``` Sources: [mem0-ts/src/oss/src/embeddings/google.ts:6-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/google.ts#L6-L26) Both single-item and batch embedding methods pass contents directly to the `embedContent` model endpoint. When `embeddingDims` is configured, it is transmitted as `outputDimensionality` within the request configuration block. ```typescript async embed(text: string): Promise { await this.ensureClient(); const response = await this.google.models.embedContent({ model: this.model, contents: text, ...(this.embeddingDims !== undefined && { config: { outputDimensionality: this.embeddingDims }, }), }); return response.embeddings![0].values!; } async embedBatch(texts: string[]): Promise { await this.ensureClient(); const response = await this.google.models.embedContent({ model: this.model, contents: texts, ...(this.embeddingDims !== undefined && { config: { outputDimensionality: this.embeddingDims }, }), }); return response.embeddings!.map((item) => item.values!); } ``` Sources: [mem0-ts/src/oss/src/embeddings/google.ts:28-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/google.ts#L28-L50) ### Google Cloud Vertex AI Embedder The `VertexAIEmbedder` communicates with Google Cloud Vertex AI via `@google-cloud/aiplatform`. It resolves project IDs from configuration or environment variables including `GCP_PROJECT_ID`, `GOOGLE_CLOUD_PROJECT`, and `GCLOUD_PROJECT`. Service account authentication supports file paths via `vertexCredentialsJson` or direct credentials via `googleServiceAccountJson`. ```typescript export class VertexAIEmbedder implements Embedder { private client: PredictionServiceClient | undefined; private helpers: AIPlatform["helpers"] | undefined; private initPromise: Promise | undefined; private clientOptions: ClientOptions; private model: string; private embeddingDims: number; private location: string; private projectId: string; private embeddingTypes: { add: string; update: string; search: string; }; constructor(config: VertexAIConfig) { this.model = config.model || "gemini-embedding-001"; this.embeddingDims = config.embeddingDims || 256; this.location = config.location || process.env.GCP_LOCATION || "us-central1"; // Left empty when unset: initClient() resolves it from Application Default // Credentials or the service account key file, the way the Python SDK does. this.projectId = config.googleProjectId || process.env.GCP_PROJECT_ID || process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT || ""; this.embeddingTypes = { add: config.memoryAddEmbeddingType || "RETRIEVAL_DOCUMENT", update: config.memoryUpdateEmbeddingType || "RETRIEVAL_DOCUMENT", search: config.memorySearchEmbeddingType || "RETRIEVAL_QUERY", }; const endpoint = `${this.location}-aiplatform.googleapis.com`; this.clientOptions = { apiEndpoint: endpoint }; if (config.vertexCredentialsJson) { this.clientOptions.keyFilename = config.vertexCredentialsJson; } else if (config.googleServiceAccountJson) { try { this.clientOptions.credentials = typeof config.googleServiceAccountJson === "string" ? JSON.parse(config.googleServiceAccountJson) : config.googleServiceAccountJson; } catch (err) { throw new Error( "Failed to parse googleServiceAccountJson: " + (err as Error).message, ); } } } ``` Sources: [mem0-ts/src/oss/src/embeddings/vertexai.ts:41-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/vertexai.ts#L41-L94) The embedder maps memory actions (`add`, `update`, `search`) to specific Vertex task types (`RETRIEVAL_DOCUMENT` or `RETRIEVAL_QUERY`). Batch requests automatically chunk input texts according to model limits: `gemini-embedding` models restrict requests to 1 instance per request, while other models accept up to 250 instances per request. ```typescript function maxInstancesPerRequest(model: string): number { return model.startsWith("gemini-embedding") ? 1 : 250; } ``` Sources: [mem0-ts/src/oss/src/embeddings/vertexai.ts:24-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/vertexai.ts#L24-L26) | Parameter / Option | AWS Bedrock (`AWSBedrockEmbedder`) | Google GenAI (`GoogleEmbedder`) | Vertex AI (`VertexAIEmbedder`) | | :--- | :--- | :--- | :--- | | Default Model | amazon.titan-embed-text-v1 | gemini-embedding-001 | gemini-embedding-001 | | Auth / Key Variables | awsAccessKeyId, awsSecretAccessKey | GOOGLE_API_KEY | googleServiceAccountJson, GCP_PROJECT_ID | | Peer Dependency | @aws-sdk/client-bedrock-runtime | @google/genai | @google-cloud/aiplatform | | Batch Support | Model-dependent payload handling | embedBatch method | Chunked via maxInstancesPerRequest | Sources: [mem0-ts/src/oss/src/embeddings/aws_bedrock.ts:68-115](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/aws_bedrock.ts#L68-L115), [mem0-ts/src/oss/src/embeddings/google.ts:1-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/google.ts#L1-L26), [mem0-ts/src/oss/src/embeddings/vertexai.ts:24-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/vertexai.ts#L24-L94) > [!TIP] > In `VertexAIEmbedder`, task types must reside directly on the instance object in snake_case (`task_type`), because Vertex silently ignores unknown fields placed inside parameters. Sources: [mem0-ts/src/oss/src/embeddings/vertexai.ts:138-147](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/vertexai.ts#L138-L147) ## Local and peer dependency loaders ### Ollama Embedding Adapter The `OllamaEmbedder` communicates with local Ollama instances to generate vector embeddings. It uses a dynamic peer dependency loader (`loadPeer`) to import the optional `ollama` package only when execution requires it, allowing consumers to omit the dependency if Ollama remains unused. ```typescript private async ensureClient(): Promise { if (this.ollama) return; const sdk = await loadPeer( "ollama", "Ollama embedder", () => import("ollama"), ); this.ollama = new sdk.Ollama({ host: this.host }); } ``` Sources: [mem0-ts/src/oss/src/embeddings/ollama.ts:24-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/ollama.ts#L24-L32) Upon instantiation, the embedder configures connection parameters and automatically verifies model availability. If the target model is absent from the local Ollama registry, it initiates a model pull action. ```typescript private async ensureModelExists(): Promise { if (this.initialized) { return true; } await this.ensureClient(); const local_models = await this.ollama.list(); const target = OllamaEmbedder.normalizeModelName(this.model); if ( !local_models.models.find( (m: any) => OllamaEmbedder.normalizeModelName(m.name) === target, ) ) { logger.info(`Pulling model ${this.model}...`); await this.ollama.pull({ model: this.model }); } this.initialized = true; return true; } ``` Sources: [mem0-ts/src/oss/src/embeddings/ollama.ts:64-81](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/ollama.ts#L64-L81) > [!NOTE] > `OllamaEmbedder` defensively coerces inputs to strings via `JSON.stringify(text)` when callers supply non-string values parsed from untrusted LLM outputs. Sources: [mem0-ts/src/oss/src/embeddings/ollama.ts:41-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/ollama.ts#L41-L42) ### FastEmbed Embedding Adapter The `FastEmbedEmbedder` runs local ONNX-based embedding models through the `fastembed` package. To prevent runtime download errors and provide immediate feedback, the constructor validates configuration against a hardcoded list of supported models before loading the peer dependency. ```typescript const SUPPORTED_MODELS = [ "fast-all-MiniLM-L6-v2", "fast-bge-base-en", "fast-bge-base-en-v1.5", "fast-bge-small-en", "fast-bge-small-en-v1.5", "fast-bge-small-zh-v1.5", "fast-multilingual-e5-large", ] as const; type FastEmbedModel = (typeof SUPPORTED_MODELS)[number]; const DEFAULT_MODEL: FastEmbedModel = "fast-bge-small-en-v1.5"; export class FastEmbedEmbedder implements Embedder { private readonly modelName: FastEmbedModel; private embeddingModel?: Promise; constructor(config: EmbeddingConfig) { if (typeof config.model === "string" && config.model.length > 0) { if (!SUPPORTED_MODELS.includes(config.model as FastEmbedModel)) { throw new Error( `Unsupported FastEmbed model "${config.model}". ` + `Supported models: ${SUPPORTED_MODELS.join(", ")}.`, ); } this.modelName = config.model as FastEmbedModel; } else { this.modelName = DEFAULT_MODEL; } } ``` Sources: [mem0-ts/src/oss/src/embeddings/fastembed.ts:12-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/fastembed.ts#L12-L40) | Supported Model Constant | Default Model | Peer Dependency | Input Normalization | | :--- | :--- | :--- | :--- | | fast-all-MiniLM-L6-v2, fast-bge-base-en, fast-bge-base-en-v1.5, fast-bge-small-en, fast-bge-small-en-v1.5, fast-bge-small-zh-v1.5, fast-multilingual-e5-large | fast-bge-small-en-v1.5 | fastembed | Replaces all newline characters (\n) with spaces | Sources: [mem0-ts/src/oss/src/embeddings/fastembed.ts:12-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/fastembed.ts#L12-L22), [mem0-ts/src/oss/src/embeddings/fastembed.ts:57-69](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/fastembed.ts#L57-L69) The embedder lazily initializes the `FlagEmbedding` instance via `loadPeer`, caching the resulting model promise to handle subsequent single and batch generation requests asynchronously. ```typescript private getEmbeddingModel(): Promise { if (!this.embeddingModel) { this.embeddingModel = this.initEmbeddingModel().catch((error) => { this.embeddingModel = undefined; throw error; }); } return this.embeddingModel; } /** * Lazily import the optional `fastembed` peer and initialize the model, so * consumers that never touch FastEmbed don't need it installed. */ private async initEmbeddingModel(): Promise { const sdk = await loadPeer( "fastembed", "FastEmbed embedder", () => import("fastembed"), ); return sdk.FlagEmbedding.init({ model: this.modelName }); } ``` Sources: [mem0-ts/src/oss/src/embeddings/fastembed.ts:42-65](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/fastembed.ts#L42-L65) ## LangChain bridge and ecosystem integrations ### Langchain Embedder Adapter The `LangchainEmbedder` bridges `@langchain/core/embeddings` models into the Mem0 embedder architecture. During construction, it validates that the provided configuration contains a valid LangChain embeddings instance equipped with both `embedQuery` and `embedDocuments` methods, throwing an error if the model object is missing or invalid. ```typescript constructor(config: EmbeddingConfig) { // Check if config.model is provided and is an object (the instance) if (!config.model || typeof config.model !== "object") { throw new Error( "Langchain embedder provider requires an initialized Langchain Embeddings instance passed via the 'model' field in the embedder config.", ); } // Basic check for embedding methods if ( typeof (config.model as any).embedQuery !== "function" || typeof (config.model as any).embedDocuments !== "function" ) { throw new Error( "Provided Langchain 'instance' in the 'model' field does not appear to be a valid Langchain Embeddings instance (missing embedQuery or embedDocuments method).", ); } this.embedderInstance = config.model as Embeddings; ``` Sources: [mem0-ts/src/oss/src/embeddings/langchain.ts:9-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/langchain.ts#L9-L25) The adapter extracts and retains an optional `batchSize` property directly from the underlying LangChain instance if present. For execution, it delegates single-text embedding tasks to `embedQuery` and batch collections to `embedDocuments`, allowing LangChain to manage internal chunking and rate limits. ```typescript // Store batch size if the instance has it (optional) this.batchSize = (this.embedderInstance as any).batchSize; } async embed(text: string): Promise { try { // Use embedQuery for single text embedding return await this.embedderInstance.embedQuery(text); } catch (error) { console.error("Error embedding text with Langchain Embedder:", error); throw error; } } async embedBatch(texts: string[]): Promise { try { // Use embedDocuments for batch embedding // Langchain's embedDocuments handles batching internally if needed/supported return await this.embedderInstance.embedDocuments(texts); } catch (error) { console.error("Error embedding batch with Langchain Embedder:", error); throw error; } } ``` Sources: [mem0-ts/src/oss/src/embeddings/langchain.ts:26-49](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/langchain.ts#L26-L49) > [!CAUTION] > `LangchainEmbedder` requires an already-initialized instance passed via `config.model`; supplying a configuration dictionary or class constructor without instantiation causes immediate validation failure. Sources: [mem0-ts/src/oss/src/embeddings/langchain.ts:11-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/embeddings/langchain.ts#L11-L15) ## Related - [Python Embedding Adapters](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/python-embedding-adapters) - [Vector Store Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/vector-store-architecture) --- ## Technical docs: GET list_keys URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/api-keys/list-keys ## Responses ## Try It --- ## Technical docs: Reranking Models URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/llm-providers/reranking-models
Relevant source files The following files were used as context for generating this wiki page: - [mem0-ts/src/oss/src/rerankers/cross_encoder.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cross_encoder.ts) - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [mem0/reranker/sentence_transformer_reranker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/sentence_transformer_reranker.py) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [mem0/reranker/cohere_reranker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/cohere_reranker.py) - [mem0/reranker/huggingface_reranker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/huggingface_reranker.py) - [mem0/reranker/llm_reranker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/llm_reranker.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [mem0-ts/src/oss/src/utils/factory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [mem0/reranker/zero_entropy_reranker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/zero_entropy_reranker.py) - [mem0-ts/src/oss/src/rerankers/cohere.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cohere.ts) - [mem0-ts/src/oss/src/rerankers/llm.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/llm.ts) - [mem0/reranker/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/__init__.py) - [mem0-ts/src/oss/src/rerankers/zeroentropy.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/zeroentropy.ts) - [mem0/utils/factory.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/factory.py) - [mem0/configs/rerankers/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/rerankers/__init__.py) - [integrations/hermes-plugin-mem0/_backend.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py) - [integrations/openclaw/recall.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts) - [mem0/utils/scoring.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/utils/scoring.py) - [mem0/configs/rerankers/config.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/rerankers/config.py) - [mem0/configs/rerankers/huggingface.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/rerankers/huggingface.py) - [mem0/configs/rerankers/sentence_transformer.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/rerankers/sentence_transformer.py) - [mem0/configs/rerankers/zero_entropy.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/rerankers/zero_entropy.py) - [mem0/configs/rerankers/cohere.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/rerankers/cohere.py) - [mem0/configs/rerankers/llm.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/rerankers/llm.py) - [mem0/vector_stores/vertex_ai_vector_search.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/vertex_ai_vector_search.py)
## Overview Reranking models refine initial memory retrieval candidates by scoring query-document relevance through specialized neural architectures, cloud APIs, and language model evaluators. Positioned downstream of vector and keyword searches across both Python and TypeScript runtimes, the reranking subsystem elevates high-precision matches before results are returned to agent pipelines. Sources: [mem0/reranker/cohere_reranker.py:16-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/cohere_reranker.py#L16-L36), [mem0/reranker/sentence_transformer_reranker.py:21-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/sentence_transformer_reranker.py#L21-L51), [mem0-ts/src/oss/src/utils/factory.ts:217-242](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L217-L242) ## Reranker Interface and Factory Integration ### TypeScript Reranker Implementation and Result Data Structures The [Typescript Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/typescript-core/typescript-core) defines the contract for search rerankers via the `Reranker` interface and the `RerankResult` data structure. Every concrete provider implementation — including `CrossEncoderReranker`, `CohereReranker`, `LLMReranker`, and `ZeroEntropyReranker` — implements an asynchronous `rerank` method accepting a query string, an array of candidate document strings, and an optional `topK` constraint. ```typescript export interface RerankResult { index: number; rerankScore: number; } export interface Reranker { rerank( query: string, documents: string[], topK?: number, ): Promise; } ``` Sources: [mem0-ts/src/oss/src/rerankers/cross_encoder.ts:2-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cross_encoder.ts#L2-L3), [mem0-ts/src/oss/src/rerankers/cohere.ts:2-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cohere.ts#L2-L3) Each reranking driver handles empty document collections by immediately returning an empty array without making external API calls or invoking local models. When operational failures occur during reranking, drivers execute safe fallback behavior: they catch the error, emit a warning via `console.warn`, assign a fallback relevance score (`0.0` for API and neural rerankers, `0.5` for prompt-based LLM scoring), preserve the original candidate indices, and slice the results to `topK` if specified. > [!WARNING] > Rerankers fall back to the original candidate order with neutral scores when runtime execution fails. Consumers should verify whether a returned score of `0.0` or `0.5` represents a genuine relevance metric or a fallback exception recovery path. Sources: [mem0-ts/src/oss/src/rerankers/cross_encoder.ts:57-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cross_encoder.ts#L57-L96), [mem0-ts/src/oss/src/rerankers/cohere.ts:58-86](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cohere.ts#L58-L86), [mem0-ts/src/oss/src/rerankers/llm.ts:37-56](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/llm.ts#L37-L56), [mem0-ts/src/oss/src/rerankers/zeroentropy.ts:54-83](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/zeroentropy.ts#L54-L83) ### Python Reranker Package Exports The Python search architecture organizes its reranking subsystem around the `BaseReranker` abstract base class and exposes concrete implementations through its package module initialization. Consumers import reranker drivers directly from the module namespace. | Reranker Class | Module File | Primary Function | | :--- | :--- | :--- | | `BaseReranker` | `mem0/reranker/base.py` | Abstract base class defining the standard rerank interface | | `CohereReranker` | `mem0/reranker/cohere_reranker.py` | Cloud-based reranking via the Cohere API client | | `HuggingFaceReranker` | `mem0/reranker/huggingface_reranker.py` | Local sequence classification via Hugging Face inference | | `LLMReranker` | `mem0/reranker/llm_reranker.py` | Prompt-based relevance scoring via language models | | `SentenceTransformerReranker` | `mem0/reranker/sentence_transformer_reranker.py` | Cross-encoder scoring using Sentence Transformers | | `ZeroEntropyReranker` | `mem0/reranker/zero_entropy_reranker.py` | ZeroEntropy API-driven relevance reranking | Sources: [mem0/reranker/__init__.py:5-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/__init__.py#L5-L18) ## Cohere API Reranking Driver ### Configuration Settings and Parameters The Cohere reranking driver is configured using dedicated parameter classes across both runtimes. In Python, `CohereRerankerConfig` inherits from `BaseRerankerConfig` and defines specific attributes for model selection, document text return behavior, and chunking limits. In TypeScript, corresponding options are supplied via the general `RerankerConfig` interface. | Parameter Name | Python Default | TypeScript Default | Description | | :--- | :--- | :--- | :--- | | `model` | `"rerank-v3.5"` | `"rerank-v3.5"` | The Cohere rerank model identifier | | `return_documents` | `False` | `false` | Whether to return the document texts in the response | | `max_chunks_per_doc` | `None` | `undefined` | Maximum number of chunks per document | | `top_k` / `topK` | `None` | `undefined` | Number of top documents to return | Sources: [mem0/configs/rerankers/cohere.py:8-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/rerankers/cohere.py#L8-L16), [mem0-ts/src/oss/src/rerankers/cohere.ts:5-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cohere.ts#L5-L28) ### Client Lifecycle and Peer Loading Initialization differs between runtimes regarding dependency management and connection handling. The Python driver checks `COHERE_AVAILABLE` during instantiation and immediately raises an `ImportError` if the `cohere` package is missing. It resolves the API key from `config.api_key` or falls back to the `COHERE_API_KEY` environment variable before instantiating `cohere.Client`. ```python if not COHERE_AVAILABLE: raise ImportError("cohere package is required for CohereReranker. Install with: pip install cohere") self.config = config self.api_key = config.api_key or os.getenv("COHERE_API_KEY") if not self.api_key: raise ValueError("Cohere API key is required. Set COHERE_API_KEY environment variable or pass api_key in config.") self.model = config.model self.client = cohere.Client(self.api_key) ``` Sources: [mem0/reranker/cohere_reranker.py:26-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/cohere_reranker.py#L26-L35) The TypeScript driver employs a lazy loading pattern via `loadPeer` to import the optional `cohere-ai` peer only when reranking is executed, avoiding mandatory dependency installation for consumers that do not use Cohere. ```typescript private async createClient(): Promise { const sdk = await loadPeer( "cohere-ai", "Cohere reranker", () => import("cohere-ai"), ); return new sdk.CohereClient({ token: this.apiKey }); } ``` Sources: [mem0-ts/src/oss/src/rerankers/cohere.ts:44-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cohere.ts#L44-L51) > [!WARNING] > Both client implementations require a valid Cohere API key. If `COHERE_API_KEY` is omitted from both the runtime configuration and the environment variables, initialization fails with a runtime error. Sources: [mem0/reranker/cohere_reranker.py:30-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/cohere_reranker.py#L30-L32), [mem0-ts/src/oss/src/rerankers/cohere.ts:17-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cohere.ts#L17-L22) ### Scoring Behavior and Fallback Execution When executing `rerank`, the Python driver extracts text content from incoming dictionary objects by inspecting keys in priority order: `'memory'`, `'text'`, `'content'`, or falling back to string conversion of the entire document. It calls `self.client.rerank` with `model`, `query`, `documents`, `top_n`, `return_documents`, and `max_chunks_per_doc`, then maps `result.relevance_score` to each document's `'rerank_score'` field. ```python response = self.client.rerank( model=self.model, query=query, documents=doc_texts, top_n=top_k or self.config.top_k or len(documents), return_documents=self.config.return_documents, max_chunks_per_doc=self.config.max_chunks_per_doc, ) ``` Sources: [mem0/reranker/cohere_reranker.py:65-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/cohere_reranker.py#L65-L73) If the remote Cohere API call raises an exception, both runtimes catch the error, log a warning, and execute fallback logic. They assign a relevance score of `0.0` to all documents while preserving their original relative ordering and applying any requested `top_k` slice. Sources: [mem0/reranker/cohere_reranker.py:84-93](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/cohere_reranker.py#L84-L93), [mem0-ts/src/oss/src/rerankers/cohere.ts:75-86](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cohere.ts#L75-L86) ## Hugging Face Cross-Encoder Scoring ### Configuration and Model Initialization The Hugging Face reranker configuration is defined by `HuggingFaceRerankerConfig`, which extends `BaseRerankerConfig` with model defaults, device placement, batch processing limits, and score normalization parameters. | Option | Type | Default | Description | | :--- | :--- | :--- | :--- | | `model` | `Optional[str]` | `"BAAI/bge-reranker-base"` | The Hugging Face model identifier to use for reranking | | `device` | `Optional[str]` | `None` | Device to run the model on (`'cpu'`, `'cuda'`, etc.) | | `batch_size` | `int` | `32` | Batch size for processing documents | | `max_length` | `int` | `512` | Maximum length for tokenization | | `normalize` | `bool` | `True` | Whether to normalize scores | Sources: [mem0/configs/rerankers/huggingface.py:7-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/rerankers/huggingface.py#L7-L17) In the Python runtime, `HuggingFaceReranker` checks if the `transformers` package is available at import time, raising an `ImportError` if it is missing. During initialization, it automatically selects `"cuda"` if PyTorch detects an available GPU, or falls back to `"cpu"`. It loads both the tokenizer and sequence classification model from the configured model ID, moves the model to the target device, and puts it into evaluation mode. ```python if self.config.device is None: self.device = "cuda" if torch.cuda.is_available() else "cpu" else: self.device = self.config.device self.tokenizer = AutoTokenizer.from_pretrained(self.config.model) self.model = AutoModelForSequenceClassification.from_pretrained(self.config.model) self.model.to(self.device) self.model.eval() ``` Sources: [mem0/reranker/huggingface_reranker.py:30-62](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/huggingface_reranker.py#L30-L62) The TypeScript implementation (`CrossEncoderReranker`) utilizes a lazy-loading pattern via `load()` to import `@huggingface/transformers` and its native ONNX runtime bindings only when a rerank operation executes. This prevents the ONNX native runtime from being pulled into every `new Memory()` instantiation, avoiding version collisions with FastEmbed. ```typescript private load() { if (!this.loaded) { this.loaded = (async () => { const { AutoModelForSequenceClassification, AutoTokenizer } = await import("@huggingface/transformers"); const options: any = {}; if (this.device) options.device = this.device; const model = await AutoModelForSequenceClassification.from_pretrained( this.modelId, options, ); const tokenizer = await AutoTokenizer.from_pretrained(this.modelId); return { model, tokenizer }; })(); } return this.loaded; } ``` Sources: [mem0-ts/src/oss/src/rerankers/cross_encoder.ts:29-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cross_encoder.ts#L29-L50) > [!NOTE] > In `CrossEncoderReranker`, configuration parameters `batchSize` and `showProgressBar` are accepted strictly for config parity with the Python SDK but operate as no-ops because memory searches rerank candidate sets in a single forward pass. Sources: [mem0-ts/src/oss/src/rerankers/cross_encoder.ts:12-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cross_encoder.ts#L12-L15) ### Logit Extraction and Score Normalization Cross-encoder models emit unbounded raw logits during inference. To convert these into interpretable relevance scores in the `[0, 1]` range without set-relative distortion, both implementations apply a sigmoid function (`1 / (1 + e^-x)`) when `normalize` is enabled. ```python @staticmethod def _normalize_scores(scores: List[float]) -> List[float]: if not scores: return [] arr = np.asarray(scores, dtype=float) return (1.0 / (1.0 + np.exp(-arr))).tolist() ``` Sources: [mem0/reranker/huggingface_reranker.py:63-81](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/huggingface_reranker.py#L63-L81) In TypeScript, logit rows are extracted from model execution outputs, checked for array structure, and mapped through a local `sigmoid` helper. ```typescript const sigmoid = (x: number) => 1 / (1 + Math.exp(-x)); ``` Sources: [mem0-ts/src/oss/src/rerankers/cross_encoder.ts:4-4](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cross_encoder.ts#L4-L4) ```typescript const { logits } = await model(inputs); const rows: unknown[] = logits.tolist(); const scored = rows.map((row, index) => { const logit = Array.isArray(row) ? (row[0] as number) : (row as number); return { index, rerankScore: this.normalize ? sigmoid(logit) : logit, }; }); ``` Sources: [mem0-ts/src/oss/src/rerankers/cross_encoder.ts:72-81](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/cross_encoder.ts#L72-L81) > [!WARNING] > Sigmoid normalization replaces legacy min-max scaling. Min-max scaling forced the lowest-ranked document to `0.0` and collapsed single-document or entirely tied score sets to zero, whereas per-document sigmoid scoring evaluates each item on its own individual merit. Sources: [mem0/reranker/huggingface_reranker.py:66-76](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/huggingface_reranker.py#L66-L76) ## Sentence Transformer Reranker Pipeline ### Configuration Options and Defaults The `SentenceTransformerReranker` relies on `SentenceTransformerRerankerConfig` for parameter initialization. If initialized with a dictionary or a generic `BaseRerankerConfig`, the pipeline maps fields to default values. | Field | Type | Default Value | Description | | :--- | :--- | :--- | :--- | | `provider` | `str` | `"sentence_transformer"` | Reranker provider identifier | | `model` | `Optional[str]` | `"cross-encoder/ms-marco-MiniLM-L-6-v2"` | Cross-encoder model name | | `device` | `Optional[str]` | `None` | Device specification (`'cpu'`, `'cuda'`, etc., auto-detected if `None`) | | `batch_size` | `int` | `32` | Batch size for processing documents | | `show_progress_bar` | `bool` | `False` | Whether to display a progress bar during inference | | `api_key` | `Optional[str]` | `None` | API key for authentication when applicable | | `top_k` | `Optional[int]` | `None` | Maximum number of top documents to return | ```python class SentenceTransformerRerankerConfig(BaseRerankerConfig): """ Configuration class for Sentence Transformer reranker-specific parameters. Inherits from BaseRerankerConfig and adds Sentence Transformer-specific settings. """ model: Optional[str] = Field(default="cross-encoder/ms-marco-MiniLM-L-6-v2", description="The cross-encoder model name to use") device: Optional[str] = Field(default=None, description="Device to run the model on ('cpu', 'cuda', etc.)") batch_size: int = Field(default=32, description="Batch size for processing documents") show_progress_bar: bool = Field(default=False, description="Whether to show progress bar during processing") ``` Sources: [mem0/configs/rerankers/sentence_transformer.py:7-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/rerankers/sentence_transformer.py#L7-L16) ```python # Convert to SentenceTransformerRerankerConfig if needed if isinstance(config, dict): config = SentenceTransformerRerankerConfig(**config) elif isinstance(config, BaseRerankerConfig) and not isinstance(config, SentenceTransformerRerankerConfig): # Convert BaseRerankerConfig to SentenceTransformerRerankerConfig with defaults config = SentenceTransformerRerankerConfig( provider=getattr(config, 'provider', 'sentence_transformer'), model=getattr(config, 'model', 'cross-encoder/ms-marco-MiniLM-L-6-v2'), api_key=getattr(config, 'api_key', None), top_k=getattr(config, 'top_k', None), device=None, # Will auto-detect batch_size=32, # Default show_progress_bar=False, # Default ) ``` Sources: [mem0/reranker/sentence_transformer_reranker.py:34-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/sentence_transformer_reranker.py#L34-L47) > [!CAUTION] > If the `sentence-transformers` package is not installed in the Python environment, `SentenceTransformerReranker.__init__` raises an immediate `ImportError` instructing the user to run `pip install sentence-transformers`. Sources: [mem0/reranker/sentence_transformer_reranker.py:31-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/sentence_transformer_reranker.py#L31-L33) ### Document Extraction and Scoring Workflow When `rerank` is invoked, the pipeline parses incoming documents by checking for specific text keys (`memory`, `text`, or `content`), falling back to string conversion if none are present. It pairs each extracted document text with the search query, passes the pairs into the `CrossEncoder` model predict method, and sorts the results in descending order by score. ```python # Extract text content for reranking doc_texts = [] for doc in documents: if 'memory' in doc: doc_texts.append(doc['memory']) elif 'text' in doc: doc_texts.append(doc['text']) elif 'content' in doc: doc_texts.append(doc['content']) else: doc_texts.append(str(doc)) try: # Create query-document pairs pairs = [[query, doc_text] for doc_text in doc_texts] scores = self.model.predict( pairs, batch_size=self.config.batch_size, show_progress_bar=self.config.show_progress_bar, ) if isinstance(scores, np.ndarray): scores = scores.tolist() # Combine documents with scores doc_score_pairs = list(zip(documents, scores)) # Sort by score (descending) doc_score_pairs.sort(key=lambda x: x[1], reverse=True) ``` Sources: [mem0/reranker/sentence_transformer_reranker.py:67-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/sentence_transformer_reranker.py#L67-L96) > [!NOTE] > If model prediction or score processing raises an exception, the reranker catches the error, logs a warning, and falls back to the original document order with a default `rerank_score` of `0.0`, applying any configured `top_k` slice to the fallback collection. Sources: [mem0/reranker/sentence_transformer_reranker.py:111-120](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/sentence_transformer_reranker.py#L111-L120) ## LLM Prompt-Based Evaluator ### Configuration and System Prompt Architecture The LLM-based reranker evaluates document relevance by leveraging language model generations guided by a strict system prompt and deterministic generation parameters. In Python, the `LLMRerankerConfig` class controls model initialization through parameters including provider, model name, API keys, temperature, maximum tokens, and optional nested LLM configurations. | Parameter | Type | Default Value | Description | | :--- | :--- | :--- | :--- | | `model` | `str` | `"gpt-5-mini"` | LLM model identifier for reranking evaluations | | `api_key` | `Optional[str]` | `None` | Authentication API key for the LLM provider | | `provider` | `str` | `"openai"` | LLM provider backend identifier | | `top_k` | `Optional[int]` | `None` | Maximum number of top documents returned after reranking | | `temperature` | `float` | `0.0` | Sampling temperature for deterministic scoring | | `max_tokens` | `int` | `100` | Maximum token limit for the LLM response | | `scoring_prompt` | `Optional[str]` | `None` | Deprecated custom prompt template (used as system message) | | `llm` | `Optional[Dict[str, Any]]` | `None` | Nested configuration dictionary with provider and config keys | ```python model: str = Field( default="gpt-5-mini", description="LLM model to use for reranking" ) api_key: Optional[str] = Field( default=None, description="API key for the LLM provider" ) provider: str = Field( default="openai", description="LLM provider (openai, anthropic, etc.)" ) top_k: Optional[int] = Field( default=None, description="Number of top documents to return after reranking" ) temperature: float = Field( default=0.0, description="Temperature for LLM generation" ) max_tokens: int = Field( default=100, description="Maximum tokens for LLM response" ) scoring_prompt: Optional[str] = Field( default=None, description="Custom prompt template for scoring documents" ) llm: Optional[Dict[str, Any]] = Field( default=None, description="Nested LLM configuration with 'provider' and 'config' keys. " "Overrides top-level provider/model/api_key when provided.", ) ``` Sources: [mem0/configs/rerankers/llm.py:22-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/rerankers/llm.py#L22-L54) > [!WARNING] > Setting a custom `scoring_prompt` triggers a `DeprecationWarning` advising that the configuration attribute is deprecated and will be removed in a future version, with the provided string automatically mapped to the system message. Sources: [mem0/reranker/llm_reranker.py:64-74](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/llm_reranker.py#L64-L74) Both Python and TypeScript implementations utilize a system prompt defining an explicit relevance scoring rubric ranging from `0.0` to `1.0`. ``` LLM Prompt-Based Relevance Scoring Rubric ┌───────────────────────────────────────────┐ │ 1.0 = Perfectly relevant & answers query │ │ 0.8-0.9 = Highly relevant with good info │ │ 0.6-0.7 = Moderately relevant │ │ 0.4-0.5 = Slightly relevant │ │ 0.0-0.3 = Not relevant │ └───────────────────────────────────────────┘ ``` Sources: [mem0/reranker/llm_reranker.py:78-89](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/llm_reranker.py#L78-L89), [mem0-ts/src/oss/src/rerankers/llm.ts:5-14](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/llm.ts#L5-L14) ### Response Parsing and Score Extraction To prevent prompt injection and flooding attacks, both runtimes truncate queries and document texts to a maximum length of `4000` characters using `_MAX_INPUT_LEN`. The user message combines the safe query and safe document into a structured prompt format before invoking the underlying LLM client. ```python safe_query = query[: self._MAX_INPUT_LEN] safe_doc = doc_text[: self._MAX_INPUT_LEN] user_message = f"Query: {safe_query}\n\nDocument: {safe_doc}" response = self.llm.generate_response( messages=[ {"role": "system", "content": self._system_prompt}, {"role": "user", "content": user_message}, ] ) ``` Sources: [mem0/reranker/llm_reranker.py:138-147](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/llm_reranker.py#L138-L147) ```typescript private async score(query: string, document: string): Promise { const safeQuery = query.slice(0, MAX_INPUT_LEN); const safeDoc = document.slice(0, MAX_INPUT_LEN); const userMessage = `Query: ${safeQuery}\n\nDocument: ${safeDoc}`; const response = await this.llm.generateResponse([ { role: "system", content: SYSTEM_PROMPT }, { role: "user", content: userMessage }, ]); ``` Sources: [mem0-ts/src/oss/src/rerankers/llm.ts:58-66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/llm.ts#L58-L66) The score extraction logic parses raw LLM output text by searching for decimal numbers first, falling back to integers, and clamping the resulting floating-point value between `0.0` and `1.0`. If parsing fails or no valid number matches, the system returns a fallback score of `0.5`. ```python def _extract_score(self, response_text: str) -> float: """Extract numerical score from LLM response.""" # Prefer a decimal, fall back to an integer, then clamp: out-of-range outputs # like "2.0"/"5" become 1.0 instead of being mis-parsed into a stray 0/1 digit. matches = re.findall(r'-?\d+\.\d+', response_text) or re.findall(r'-?\d+', response_text) if matches: score = float(matches[0]) return min(max(score, 0.0), 1.0) # Clamp between 0.0 and 1.0 # Fallback: return 0.5 if no valid score found return 0.5 ``` Sources: [mem0/reranker/llm_reranker.py:94-105](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/llm_reranker.py#L94-L105) ```typescript private extractScore(responseText: string): number { const matches = responseText.match(/-?\d+\.\d+/g) || responseText.match(/-?\d+/g); if (matches && matches.length > 0) { const score = parseFloat(matches[0]); return Math.min(Math.max(score, 0.0), 1.0); } return 0.5; } ``` Sources: [mem0-ts/src/oss/src/rerankers/llm.ts:76-86](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/llm.ts#L76-L86) > [!CAUTION] > Out-of-range numerical outputs such as `"2.0"` or `"5"` are clamped to `1.0` by the clipping function `min(max(score, 0.0), 1.0)`, preventing mis-parsed numbers from corrupting relative document rankings. Sources: [mem0/reranker/llm_reranker.py:96-102](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/llm_reranker.py#L96-L102), [mem0-ts/src/oss/src/rerankers/llm.ts:81-82](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/llm.ts#L81-L82) ## ZeroEntropy API Reranking Client ### ZeroEntropy Reranker Configuration and Initialization The ZeroEntropy reranker integration relies on the `zeroentropy` package across both Python and TypeScript runtimes, utilizing the default model identifier `"zerank-1"` when no explicit model is provided. In Python, `ZeroEntropyReranker` validates package availability via an `ImportError` guard and inspects configuration or environment variables for `ZERO_ENTROPY_API_KEY`. ```python def __init__(self, config): """ Initialize Zero Entropy reranker. Args: config: ZeroEntropyRerankerConfig object with configuration parameters """ if not ZERO_ENTROPY_AVAILABLE: raise ImportError("zeroentropy package is required for ZeroEntropyReranker. Install with: pip install zeroentropy") self.config = config self.api_key = config.api_key or os.getenv("ZERO_ENTROPY_API_KEY") if not self.api_key: raise ValueError("Zero Entropy API key is required. Set ZERO_ENTROPY_API_KEY environment variable or pass api_key in config.") self.model = config.model or "zerank-1" # Initialize Zero Entropy client if self.api_key: self.client = ZeroEntropy(api_key=self.api_key) else: self.client = ZeroEntropy() # Will use ZERO_ENTROPY_API_KEY from environment ``` Sources: [mem0/reranker/zero_entropy_reranker.py:19-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/zero_entropy_reranker.py#L19-L41) In the TypeScript runtime, `ZeroEntropyReranker` employs a lazy peer loading utility to dynamically import `zeroentropy` only upon the first execution, preventing unnecessary dependency requirements for consumers that do not use ZeroEntropy. ```typescript private async getClient(): Promise { if (this.clientInstance) return this.clientInstance; if (!this.clientPromise) { this.clientPromise = this.createClient(); } this.clientInstance = await this.clientPromise; return this.clientInstance; } private async createClient(): Promise { const sdk = await loadPeer( "zeroentropy", "ZeroEntropy reranker", () => import("zeroentropy"), ); return new sdk.ZeroEntropy({ apiKey: this.apiKey }); } ``` Sources: [mem0-ts/src/oss/src/rerankers/zeroentropy.ts:31-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/zeroentropy.ts#L31-L47) | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `model` | `str` | `"zerank-1"` | Model identifier for ZeroEntropy reranking (`zerank-1`, `zerank-1-small`) | | `api_key` | `Optional[str]` | `None` | Authentication API key checked against `ZERO_ENTROPY_API_KEY` | | `top_k` | `Optional[int]` | `None` | Maximum number of top documents returned after reranking | Sources: [mem0/configs/rerankers/zero_entropy.py:17-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/rerankers/zero_entropy.py#L17-L28) > [!WARNING] > Both Python and TypeScript implementations require a valid API key, raising a `ValueError` if neither `config.api_key` nor the `ZERO_ENTROPY_API_KEY` environment variable is defined. Sources: [mem0/reranker/zero_entropy_reranker.py:30-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/zero_entropy_reranker.py#L30-L32), [mem0-ts/src/oss/src/rerankers/zeroentropy.ts:15-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/zeroentropy.ts#L15-L20) ### Batch Relevance Scoring and Error Fallback During execution, `rerank` extracts document texts from keys including `'memory'`, `'text'`, or `'content'`, falling back to string conversion if none match. It then submits the query and text array to `client.models.rerank`, maps the resulting relevance scores to each document index, sorts candidates in descending order by score, and applies the `top_k` limit. ```python def rerank(self, query: str, documents: List[Dict[str, Any]], top_k: int = None) -> List[Dict[str, Any]]: """ Rerank documents using Zero Entropy's rerank API. Args: query: The search query documents: List of documents to rerank top_k: Number of top documents to return Returns: List of reranked documents with rerank_score """ if not documents: return documents # Extract text content for reranking doc_texts = [] for doc in documents: if 'memory' in doc: doc_texts.append(doc['memory']) elif 'text' in doc: doc_texts.append(doc['text']) elif 'content' in doc: doc_texts.append(doc['content']) else: doc_texts.append(str(doc)) try: # Call Zero Entropy rerank API response = self.client.models.rerank( model=self.model, query=query, documents=doc_texts, ) # Create reranked results reranked_docs = [] for result in response.results: original_doc = documents[result.index].copy() original_doc['rerank_score'] = result.relevance_score reranked_docs.append(original_doc) # Sort by relevance score in descending order reranked_docs.sort(key=lambda x: x['rerank_score'], reverse=True) # Apply top_k limit if top_k: reranked_docs = reranked_docs[:top_k] elif self.config.top_k: reranked_docs = reranked_docs[:self.config.top_k] return reranked_docs ``` Sources: [mem0/reranker/zero_entropy_reranker.py:42-93](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/zero_entropy_reranker.py#L42-L93) ```typescript async rerank( query: string, documents: string[], topK?: number, ): Promise { if (documents.length === 0) return []; try { const client = await this.getClient(); const response = await client.models.rerank({ model: this.model, query, documents, }); const scored: RerankResult[] = response.results.map((result: any) => ({ index: result.index, rerankScore: result.relevance_score, })); scored.sort((a, b) => b.rerankScore - a.rerankScore); const finalTopK = topK || this.topK; return finalTopK ? scored.slice(0, finalTopK) : scored; ``` Sources: [mem0-ts/src/oss/src/rerankers/zeroentropy.ts:49-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/zeroentropy.ts#L49-L71) > [!TIP] > If the ZeroEntropy API call throws an exception, both clients fail open by catching the error, logging a warning, and returning documents in their original order with a fallback `rerank_score` of `0.0` up to the requested `top_k` limit. Sources: [mem0/reranker/zero_entropy_reranker.py:95-104](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/reranker/zero_entropy_reranker.py#L95-L104), [mem0-ts/src/oss/src/rerankers/zeroentropy.ts:72-82](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/rerankers/zeroentropy.ts#L72-L82) ## Related - [Multi-Signal Retrieval](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/multi-signal-retrieval) - [Typescript Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/typescript-core/typescript-core) --- ## Technical docs: POST create_key URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/api-keys/create-key ## Request Body ## Responses ## Try It --- ## Technical docs: Vector Store Architecture URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/vector-store-architecture
Relevant source files The following files were used as context for generating this wiki page: - [mem0/vector_stores/base.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py) - [mem0-ts/src/oss/src/vector_stores/qdrant.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/qdrant.ts) - [mem0/vector_stores/mongodb.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/mongodb.py) - [mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts) - [mem0-ts/src/oss/src/vector_stores/databricks.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/databricks.ts) - [mem0-ts/src/oss/src/vector_stores/s3_vectors.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts) - [mem0-ts/src/oss/src/vector_stores/milvus.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts) - [mem0-ts/src/oss/src/vector_stores/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/memory.ts) - [mem0-ts/src/oss/src/vector_stores/base.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/base.ts) - [mem0/vector_stores/weaviate.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py) - [mem0/vector_stores/qdrant.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/qdrant.py) - [mem0-ts/src/oss/src/vector_stores/opensearch.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/opensearch.ts) - [mem0-ts/src/oss/src/vector_stores/mongodb.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/mongodb.ts) - [mem0-ts/src/oss/src/vector_stores/pgvector.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pgvector.ts) - [mem0/vector_stores/neptune_analytics.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/neptune_analytics.py) - [mem0-ts/src/oss/src/vector_stores/langchain.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/langchain.ts) - [mem0-ts/src/oss/src/vector_stores/cassandra.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/cassandra.ts) - [mem0-ts/src/oss/src/vector_stores/pinecone.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pinecone.ts) - [mem0/vector_stores/pgvector.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/pgvector.py) - [mem0-ts/src/oss/src/vector_stores/weaviate.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts) - [mem0-ts/src/oss/src/vector_stores/redis.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/redis.ts) - [mem0-ts/src/oss/src/vector_stores/elasticsearch.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/elasticsearch.ts) - [mem0-ts/src/oss/src/vector_stores/azure_ai_search.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/azure_ai_search.ts) - [mem0/vector_stores/azure_ai_search.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py) - [mem0-ts/src/oss/src/vector_stores/turbopuffer.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/turbopuffer.ts) - [mem0/vector_stores/langchain.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py) - [mem0-ts/src/oss/src/vector_stores/upstash_vector.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/upstash_vector.ts) - [mem0/vector_stores/opensearch.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/opensearch.py) - [mem0/vector_stores/milvus.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/milvus.py) - [mem0-ts/src/oss/src/utils/factory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts)
## Overview The vector store architecture provides a polymorphic data persistence and similarity retrieval layer that unifies disparate [Cloud Vector Backends](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/cloud-vector-backends), [Database Vector Stores](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/database-vector-stores), and [Embedded Vector Stores](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/embedded-vector-stores) under a shared abstract interface VectorStoreBase. By abstracting vector indexing, Approximate Nearest Neighbor (ANN) search, full-text keyword matching, and metadata filtering across engines such as Qdrant, Milvus, MongoDB, PostgreSQL, and LangChain, the system insulates higher-level memory management logic from vendor-specific driver idiosyncrasies and client query dialects. Sources: [mem0/vector_stores/base.py:4-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L4-L26), [mem0-ts/src/oss/src/vector_stores/base.ts:3-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/base.ts#L3-L33), [mem0-ts/src/oss/src/utils/factory.ts:154-214](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L154-L214) ## Dual-runtime vector store contract abstractions ### Python Abstract Base Class Surface The Python implementation defines `VectorStoreBase` as an abstract base class inheriting from `ABC`, establishing strict structural requirements across all concrete vector store implementations [mem0/vector_stores/base.py:1-4](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L1-L4). It enforces mandatory abstract methods for lifecycle operations, vector mutations, metadata storage, and collection management [mem0/vector_stores/base.py:5-67](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L5-L67). Sources: [mem0/vector_stores/base.py:1-67](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L1-L67) | Method | Parameters | Description | Sources | | :--- | :--- | :--- | :--- | | `create_col` | `name, vector_size, distance` | Create a new collection | [mem0/vector_stores/base.py:6-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L6-L8) | | `insert` | `vectors, payloads=None, ids=None` | Insert vectors into a collection | [mem0/vector_stores/base.py:11-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L11-L13) | | `search` | `query, vectors, top_k=5, filters=None` | Search for similar vectors | [mem0/vector_stores/base.py:16-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L16-L26) | | `delete` | `vector_id` | Delete a vector by ID | [mem0/vector_stores/base.py:29-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L29-L31) | | `update` | `vector_id, vector=None, payload=None` | Update a vector and its payload | [mem0/vector_stores/base.py:34-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L34-L36) | | `get` | `vector_id` | Retrieve a vector by ID | [mem0/vector_stores/base.py:39-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L39-L41) | | `list_cols` | None | List all collections | [mem0/vector_stores/base.py:44-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L44-L46) | | `delete_col` | None | Delete a collection | [mem0/vector_stores/base.py:49-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L49-L51) | | `col_info` | None | Get information about a collection | [mem0/vector_stores/base.py:54-56](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L54-L56) | | `list` | `filters=None, top_k=None` | List all memories | [mem0/vector_stores/base.py:59-61](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L59-L61) | | `reset` | None | Reset by deleting collection and recreating it | [mem0/vector_stores/base.py:64-66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L64-L66) | Sources: [mem0/vector_stores/base.py:5-66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L5-L66) > [!NOTE] > All implementations must normalize search similarity scores to a higher-is-better range of `[0, 1]`. Distance metrics must be explicitly converted: Cosine distance uses `score = max(0.0, 1.0 - distance)`, L2 distance uses `score = 1.0 / (1.0 + distance)`, and inner product preserves raw values. > Sources: [mem0/vector_stores/base.py:19-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L19-L25) Base methods with default implementations provide optional capabilities: `keyword_search` executes BM25 full-text queries and returns `None` unless overridden, while `search_batch` processes multiple queries sequentially via `search` unless a provider supplies native batch execution [mem0/vector_stores/base.py:68-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L68-L100). Sources: [mem0/vector_stores/base.py:68-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L68-L100) ### TypeScript Interface Contract The TypeScript client SDK exposes an asynchronous `VectorStore` interface defining equivalent persistence and retrieval primitives mapped to Promise-based signatures [mem0-ts/src/oss/src/vector_stores/base.ts:3-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/base.ts#L3-L33). ```typescript export interface VectorStore { insert( vectors: number[][], ids: string[], payloads: Record[], ): Promise; search( query: number[], topK?: number, filters?: SearchFilters, ): Promise; keywordSearch?( query: string, topK?: number, filters?: SearchFilters, ): Promise; get(vectorId: string): Promise; update( vectorId: string, vector: number[], payload: Record, ): Promise; delete(vectorId: string): Promise; deleteCol(): Promise; list( filters?: SearchFilters, topK?: number, ): Promise<[VectorStoreResult[], number]>; getUserId(): Promise; setUserId(userId: string): Promise; initialize(): Promise; ``` Sources: [mem0-ts/src/oss/src/vector_stores/base.ts:3-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/base.ts#L3-L33) > [!IMPORTANT] > The TypeScript contract models optional capabilities through optional interface members (`keywordSearch?`), whereas the Python runtime relies on base class method defaults returning `None`. > Sources: [mem0/vector_stores/base.py:68-83](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L68-L83), [mem0-ts/src/oss/src/vector_stores/base.ts:14-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/base.ts#L14-L18) ## Factory instantiation and runtime resolution ### VectorStoreFactory Provider Mapping The `VectorStoreFactory` class resolves runtime configuration strings into concrete vector store instances via a case-insensitive `switch` statement over the provider identifier [mem0-ts/src/oss/src/utils/factory.ts:154-214](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L154-L214). Sources: [mem0-ts/src/oss/src/utils/factory.ts:154-214](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L154-L214) | Provider Identifier(s) | Concrete Class | Sources | | :--- | :--- | :--- | | `memory` | `MemoryVectorStore` | [mem0-ts/src/oss/src/utils/factory.ts:157-158](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L157-L158) | | `baidu` | `BaiduDB` | [mem0-ts/src/oss/src/utils/factory.ts:159-160](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L159-L160) | | `qdrant` | `Qdrant` | [mem0-ts/src/oss/src/utils/factory.ts:161-162](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L161-L162) | | `chroma` | `ChromaDB` | [mem0-ts/src/oss/src/utils/factory.ts:163-164](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L163-L164) | | `redis` | `RedisDB` | [mem0-ts/src/oss/src/utils/factory.ts:165-166](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L165-L166) | | `valkey` | `ValkeyDB` | [mem0-ts/src/oss/src/utils/factory.ts:167-168](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L167-L168) | | `supabase` | `SupabaseDB` | [mem0-ts/src/oss/src/utils/factory.ts:169-170](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L169-L170) | | `langchain` | `LangchainVectorStore` | [mem0-ts/src/oss/src/utils/factory.ts:171-172](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L171-L172) | | `vectorize` | `VectorizeDB` | [mem0-ts/src/oss/src/utils/factory.ts:173-174](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L173-L174) | | `azure-ai-search` | `AzureAISearch` | [mem0-ts/src/oss/src/utils/factory.ts:175-176](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L175-L176) | | `vertex_ai_vector_search` | `VertexAIVectorSearch` | [mem0-ts/src/oss/src/utils/factory.ts:177-178](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L177-L178) | | `pgvector` | `PGVector` | [mem0-ts/src/oss/src/utils/factory.ts:179-180](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L179-L180) | | `databricks` | `DatabricksVectorStore` | [mem0-ts/src/oss/src/utils/factory.ts:181-182](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L181-L182) | | `neptune`, `neptune-analytics` | `NeptuneAnalyticsVectorStore` | [mem0-ts/src/oss/src/utils/factory.ts:183-185](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L183-L185) | | `elasticsearch` | `ElasticsearchDB` | [mem0-ts/src/oss/src/utils/factory.ts:186-187](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L186-L187) | | `opensearch` | `OpenSearchDB` | [mem0-ts/src/oss/src/utils/factory.ts:188-189](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L188-L189) | | `upstash_vector` | `UpstashVector` | [mem0-ts/src/oss/src/utils/factory.ts:190-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L190-L191) | | `azure_mysql` | `AzureMySQLDB` | [mem0-ts/src/oss/src/utils/factory.ts:192-193](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L192-L193) | | `cassandra` | `CassandraDB` | [mem0-ts/src/oss/src/utils/factory.ts:194-195](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L194-L195) | | `pinecone` | `PineconeDB` | [mem0-ts/src/oss/src/utils/factory.ts:196-197](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L196-L197) | | `s3-vectors`, `s3_vectors` | `S3Vectors` | [mem0-ts/src/oss/src/utils/factory.ts:198-200](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L198-L200) | | `turbopuffer` | `TurbopufferDB` | [mem0-ts/src/oss/src/utils/factory.ts:201-202](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L201-L202) | | `milvus` | `Milvus` | [mem0-ts/src/oss/src/utils/factory.ts:203-204](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L203-L204) | | `mongodb` | `MongoDB` | [mem0-ts/src/oss/src/utils/factory.ts:205-206](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L205-L206) | | `weaviate` | `WeaviateDB` | [mem0-ts/src/oss/src/utils/factory.ts:207-208](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L207-L208) | | `oracledb` | `OracleAIVectorSearch` | [mem0-ts/src/oss/src/utils/factory.ts:209-210](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L209-L210) | Sources: [mem0-ts/src/oss/src/utils/factory.ts:154-214](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L154-L214) > [!WARNING] > Provider matching normalizes input keys via `provider.toLowerCase()` but throws an explicit runtime `Error` containing `Unsupported vector store provider: ${provider}` if no matching case or alias is found. > Sources: [mem0-ts/src/oss/src/utils/factory.ts:155-213](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/factory.ts#L155-L213) ## Normalized payload and result structures ### Document Schema and Payload Serialization The document representation layer standardizes how vector entries map to backend fields [mem0/vector_stores/azure_ai_search.py:164-170](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L164-L170). In backends such as `AzureAISearch`, metadata payloads are serialized into JSON strings alongside explicit filterable dimensions like `user_id`, `run_id`, and `agent_id` extracted directly from the payload dictionary [mem0/vector_stores/azure_ai_search.py:137-147](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L137-L147), [mem0/vector_stores/azure_ai_search.py:164-170](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L164-L170). Sources: [mem0/vector_stores/azure_ai_search.py:137-170](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L137-L170) ```python class OutputData(BaseModel): id: Optional[str] score: Optional[float] payload: Optional[dict] ``` Sources: [mem0/vector_stores/azure_ai_search.py:37-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L37-L41) > [!NOTE] > `OutputData` models represent query hits across backends, standardizing fields into nullable `id`, `score`, and `payload` attributes [mem0/vector_stores/azure_ai_search.py:37-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L37-L41). > Sources: [mem0/vector_stores/azure_ai_search.py:37-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L37-L41) ### Distance-to-Similarity Conversion Rules All Python implementations of `VectorStoreBase` must return normalized similarity scores within the range `[0, 1]`, where higher values indicate greater similarity [mem0/vector_stores/base.py:19-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L19-L20). When a backend natively uses distance metrics, the score must be converted according to explicit mathematical transformations before returning [mem0/vector_stores/base.py:21-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L21-L25). Sources: [mem0/vector_stores/base.py:19-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L19-L25) | Metric Type | Conversion Formula / Rule | Score Range / Behavior | | :--- | :--- | :--- | | **Cosine distance** | `score = max(0.0, 1.0 - distance)` | Clamped between `0.0` and `1.0` | | **L2 distance** | `score = 1.0 / (1.0 + distance)` | Inverted scale, higher is closer | | **Inner product** | `score = value` | Preserved directly as higher is better | Sources: [mem0/vector_stores/base.py:21-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/base.py#L21-L25) ## Query filter DSL translation strategies ### Filter Expression Generation Vector store backends translate incoming `SearchFilters` dictionaries into native query language expressions [mem0-ts/src/oss/src/vector_stores/milvus.ts:245-259](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts#L245-L259), [mem0/vector_stores/azure_ai_search.py:195-213](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L195-L213). In the TypeScript `Milvus` provider, metadata keys are assembled into string literal comparisons formatted as `(metadata["${key}"] == "${escaped}")` for strings, or `(metadata["${key}"] == ${value})` for numbers and booleans, joined by ` and ` [mem0-ts/src/oss/src/vector_stores/milvus.ts:248-258](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts#L248-L258). Sources: [mem0-ts/src/oss/src/vector_stores/milvus.ts:245-259](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts#L245-L259) ```typescript if (typeof value === "string") { // Escape backslashes before quotes so a value can't break out of the // string literal (order matters, exactly as in the Python provider). const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); operands.push(`(metadata["${key}"] == "${escaped}")`); } else if (typeof value === "number" || typeof value === "boolean") { operands.push(`(metadata["${key}"] == ${value})`); } else { throw new Error( `Filter value for ${JSON.stringify(key)} must be a string, number, or boolean, got ${typeof value}`, ); } } return operands.length > 0 ? operands.join(" and ") : undefined; } ``` Sources: [mem0-ts/src/oss/src/vector_stores/milvus.ts:245-259](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts#L245-L259) Similarly, the Python `AzureAISearch` vector store translates filter keys by sanitizing identifiers and evaluating data types to construct OData filter expressions [mem0/vector_stores/azure_ai_search.py:192-213](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L192-L213). String values have single quotes escaped as `''`, booleans are converted via lowercase string representation, and numbers are appended directly before being joined with ` and ` [mem0/vector_stores/azure_ai_search.py:199-212](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L199-L212). Sources: [mem0/vector_stores/azure_ai_search.py:192-213](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L192-L213) ```python def _build_filter_expression(self, filters): filter_conditions = [] for key, value in filters.items(): safe_key = self._sanitize_key(key) if isinstance(value, str): safe_value = value.replace("'", "''") condition = f"{safe_key} eq '{safe_value}'" elif isinstance(value, bool): condition = f"{safe_key} eq {str(value).lower()}" elif isinstance(value, (int, float)): condition = f"{safe_key} eq {value}" else: raise ValueError( f"Filter value for {key!r} must be str, int, float, or bool, " f"got {type(value).__name__}" ) filter_conditions.append(condition) filter_expression = " and ".join(filter_conditions) return filter_expression ``` Sources: [mem0/vector_stores/azure_ai_search.py:195-213](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L195-L213) ### Filter Translation Mapping | Backend Provider | String Format | Boolean Format | Number Format | Operator Join | | :--- | :--- | :--- | :--- | :--- | | **Milvus** (TypeScript) | `(metadata["key"] == "escaped")` | `(metadata["key"] == true)` | `(metadata["key"] == 123)` | ` and ` | | **Azure AI Search** (Python) | `key eq 'value'` | `key eq true` | `key eq 123` | ` and ` | Sources: [mem0-ts/src/oss/src/vector_stores/milvus.ts:245-259](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts#L245-L259), [mem0/vector_stores/azure_ai_search.py:195-213](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L195-L213) > [!WARNING] > Filter value translation logic strictly validates input types; unsupported types trigger an explicit runtime `Error` in Milvus or a `ValueError` in Azure AI Search if values do not match allowed primitives. > Sources: [mem0-ts/src/oss/src/vector_stores/milvus.ts:253-256](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts#L253-L256), [mem0/vector_stores/azure_ai_search.py:207-210](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L207-L210) ## LangChain bridge and adapter architecture ### Client Validation and Embedding Fallbacks The LangChain bridge allows external vector stores to integrate with both runtimes. In TypeScript, `LangchainVectorStore` validates that a provided client object implements `addVectors` and `similaritySearchVectorWithScore` [mem0-ts/src/oss/src/vector_stores/langchain.ts:16-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/langchain.ts#L16-L30). In Python, the `Langchain` class inspects the client interface to check for `add_embeddings`; if absent, it falls back to calling `add_texts` using data extracted from payloads [mem0/vector_stores/langchain.py:95-103](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py#L95-L103). Sources: [mem0-ts/src/oss/src/vector_stores/langchain.ts:16-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/langchain.ts#L16-L30), [mem0/vector_stores/langchain.py:95-103](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py#L95-L103) ```typescript constructor(config: LangchainStoreConfig) { if (!config.client || typeof config.client !== "object") { throw new Error( "Langchain vector store provider requires an initialized Langchain VectorStore instance passed via the 'client' field.", ); } // Basic checks for core methods if ( typeof config.client.addVectors !== "function" || typeof config.client.similaritySearchVectorWithScore !== "function" ) { throw new Error( "Provided Langchain 'client' does not appear to be a valid Langchain VectorStore (missing addVectors or similaritySearchVectorWithScore method).", ); } ``` Sources: [mem0-ts/src/oss/src/vector_stores/langchain.ts:16-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/langchain.ts#L16-L30) > [!WARNING] > Initializing `LangchainVectorStore` in TypeScript throws an explicit `Error` if `config.client` is missing or omits required methods like `addVectors` or `similaritySearchVectorWithScore`. > Sources: [mem0-ts/src/oss/src/vector_stores/langchain.ts:17-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/langchain.ts#L17-L30) ### Scored Search Method Resolution The Python adapter iterates through a ranked priority list of methods to retrieve document-score pairs from diverse underlying stores [mem0/vector_stores/langchain.py:27-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py#L27-L31), [mem0/vector_stores/langchain.py:114-129](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py#L114-L129). Sources: [mem0/vector_stores/langchain.py:27-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py#L27-L31), [mem0/vector_stores/langchain.py:114-129](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py#L114-L129) | Method Name | Target Store Implementation | | :--- | :--- | | `similarity_search_by_vector_with_relevance_scores` | Chroma | | `similarity_search_with_score_by_vector` | FAISS, Qdrant | | `similarity_search_by_vector_with_score` | Pinecone, YDB | Sources: [mem0/vector_stores/langchain.py:27-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py#L27-L31), [mem0/vector_stores/langchain.py:114-129](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py#L114-L129) > [!NOTE] > If none of the scored vector methods are found on the LangChain client instance, search execution falls back to `similarity_search_by_vector`, assigning a default score of `1.0` so subsequent rankers do not receive `None`. > Sources: [mem0/vector_stores/langchain.py:131-141](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py#L131-L141) ## Input sanitization and injection prevention ### Identifier and Payload Escaping Mechanics Vector backends that expose query languages or dynamic shell interfaces implement strict regex validations and string escaping routines to prevent injection attacks. In Databricks, identifiers are validated against `SAFE_IDENTIFIER_RE` (`/^[A-Za-z_][A-Za-z0-9_]{0,127}$/`), and SQL values are serialized with backslashes and single quotes escaped prior to literal interpolation [mem0-ts/src/oss/src/vector_stores/databricks.ts:5-154](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/databricks.ts#L5-L154). Similarly, Milvus escapes backslashes and double quotes when constructing string filter operands [mem0-ts/src/oss/src/vector_stores/milvus.ts:245-249](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts#L245-L249). Sources: [mem0-ts/src/oss/src/vector_stores/databricks.ts:5-154](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/databricks.ts#L5-L154), [mem0-ts/src/oss/src/vector_stores/milvus.ts:245-249](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts#L245-L249) ```typescript const SAFE_IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/; const DEFAULT_PAGE_SIZE = 100; const MAX_QUERY_RESULTS = 10_000; const MAX_FULL_TEXT_RESULTS = 200; const DEFAULT_SYNC_POLL_INTERVAL_MS = 1000; const DEFAULT_SYNC_TIMEOUT_MS = 5 * 60 * 1000; ``` Sources: [mem0-ts/src/oss/src/vector_stores/databricks.ts:5-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/databricks.ts#L5-L10) > [!CAUTION] > Databricks string serialization handles backslashes before doubling quotes so that literal escape characters do not prematurely close SQL string boundaries or corrupt round-trip data. > Sources: [mem0-ts/src/oss/src/vector_stores/databricks.ts:149-154](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/databricks.ts#L149-L154) ### Filter Key Sanitization and Type Enforcement Query filter keys and values undergo rigorous validation across search integrations to reject malformed payloads before execution. Elasticsearch evaluates filter keys against `SAFE_FILTER_KEY` (`/^[a-zA-Z_][a-zA-Z0-9_]*$/`) and verifies that scalar values are restricted to strings, numbers, or booleans [mem0-ts/src/oss/src/vector_stores/elasticsearch.ts:26-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/elasticsearch.ts#L26-L44). Redis tag filters explicitly escape special characters including `-`, `|`, and whitespace to prevent RediSearch operator injection [mem0-ts/src/oss/src/vector_stores/redis.ts:13-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/redis.ts#L13-L24). Sources: [mem0-ts/src/oss/src/vector_stores/elasticsearch.ts:26-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/elasticsearch.ts#L26-L44), [mem0-ts/src/oss/src/vector_stores/redis.ts:13-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/redis.ts#L13-L24) ```typescript const SAFE_FILTER_KEY = /^[a-zA-Z_][a-zA-Z0-9_]*$/; // Mirrors the Python provider's _validate_filter. Filter keys are interpolated // into `metadata.${key}` field paths, so reject anything that is not a plain // identifier and reject non-scalar values before they reach the query. function validateFilter(key: string, value: unknown): void { if (typeof key !== "string" || !SAFE_FILTER_KEY.test(key)) { throw new Error(`Invalid filter key: ${JSON.stringify(key)}`); } ``` Sources: [mem0-ts/src/oss/src/vector_stores/elasticsearch.ts:26-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/elasticsearch.ts#L26-L34) > [!WARNING] > Unescaped hyphens or punctuation in Redis filter tag values cause RediSearch to interpret them as boolean operators or trigger immediate query syntax validation failures. > Sources: [mem0-ts/src/oss/src/vector_stores/redis.ts:13-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/redis ## Related - [Cloud Vector Backends](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/cloud-vector-backends) - [Database Vector Stores](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/database-vector-stores) - [Embedded Vector Stores](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/embedded-vector-stores) --- ## Technical docs: DELETE revoke_key URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/api-keys/revoke-key ## Parameters ## Responses ## Try It --- ## Technical docs: Cloud Vector Backends URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/cloud-vector-backends
Relevant source files The following files were used as context for generating this wiki page: - [mem0/vector_stores/s3_vectors.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/s3_vectors.py) - [mem0-ts/src/oss/src/vector_stores/s3_vectors.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts) - [mem0/vector_stores/neptune_analytics.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/neptune_analytics.py) - [mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts) - [mem0/vector_stores/weaviate.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py) - [mem0/vector_stores/turbopuffer.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/turbopuffer.py) - [mem0/vector_stores/upstash_vector.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/upstash_vector.py) - [mem0-ts/src/oss/src/vector_stores/turbopuffer.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/turbopuffer.ts) - [mem0/vector_stores/valkey.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/valkey.py) - [mem0/vector_stores/opensearch.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/opensearch.py) - [mem0-ts/src/oss/src/vector_stores/qdrant.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/qdrant.ts) - [mem0-ts/src/oss/src/vector_stores/milvus.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts) - [mem0/vector_stores/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/__init__.py) - [mem0-ts/src/oss/src/vector_stores/baidu.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/baidu.ts) - [mem0/configs/vector_stores/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/vector_stores/__init__.py) - [mem0-ts/src/oss/src/vector_stores/databricks.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/databricks.ts) - [mem0/vector_stores/chroma.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/chroma.py) - [mem0-ts/src/oss/src/vector_stores/azure_ai_search.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/azure_ai_search.ts) - [mem0-ts/src/oss/src/vector_stores/oracledb.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/oracledb.ts) - [mem0-ts/src/oss/src/vector_stores/weaviate.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts) - [mem0-ts/src/oss/src/vector_stores/upstash_vector.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/upstash_vector.ts) - [mem0/vector_stores/langchain.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py) - [mem0/vector_stores/mongodb.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/mongodb.py) - [mem0-ts/src/oss/src/vector_stores/pinecone.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pinecone.ts) - [mem0/vector_stores/milvus.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/milvus.py) - [mem0/vector_stores/azure_ai_search.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py) - [mem0/vector_stores/supabase.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/supabase.py) - [mem0-ts/src/oss/src/vector_stores/vectorize.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/vectorize.ts) - [mem0/vector_stores/faiss.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/faiss.py) - [mem0/vector_stores/pinecone.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/pinecone.py)
## Overview Cloud vector backends provide scalable, managed infrastructure for high-dimensional vector persistence and approximate nearest neighbor (ANN) retrieval across distributed cloud storage engines and managed database services. These storage backends form the core persistence layer for memory management systems, handling vector insertions, multi-tenant namespace isolation, metadata filtering, and hybrid sparse-dense keyword searches. By abstracting diverse remote protocols—ranging from object-backed S3 endpoints and serverless HTTP APIs to graph analytics engines and distributed index clusters—these drivers enable uniform vector operations across both Python and TypeScript runtimes. Sources: [mem0/vector_stores/s3_vectors.py:24-181](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/s3_vectors.py#L24-L181), [mem0-ts/src/oss/src/vector_stores/s3_vectors.ts:35-242](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts#L35-L242), [mem0/vector_stores/neptune_analytics.py:41-199](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/neptune_analytics.py#L41-L199), [mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts:42-240](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts#L42-L240), [mem0/vector_stores/turbopuffer.py:25-207](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/turbopuffer.py#L25-L207), [mem0-ts/src/oss/src/vector_stores/turbopuffer.ts:13-212](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/turbopuffer.ts#L13-L212), [mem0-ts/src/oss/src/vector_stores/qdrant.ts:300-412](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/qdrant.ts#L300-L412), [mem0/vector_stores/milvus.py:33-146](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/milvus.py#L33-L146), [mem0-ts/src/oss/src/vector_stores/milvus.ts:272-335](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts#L272-L335), [mem0/vector_stores/pinecone.py:25-121](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/pinecone.py#L25-L121), [mem0-ts/src/oss/src/vector_stores/pinecone.ts:244-289](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pinecone.ts#L244-L289), [mem0/vector_stores/weaviate.py:31-157](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py#L31-L157), [mem0-ts/src/oss/src/vector_stores/weaviate.ts:31-237](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts#L31-L237), [mem0/vector_stores/upstash_vector.py:41-172](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/upstash_vector.py#L41-L172), [mem0-ts/src/oss/src/vector_stores/upstash_vector.ts:17-268](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/upstash_vector.ts#L17-L268) ## Cloud Vector Driver Abstractions ### Architectural Contract and Client Initialization Cloud vector storage drivers implement a standard interface across both Python and TypeScript runtimes, wrapping diverse remote SDKs like `boto3`, `@aws-sdk/client-s3vectors`, and `weaviate-client`. Initialization routines handle optional peer dependencies dynamically, establish remote connections across HTTP, gRPC, or custom client parameters, and provision underlying vector buckets, indices, or classes. Sources: [mem0/vector_stores/s3_vectors.py:24-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/s3_vectors.py#L24-L41), [mem0-ts/src/oss/src/vector_stores/s3_vectors.ts:35-123](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts#L35-L123), [mem0/vector_stores/weaviate.py:31-85](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py#L31-L85), [mem0-ts/src/oss/src/vector_stores/weaviate.ts:31-118](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts#L31-L118) > [!NOTE] > Optional SDK peer dependencies such as `@aws-sdk/client-s3vectors` and `weaviate-client` are lazily imported at runtime in TypeScript and safely caught behind missing-import exceptions in Python to keep base package installations lightweight for consumers who exclude specific cloud backends. Sources: [mem0/vector_stores/s3_vectors.py:9-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/s3_vectors.py#L9-L13), [mem0-ts/src/oss/src/vector_stores/s3_vectors.ts:69-82](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts#L69-L82), [mem0/vector_stores/weaviate.py:9-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py#L9-L13), [mem0-ts/src/oss/src/vector_stores/weaviate.ts:49-59](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts#L49-L59) ### Lifecycle Hooks and Data Operations Drivers manage the complete entity lifecycle across insertion, vector search, metadata filtering, retrieval, updates, and deletion. Depending on the engine capabilities, updates are handled either via direct object overwrites, native upsert batches, or pre-flight fetching to resolve missing vector payloads. | Operation | Python Method | TypeScript Method | Core Responsibility | | :--- | :--- | :--- | :--- | | **Insert** | `insert(...)` | `insert(...)` | Batch writing of vectors, IDs, and metadata payloads into indices or collections. | | **Search** | `search(...)` | `search(...)` | Approximate nearest neighbor vector query with optional metadata filters and top-$k$ limits. | | **Get** | `get(...)` | `get(...)` | Retrieval of stored vector record details and metadata by primary identifier. | | **Update** | `update(...)` | `update(...)` | Modification of vector data or associated payload properties, handling missing parameters via pre-flight lookups. | | **Delete** | `delete(...)` | `delete(...)` | Removal of individual vector entries by identifier. | Sources: [mem0/vector_stores/s3_vectors.py:98-181](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/s3_vectors.py#L98-L181), [mem0-ts/src/oss/src/vector_stores/s3_vectors.ts:125-242](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts#L125-L242), [mem0/vector_stores/weaviate.py:158-199](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py#L158-L199), [mem0-ts/src/oss/src/vector_stores/weaviate.ts:128-204](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts#L128-L204) > [!WARNING] > When executing metadata-only updates in S3 Vectors where `vector=None`, the driver must fetch the existing vector payload beforehand; passing `None` directly to `put_vectors` triggers parameter validation errors in the underlying SDK. Sources: [mem0/vector_stores/s3_vectors.py:136-168](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/s3_vectors.py#L136-L168) ## S3 Vectors Storage Architecture The S3 Vectors driver provisions and interacts with object-backed storage collections across Python and TypeScript runtimes using `boto3` and `@aws-sdk/client-s3vectors`. Initialization ensures that both the target vector bucket and the specified index exist before executing any operations, catching `NotFoundException` errors from remote service calls to trigger bucket and index creation automatically. ```mermaid flowchart TD Init[Initialize S3Vectors] --> CheckBucket{Bucket Exists?} CheckBucket -- No --> CreateBucket[Create Vector Bucket] CheckBucket -- Yes --> CheckIndex{Index Exists?} CreateBucket --> CheckIndex CheckIndex -- No --> CreateIndex[Create Index with Dimension & Distance] CheckIndex -- Yes --> Ready[Store Ready for Operations] CreateIndex --> Ready ``` Sources: [mem0/vector_stores/s3_vectors.py:39-70](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/s3_vectors.py#L39-L70), [mem0-ts/src/oss/src/vector_stores/s3_vectors.ts:116-123](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts#L116-L123) Bucket and index provisioning operations configure data types, vector dimensions, and distance metrics. The supported index configurations and initialization parameters handle dimensional assertions across runtimes. | Configuration Parameter | Python Type | TypeScript Type | Default Value | Purpose | | :--- | :--- | :--- | :--- | :--- | | `vectorBucketName` | `str` | `string` | *Required* | Target S3 bucket container name for vector objects. | | `collectionName` / `collectionName` | `str` | `string` | *Required* | Index or collection namespace within the vector bucket. | | `embeddingModelDims` / `dimension` | `int` | `number` | *Required* | Dimensionality of the stored vector embeddings. | | `distanceMetric` | `str` | `"cosine" \| "euclidean"` | `"cosine"` | Distance calculation metric for similarity searches. | | `region_name` / `region` | `Optional[str]` | `string` | `undefined` | AWS region hosting the S3 Vectors client. | Sources: [mem0/vector_stores/s3_vectors.py:25-37](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/s3_vectors.py#L25-L37), [mem0-ts/src/oss/src/vector_stores/s3_vectors.ts:17-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts#L17-L29), [mem0-ts/src/oss/src/vector_stores/s3_vectors.ts:47-65](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts#L47-L65) > [!NOTE] > Distance score normalization maps raw engine distances into bounded similarity scores. Euclidean distances use an inverse monotonic transformation `1.0 / (1.0 + raw_distance)` since they are unbounded, whereas cosine distances clip `1.0 - raw_distance` at `0.0`. Sources: [mem0/vector_stores/s3_vectors.py:72-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/s3_vectors.py#L72-L80) ## Qdrant and Milvus Ingestion ### Collection Provisioning and Schema Detection Qdrant and Milvus drivers handle collection provisioning and schema detection differently depending on whether hybrid keyword search is enabled. In Milvus, collection initialization checks whether an existing collection contains both `text` and `sparse` fields, setting an internal `_has_bm25_schema` flag accordingly. When creating a fresh collection, Milvus defines fields for `id` (VARCHAR up to 512 characters, primary key), `vectors` (FLOAT_VECTOR with custom dimensions), `metadata` (JSON), `text` (VARCHAR up to 65535 characters with analyzer enabled), and `sparse` (SPARSE_FLOAT_VECTOR). It then adds a BM25 function linking `text` to `sparse`, applies an `AUTOINDEX` index on `vectors`, and adds a `SPARSE_INVERTED_INDEX` index named `sparse_index` with metric type `BM25`. Sources: [mem0/vector_stores/milvus.py:53-124](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/milvus.py#L53-L124) > [!WARNING] > If an existing Milvus collection predates v3 hybrid search and lacks `text` or `sparse` fields, BM25 keyword scoring is disabled for that collection with a warning logger call, and writing an unknown top-level `text` field will cause insertion rejections. Sources: [mem0/vector_stores/milvus.py:80-90](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/milvus.py#L80-L90) ### Point Upsert Protocols and Hybrid Handling Point insertion and upsert workflows adapt to server-side inference capabilities. In Qdrant, point vector building inspects `_has_bm25_slot`. Without a BM25 slot, it returns the plain dense vector array. When a BM25 slot is active, it builds a named vector map containing `""` for the dense vector and a sparse query object for the BM25 vector name using text extracted from `payload.textLemmatized` or `payload.data`. ```typescript private async upsertPoints( points: { id: string | number; vector: any; payload?: any }[], ): Promise { try { await this.client.upsert(this.collectionName, { points: points as any }); } catch (error) { if (!this._hasBm25Slot) { throw error; } this._hasBm25Slot = false; console.warn( `Qdrant rejected the server-side BM25 vector for collection '${this.collectionName}'; ` + "disabling hybrid keyword search. This requires Qdrant >= 1.15.2 with inference enabled. " + "Retrying the write with a plain dense vector.", ); const denseOnly = points.map((p) => ({ ...p, vector: Array.isArray(p.vector) ? p.vector : p.vector[""], })); await this.client.upsert(this.collectionName, { points: denseOnly as any, }); } } ``` Sources: [mem0-ts/src/oss/src/vector_stores/qdrant.ts:320-343](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/qdrant.ts#L320-L343) > [!NOTE] > Qdrant client types predate server-side `sparse_vectors`. Because older server versions (Qdrant < 1.15.2 or unactivated clusters) reject server-side BM25 configs during upsert, the driver catches the first upsert failure, disables `_has_bm25_slot`, and transparently retries the write using a plain dense vector. Sources: [mem0-ts/src/oss/src/vector_stores/qdrant.ts:312-343](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/qdrant.ts#L312-L343) In Milvus, record construction for insertion and updates similarly checks `_has_bm25_schema`. If the schema supports BM25, the record includes a `text` field populated by extracting `text_lemmatized` or `data` from metadata and slicing it to a maximum of 65,535 characters. ```typescript async insert( vectors: number[][], ids: string[], payloads: Record[], ): Promise { const data = vectors.map((vector, idx) => { const metadata = payloads[idx] || {}; const row: Record = { id: ids[idx], vectors: vector, metadata, }; // Only write `text` when the collection has the BM25 schema; legacy // collections reject an unknown top-level field. if (this.hasBm25Schema) row.text = this.bm25Text(metadata); return row; }); await this.client.insert({ collection_name: this.collectionName, data, }); } ``` Sources: [mem0-ts/src/oss/src/vector_stores/milvus.ts:272-293](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts#L272-L293), [mem0/vector_stores/milvus.py:126-146](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/milvus.py#L126-L146) ## Pinecone Namespacing and Operations ### Index Resolution and Namespace Isolation Pinecone client initialization resolves indices through either explicit client instances or API keys retrieved from parameters or the `PINECONE_API_KEY` environment variable. When creating or binding to collections, the driver checks existing indexes via `list_cols().names()` in Python, creating a new serverless or pod-based index if absent using either `ServerlessSpec` or `PodSpec`. Serverless configurations default to AWS region `us-west-2` if no explicit configuration dictionary is supplied. Namespace isolation is maintained by passing the configured namespace string directly into query, upsert, fetch, and delete operations. Sources: [mem0/vector_stores/pinecone.py:58-123](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/pinecone.py#L58-L123) > [!NOTE] > Deletion behavior varies based on whether a namespace is configured. If a namespace is set, `delete_col()` clears only that specific namespace via `delete(delete_all=True, namespace=...)`. If `namespace` is `None`, it deletes the entire index collection. Sources: [mem0/vector_stores/pinecone.py:361-371](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/pinecone.py#L361-L371), [mem0-ts/src/oss/src/vector_stores/pinecone.ts:318-330](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pinecone.ts#L318-L330) ### Filter Operator Mapping and Migrations Metadata filtering maps domain filter operators to Pinecone-compatible selector syntax. The driver translates comparison operators such as equality, bounds, and inclusion sets into standard Pinecone expression keys. | Operator Key | Pinecone Selector | | :--- | :--- | | `eq` | `$eq` | | `ne` | `$ne` | | `gt` | `$gt` | | `gte` | `$gte` | | `lt` | `$lt` | | `lte` | `$lte` | | `in` | `$in` | | `nin` | `$nin` | Sources: [mem0/vector_stores/pinecone.py:189-222](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/pinecone.py#L189-L222), [mem0-ts/src/oss/src/vector_stores/pinecone.ts:209-242](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pinecone.ts#L209-L242) > [!WARNING] > Text containment filters (`contains` and `icontains`) are unsupported by Pinecone metadata filters; the TypeScript driver skips them with a console warning, whereas unknown operators trigger an explicit runtime error. Sources: [mem0-ts/src/oss/src/vector_stores/pinecone.ts:220-230](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pinecone.ts#L220-L230) ## Weaviate Class and Filter Mechanics ### Weaviate Class Collections and Client Initialization ### Connection Schemes and Local Setup The Weaviate driver supports three distinct connection pathways across both Python and TypeScript runtimes: local development instances, managed Weaviate Cloud instances via API keys, and custom HTTP/gRPC host targets. | Connection Mode | Trigger Condition | Initialization Method (Python) | Initialization Method (TypeScript) | | :--- | :--- | :--- | :--- | | **Local** | `cluster_url` contains `"localhost"` | `weaviate.connect_to_local()` | `weaviate.connectToLocal()` | | **Cloud** | `auth_client_secret` or `apiKey` present | `weaviate.connect_to_weaviate_cloud()` | `weaviate.connectToWeaviateCloud()` | | **Custom** | Default fallback when URL is provided | `weaviate.connect_to_custom()` | `weaviate.connectToCustom()` | Sources: [mem0/vector_stores/weaviate.py:51-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py#L51-L80), [mem0-ts/src/oss/src/vector_stores/weaviate.ts:64-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts#L64-L96) > [!NOTE] > When connecting via the custom fallback mode, the driver automatically targets gRPC port `50051` with insecure transport (`grpcSecure: false`), while inspecting the URL scheme to set the HTTP port to `443` for HTTPS or `8080` for plain HTTP. Sources: [mem0/vector_stores/weaviate.py:60-79](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py#L60-L79), [mem0-ts/src/oss/src/vector_stores/weaviate.ts:81-95](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts#L81-L95) ### Schema Properties and Collection Creation Collections are configured with a standard set of text-typed properties and an HNSW vector index combined with a `none` vectorizer configuration. ```python properties = [ wvcc.Property(name="ids", data_type=wvcc.DataType.TEXT), wvcc.Property(name="hash", data_type=wvcc.DataType.TEXT), wvcc.Property( name="metadata", data_type=wvcc.DataType.TEXT, description="Additional metadata", ), wvcc.Property(name="data", data_type=wvcc.DataType.TEXT), wvcc.Property(name="created_at", data_type=wvcc.DataType.TEXT), wvcc.Property(name="category", data_type=wvcc.DataType.TEXT), wvcc.Property(name="updated_at", data_type=wvcc.DataType.TEXT), wvcc.Property(name="user_id", data_type=wvcc.DataType.TEXT), wvcc.Property(name="agent_id", data_type=wvcc.DataType.TEXT), wvcc.Property(name="run_id", data_type=wvcc.DataType.TEXT), ] vectorizer_config = wvcc.Configure.Vectorizer.none() vector_index_config = wvcc.Configure.VectorIndex.hnsw() self.client.collections.create( self.collection_name, vectorizer_config=vectorizer_config, vector_index_config=vector_index_config, properties=properties, ) ``` Sources: [mem0/vector_stores/weaviate.py:131-156](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py#L131-L156), [mem0-ts/src/oss/src/vector_stores/weaviate.ts:18-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts#L18-L29), [mem0-ts/src/oss/src/vector_stores/weaviate.ts:104-115](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts#L104-L115) ### Vector Search Filter Compilation Metadata search filters are compiled into Weaviate filter expressions by inspecting query keys for `user_id`, `agent_id`, and `run_id`. The drivers construct property-level equality checks and combine them using logical conjunction. ```python collection = self.client.collections.get(str(self.collection_name)) filter_conditions = [] if filters: for key, value in filters.items(): if value and key in ["user_id", "agent_id", "run_id"]: filter_conditions.append(Filter.by_property(key).equal(value)) combined_filter = Filter.all_of(filter_conditions) if filter_conditions else None ``` Sources: [mem0/vector_stores/weaviate.py:187-193](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py#L187-L193), [mem0-ts/src/oss/src/vector_stores/weaviate.ts:120-126](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts#L120-L126) > [!WARNING] > During object insertion, the Python driver explicitly checks if an `ids` key exists within the payload dictionary and deletes it to prevent conflicts with Weaviate's native object UUID assignment. Sources: [mem0/vector_stores/weaviate.py:175-178](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py#L175-L178) ## Turbopuffer and Upstash Serverless Models ### Serverless Namespace Queries and Row Parsing Serverless vector backends delegate index lifecycle management and scaling to cloud infrastructure, exposing storage through lightweight namespace abstractions. The Turbopuffer driver structures vector collections as isolated namespaces that support implicit creation on first write. During similarity queries, result rows are parsed from model dumps where distance metrics—such as unbounded squared-L2 distances or cosine metrics—are normalized into bounded scores. ```python results = [] for row in rows: row_dict = row.model_dump() row_id = str(row_dict.pop("id")) dist = row_dict.pop("$dist", None) row_dict.pop("vector", None) if dist is None: score = None elif self.distance_metric == "euclidean_squared": # $dist is unbounded squared-L2 (lower = closer); map to a bounded # higher-is-better score, mirroring milvus/baidu. Cosine returns 1 - dist. score = 1.0 / (1.0 + dist) else: score = 1 - dist results.append(OutputData( id=row_id, score=score, payload=row_dict, )) return results ``` Sources: [mem0/vector_stores/turbopuffer.py:64-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/turbopuffer.py#L64-L71), [mem0/vector_stores/turbopuffer.py:118-139](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/turbopuffer.py#L118-L139), [mem0-ts/src/oss/src/vector_stores/turbopuffer.ts:68-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/turbopuffer.ts#L68-L80) > [!NOTE] > When the distance metric is configured to `euclidean_squared`, Turbopuffer returns unbounded squared-L2 values where lower numbers indicate closer proximity. The parser transforms these distances into higher-is-better relevance scores via the inverse mapping `1.0 / (1.0 + dist)`. Sources: [mem0/vector_stores/turbopuffer.py:127-130](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/turbopuffer.py#L127-L130) ### Stringified Metadata Validation and Filter Construction Upstash Vector enforces strict type validation for search and listing filters before transmitting expressions to the serverless endpoint. Filter keys are verified, and values must conform to primitive types (`str`, `int`, `float`, or `bool`). Furthermore, string values undergo strict validation to prevent query injection or syntax corruption. ```python if not isinstance(value, (str, int, float, bool)): raise ValueError( f"Filter value for {key!r} must be str, int, float, or bool, " f"got {type(value).__name__}" ) if isinstance(value, str) and ('"' in value or "\\" in value): raise ValueError( f"Filter value for {key!r} contains prohibited characters " f"(double quote or backslash): {value!r}" ) ``` Sources: [mem0/vector_stores/upstash_vector.py:23-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/upstash_vector.py#L23-L32), [mem0-ts/src/oss/src/vector_stores/upstash_vector.ts:229-239](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/upstash_vector.ts#L229-L239) > [!WARNING] > Filter values of type `string` submitted to Upstash Vector cannot contain double quotes (`"`) or backslashes (`\`). Any matching characters trigger an explicit `ValueError` during filter preprocessing. Sources: [mem0/vector_stores/upstash_vector.py:28-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/upstash_vector.py#L28-L32) ## Related - [Vector Store Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/vector-store-architecture) - [Database Vector Stores](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/database-vector-stores) --- ## Technical docs: Database Vector Stores URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/database-vector-stores
Relevant source files The following files were used as context for generating this wiki page: - [mem0/vector_stores/oracledb.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/oracledb.py) - [mem0-ts/src/oss/src/vector_stores/oracledb.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/oracledb.ts) - [mem0/vector_stores/valkey.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/valkey.py) - [mem0-ts/src/oss/src/vector_stores/baidu.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/baidu.ts) - [mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts) - [mem0/vector_stores/redis.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/redis.py) - [mem0/vector_stores/neptune_analytics.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/neptune_analytics.py) - [mem0-ts/src/oss/src/vector_stores/cassandra.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/cassandra.ts) - [mem0-ts/src/oss/src/vector_stores/redis.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/redis.ts) - [mem0/vector_stores/opensearch.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/opensearch.py) - [mem0-ts/src/oss/src/vector_stores/valkey.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/valkey.ts) - [mem0/vector_stores/baidu.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/baidu.py) - [mem0-ts/src/oss/src/vector_stores/opensearch.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/opensearch.ts) - [mem0/vector_stores/mongodb.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/mongodb.py) - [mem0-ts/src/oss/src/vector_stores/elasticsearch.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/elasticsearch.ts) - [mem0-ts/src/oss/src/vector_stores/azure_mysql.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/azure_mysql.ts) - [mem0/configs/vector_stores/oracledb.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/vector_stores/oracledb.py) - [mem0/configs/vector_stores/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/vector_stores/__init__.py) - [mem0/vector_stores/chroma.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/chroma.py) - [mem0/vector_stores/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/__init__.py) - [mem0-ts/src/oss/src/vector_stores/mongodb.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/mongodb.ts) - [mem0/vector_stores/azure_ai_search.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py) - [mem0-ts/src/oss/src/vector_stores/weaviate.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/weaviate.ts) - [mem0/vector_stores/faiss.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/faiss.py) - [mem0/vector_stores/weaviate.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py) - [mem0/vector_stores/cassandra.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/cassandra.py) - [mem0/vector_stores/milvus.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/milvus.py) - [mem0-ts/src/oss/src/vector_stores/vectorize.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/vectorize.ts) - [mem0-ts/src/oss/src/vector_stores/pgvector.ts](https://github.com/blade47/mem0-ts/src/oss/src/vector_stores/pgvector.ts) - [mem0/vector_stores/elasticsearch.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/elasticsearch.py)
## Overview Database Vector Stores provide persistent indexing, metadata filtering, and approximate nearest neighbor (ANN) retrieval capabilities across relational databases, distributed search engines, in-memory data structures, and graph analytics systems. Within Mem0, these database backends handle the storage and querying of long-term memory embeddings alongside associated payloads, user tenant contexts, and timestamps. Unlike standalone [Embedded Vector Stores](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/embedded-vector-stores) or managed [Cloud Vector Backends](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/cloud-vector-backends), database vector stores integrate embedding storage directly into existing enterprise data systems like PostgreSQL via `pgvector`, Elasticsearch, OpenSearch, Redis, Valkey, Apache Cassandra, Amazon Neptune Analytics, Oracle AI Vector Search, MongoDB, Azure AI Search, and Baidu Mochow. Sources: [mem0-ts/src/oss/src/vector_stores/pgvector.ts:217-245](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pgvector.ts#L217-L245) The primary design challenge solved by these drivers is bridging multi-dimensional vector math with structured relational tables, document schemas, or inverted indices. Because different engines handle vector indexing via distinct internal mechanisms—such as PostgreSQL native `vector` data types, Elasticsearch `dense_vector` fields with HNSW graphs, or Redis Hash keys with RediSearch modules—each backend abstracts the connection lifecycle, schema initialization, parameter binding, and query filtering into a unified `VectorStore` interface. Sources: [mem0-ts/src/oss/src/vector_stores/pgvector.ts:217-245](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pgvector.ts#L217-L245) ```mermaid flowchart TD A["Memory Core Engine"] --> B["VectorStore Interface"] B --> C["Relational DBs
(pgvector, Azure MySQL, Oracle)"] B --> D["Search Engines
(Elasticsearch, OpenSearch, Azure AI Search)"] B --> E["In-Memory & KV
(Redis, Valkey)"] B --> F["Distributed & Graph
(Cassandra, Neptune Analytics, Milvus, Baidu)"] ``` Sources: [mem0-ts/src/oss/src/vector_stores/pgvector.ts:217-245](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pgvector.ts#L217-L245) ## Relational Database Integrations: pgvector, Azure MySQL, and Oracle DB Relational database integrations extend traditional SQL tables with native or JSON-serialized vector storage and similarity operators. In PGVector, tables are initialized with a `UUID` primary key, a `vector` data type column sized to `embeddingModelDims`, and a `JSONB` payload column. During nearest neighbor search, `pgvector` executes queries using the `<->` cosine distance operator, sorting results and bounding output sizes with `LIMIT $2`. Parameterized filters are dynamically constructed via `buildFilterConditions()`, translating metadata JSON path lookups into SQL expressions with strict parameter indexing to prevent injection. Sources: [mem0-ts/src/oss/src/vector_stores/pgvector.ts:284-446](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pgvector.ts#L284-L446) For Azure MySQL, embeddings and JSON payloads are stored as JSON columns and evaluated via application-level `cosineSimilarity()` loops over fetched rows, since MySQL handles vector distance sorting in memory after fetching candidate sets. Sources: [mem0-ts/src/oss/src/vector_stores/azure_mysql.ts:123-211](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/azure_mysql.ts#L123-L211) Oracle AI Vector Search utilizes Oracle 23.4+ native `VECTOR` data types and `JSON` payload columns inside relational tables. Search operations invoke `VECTOR_DISTANCE(vector, :query_vec, ...)` combined with `ORDER BY distance FETCH APPROX FIRST :max_rows ROWS ONLY`. When executing filtered searches, Oracle conditionally omits index hints or injects `VECTOR_INDEX_TRANSFORM` depending on filter presence. Sources: [mem0/vector_stores/oracledb.py:350-402](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/oracledb.py#L350-L402) ```mermaid sequenceDiagram participant App as Memory Client participant PG as PGVector Store participant DB as PostgreSQL Engine App->>PG: search(queryVector, topK, filters) PG->>DB: SELECT id, vector <=> $1::vector, payload WHERE ... ORDER BY distance LIMIT $2 DB-->>PG: Result rows with distance and JSONB payload PG-->>App: Normalized VectorStoreResult[] array ``` Sources: [mem0-ts/src/oss/src/vector_stores/pgvector.ts:414-446](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pgvector.ts#L414-L446) | Provider | Vector Storage Format | Index / Distance Mechanism | Metadata / Filter Strategy | | :--- | :--- | :--- | :--- | | **pgvector** | `vector(dims)` column | HNSW / DiskANN via `<->` operator | JSONB operators (`payload->>'key'`) | | **Azure MySQL** | `JSON` column | Application-level `cosineSimilarity()` | `JSON_EXTRACT(payload, ?)` | | **Oracle DB** | `VECTOR(dims)` column | HNSW / IVF neighbor graph via `VECTOR_DISTANCE` | Oracle JSON relational duality / bind vars | Sources: [mem0-ts/src/oss/src/vector_stores/pgvector.ts:315-349](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/pgvector.ts#L315-L349), [mem0-ts/src/oss/src/vector_stores/azure_mysql.ts:124-189](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/azure_mysql.ts#L124-L189), [mem0/vector_stores/oracledb.py:321-364](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/oracledb.py#L321-L364) ## Search Engines: Elasticsearch, OpenSearch, and Azure AI Search Distributed search and retrieval engines leverage inverted indices alongside vector quantization and HNSW graphs to execute hybrid keyword and semantic searches. Sources: [mem0/vector_stores/opensearch.py:83-135](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/opensearch.py#L83-L135) Elasticsearch and OpenSearch store embeddings in `dense_vector` or `knn_vector` fields configured with `nmslib` or native engines using cosine similarity. When inserting documents, payloads are flattened into `metadata` objects. During vector search, queries construct a `knn` block specifying `query_vector`, `k`, and `num_candidates` (typically `topK * 2`). Metadata filters are validated against a strict regex (`/^[a-zA-Z_][a-zA-Z0-9_]*$/`) and injected into the KNN filter block as boolean term queries. Sources: [mem0-ts/src/oss/src/vector_stores/elasticsearch.ts:134-164](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/elasticsearch.ts#L134-L164), [mem0-ts/src/oss/src/vector_stores/opensearch.ts:160-194](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/opensearch.ts#L160-L194) Azure AI Search configures indices with `SearchField` vector definitions (supporting `Collection(Edm.Single)` or `Collection(Edm.Half)` precision) and optional Scalar or Binary Quantization compression profiles (`ScalarQuantizationCompression`, `BinaryQuantizationCompression`). Filtering is performed via OData filter expressions generated by `_build_filter_expression()`. Sources: [mem0/vector_stores/azure_ai_search.py:105-163](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L105-L163) > [!NOTE] > OpenSearch and Elasticsearch require explicit index settings (`index.knn: true` in OpenSearch; `dense_vector` mapping with `index: true` in Elasticsearch) to enable approximate nearest neighbor indexing. Without these settings, searches fall back to exact brute-force scans across all documents. Sources: [mem0/vector_stores/elasticsearch.py:72-95](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/elasticsearch.py#L72-L95) | Search Backend | Vector Mapping Type | Default Distance / Metric | Filter Execution Mechanism | | :--- | :--- | :--- | :--- | | **Elasticsearch** | `dense_vector` | `cosine` | `knn.filter` boolean term clauses | | **OpenSearch** | `knn_vector` | `cosinesimil` | QueryDSL bool / term filters on `payload` | | **Azure AI Search** | `Collection(Edm.Single)` | Cosine via HNSW profile | OData filter expression (`eq`, `and`) | Sources: [mem0/vector_stores/elasticsearch.py:72-95](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/elasticsearch.py#L72-L95), [mem0/vector_stores/opensearch.py:83-107](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/opensearch.py#L83-L107), [mem0/vector_stores/azure_ai_search.py:135-160](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py#L135-L160) ## In-Memory & Key-Value Stores: Redis and Valkey In-memory data stores like Redis and Valkey employ RediSearch and RedisVL modules to index hash structures containing binary-encoded floating-point vector embeddings. Sources: [mem0/vector_stores/redis.py:48-78](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/redis.py#L48-L78) The `RedisDB` and `ValkeyDB` drivers store entries as Redis Hashes using keys prefixed with `mem0:{collection_name}:{id}`. Float32 arrays are converted to raw byte buffers (`np.array(vector, dtype=np.float32).tobytes()` in Python or `Buffer.from(new Float32Array(query).buffer)` in TypeScript) before persistence. Sources: [mem0/vector_stores/redis.py:123-147](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/redis.py#L123-L147), [mem0-ts/src/oss/src/vector_stores/valkey.ts:432-439](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/valkey.ts#L432-L439) Schema definitions declare fields such as `memory_id`, `hash`, `agent_id`, `run_id`, `user_id` as `TAG` types, `memory` and `metadata` as `TEXT` or `TAG`, `created_at` as `NUMERIC`, and `embedding` as a `VECTOR` attribute using FLAT or HNSW algorithms. Sources: [mem0/vector_stores/valkey.py:18-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/valkey.py#L18-L34), [mem0/vector_stores/redis.py:20-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/redis.py#L20-L36) ```typescript // Example: Building a Valkey / Redis pre-filter expression export function buildRedisFilterExpr(filters?: SearchFilters): string { if (!filters) return "*"; const conditions = Object.entries(toSnakeCase(filters)) .filter(([, value]) => value !== null && value !== undefined) .map(([key, value]) => `@${key}:{${escapeRedisTagValue(value)}}`); return conditions.length > 0 ? conditions.join(" ") : "*"; } ``` Sources: [mem0-ts/src/oss/src/vector_stores/redis.ts:141-147](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/redis.ts#L141-L147) > [!CAUTION] > Tag filter values in RediSearch must be escaped via `escapeTagValue()` or `escapeRedisTagValue()` to neutralize special characters such as `*`, `|`, and whitespace. Unescaped tag values alter query syntax and can allow tenant-isolation filter bypasses. Sources: [mem0/vector_stores/valkey.py:48-57](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/valkey.py#L48-L57), [mem0-ts/src/oss/src/vector_stores/valkey.ts:22-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/valkey.ts#L22-L27) ## Distributed & Graph Backends: Cassandra, Neptune Analytics, Milvus, and Baidu Mochow Distributed and specialized graph stores handle massive memory throughput using custom partition keys and vector-optimized storage engines. Sources: [mem0-ts/src/oss/src/vector_stores/cassandra.ts:41-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/cassandra.ts#L41-L80) - **Apache Cassandra**: Stores vectors as `list` and payloads as JSON text inside tables structured under configurable keyspaces (default `mem0`). Search queries scan rows in pages (`PAGE_SIZE = 500`), normalize vectors, apply in-memory metadata filters, and maintain top-K results via `pushTopResult()`. - **Amazon Neptune Analytics**: Operates as a serverless graph database where memory entities are stored as graph vertices with label `MEM0_VECTOR_{collection_name}` and identifier `~id`. Vector upserts and similarity searches execute Cypher procedures such as `CALL neptune.algo.vectors.upsert(n, embedding)` and `CALL neptune.algo.vectors.topKByEmbeddingWithFiltering(...)`. - **Milvus / Zilliz**: Configures collections with `AUTOINDEX` for float vectors and `SPARSE_INVERTED_INDEX` with built-in `BM25` functions for hybrid sparse-dense retrieval over auto-tokenized `text` fields. - **Baidu Mochow**: Utilizes typed tables (`STRING` id, `FLOAT_VECTOR` vector, `JSON` metadata) with HNSW vector indices and `BM25SearchRequest` full-text capabilities. Sources: [mem0-ts/src/oss/src/vector_stores/cassandra.ts:143-185](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/cassandra.ts#L143-L185), [mem0/vector_stores/neptune_analytics.py:41-61](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/neptune_analytics.py#L41-L61), [mem0/vector_stores/milvus.py:33-65](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/milvus.py#L33-L65), [mem0-ts/src/oss/src/vector_stores/baidu.ts:352-391](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/baidu.ts#L352-L391) ```mermaid graph LR subgraph Cassandra C1["Scan Rows (Paged)"] --> C2["Normalize Vector & Parse Payload"] C2 --> C3["Filter & pushTopResult()"] end subgraph Neptune N1["Cypher Query Generation"] --> N2["neptune.algo.vectors.topKByEmbeddingWithFiltering"] N2 --> N3["Parse Node & Score Output"] end subgraph Milvus M1["Dense Vectors (AUTOINDEX)"] --> M2["Sparse Vectors (BM25 Function)"] M2 --> M3["Hybrid Search Result Ranking"] end ``` Sources: [mem0-ts/src/oss/src/vector_stores/cassandra.ts:143-185](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/cassandra.ts#L143-L185), [mem0/vector_stores/neptune_analytics.py:159-198](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/neptune_analytics.py#L159-L198), [mem0/vector_stores/milvus.py:66-125](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/milvus.py#L66-L125) ```typescript async search( query: number[], topK: number = 5, filters?: SearchFilters, ): Promise { await this.initialize(); this.assertVectorDimension(query, "Query"); const scored: VectorStoreResult[] = []; await this.scanRows( ` SELECT id, vector, payload FROM ${this.keyspace}.${this.collectionName} `, async (row) => { const vector = this.normalizeVector(row.vector); const payload = this.parsePayload(row.payload); if (!vector || vector.length !== this.dimension) { return; } const item: CassandraVector = { id: String(row.id), vector, payload, }; if (!this.filterVector(item, filters)) { return; } this.pushTopResult( scored, { id: item.id, payload: item.payload, score: this.cosineSimilarity(query, item.vector), }, topK, ); }, Math.max(topK, CassandraDB.PAGE_SIZE), ); return scored; } ``` Sources: [mem0-ts/src/oss/src/vector_stores/cassandra.ts:143-185](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/cassandra.ts#L143-L185) ## Configuration, Connection Management, and Lazy Loading Database vector store initializers implement lazy connection loading to prevent optional peer dependencies (such as `@elastic/elasticsearch`, `pymongo`, `cassandra-driver`, `redis`, or `oracledb`) from breaking base package imports when a provider is not actively in use. Sources: [mem0-ts/src/oss/src/vector_stores/elasticsearch.ts:63-65](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/elasticsearch.ts#L63-L65), [mem0-ts/src/oss/src/vector_stores/mongodb.ts:35-37](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/mongodb.ts#L35-L37) Clients and connection pools are initialized on demand during the first call to `initialize()`, which caches its execution promise (`_initPromise`) to prevent race conditions during concurrent startup sequences. Sources: [mem0-ts/src/oss/src/vector_stores/opensearch.ts:120-126](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/opensearch.ts#L120-L126) ```typescript // Example: Lazy peer loading pattern in OpenSearch and Elasticsearch async initialize(): Promise { if (!this._initPromise) { this._initPromise = this._doInitialize(); } return this._initPromise; } ``` Sources: [mem0-ts/src/oss/src/vector_stores/opensearch.ts:120-126](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/opensearch.ts#L120-L126) | Configuration Property | Target Stores | Purpose & Default Behavior | | :--- | :--- | :--- | | `collection_name` / `tableName` | All Stores | Namespace or table identifier (defaults to `mem0` or `memories`) | | `embeddingModelDims` / `dimension` | All Stores | Vector dimensionality (defaults to `1536`) | | `autoRefresh` | OpenSearch | Controls `indices.refresh()` post-insert (default `false` for Serverless compatibility) | | `useAzureCredential` | Azure MySQL, Azure AI Search | Authenticates via `@azure/identity` DefaultAzureCredential | Sources: [mem0/configs/vector_stores/oracledb.py:41-69](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/vector_stores/oracledb.py#L41-L69), [mem0/vector_stores/opensearch.py:62-81](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/opensearch.py#L62-L81), [mem0-ts/src/oss/src/vector_stores/azure_mysql.ts:34-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/azure_mysql.ts#L34-L46) ## Error Handling, Sanitization, and Security Invariants Database vector stores enforce strict input sanitization and parameter binding guards to protect against injection attacks and malformed embeddings. Sources: [mem0/vector_stores/mongodb.py:150-165](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/mongodb.py#L150-L165) - **Filter Key Validation**: Drivers such as Elasticsearch, OpenSearch, pgvector, and Baidu enforce strict regular expression validation on filter keys (`/^[a-zA-Z_][a-zA-Z0-9_]*$/`) to prevent arbitrary query injection through metadata keys. - **Operator Blocking**: MongoDB drivers reject filter values that contain dictionaries (`$ne`, `$gt`, etc.) to prevent NoSQL query operator injection. - **Dimension Checking**: Insert and search operations validate that input vector lengths match configured embedding dimensions before issuing database commands. Sources: [mem0/vector_stores/mongodb.py:150-165](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/mongodb.py#L150-L165), [mem0-ts/src/oss/src/vector_stores/elasticsearch.ts:26-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/elasticsearch.ts#L26-L44), [mem0/vector_stores/opensearch.py:25-53](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/opensearch.py#L25-L53) > [!WARNING] > Bypassing filter key validation or passing unvalidated user input directly into MongoDB query objects or SQL string templates can permit query manipulation. Always use the provided validation helpers (`_validate_filter`, `validateFilterValue`) prior to query construction. Sources: [mem0/vector_stores/mongodb.py:150-165](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/mongodb.py#L150-L165) ## Related - [Vector Store Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/vector-store-architecture) - [Cloud Vector Backends](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/cloud-vector-backends) --- ## Technical docs: GET Setup Status URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/auth/setup-status ## Responses ## Try It --- ## Technical docs: Embedded Vector Stores URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/embedded-vector-stores
Relevant source files The following files were used as context for generating this wiki page: - [mem0/vector_stores/chroma.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/chroma.py) - [mem0-ts/src/oss/src/vector_stores/chroma.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/chroma.ts) - [mem0/vector_stores/faiss.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/faiss.py) - [mem0-ts/src/oss/src/vector_stores/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/memory.ts) - [mem0/vector_stores/valkey.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/valkey.py) - [mem0/vector_stores/upstash_vector.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/upstash_vector.py) - [mem0/configs/vector_stores/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/vector_stores/__init__.py) - [mem0-ts/src/oss/src/vector_stores/valkey.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/valkey.ts) - [mem0-ts/src/oss/src/vector_stores/azure_ai_search.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/azure_ai_search.ts) - [mem0/vector_stores/azure_ai_search.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_ai_search.py) - [mem0/vector_stores/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/__init__.py) - [mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts) - [mem0-ts/src/oss/src/vector_stores/s3_vectors.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts) - [mem0/vector_stores/redis.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/redis.py) - [mem0/vector_stores/langchain.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py) - [mem0-ts/src/oss/src/vector_stores/oracledb.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/oracledb.ts) - [mem0/vector_stores/opensearch.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/opensearch.py) - [mem0-ts/src/oss/src/vector_stores/cassandra.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/cassandra.ts) - [mem0/vector_stores/weaviate.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/weaviate.py) - [mem0/vector_stores/azure_mysql.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/azure_mysql.py) - [mem0/vector_stores/neptune_analytics.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/neptune_analytics.py) - [mem0-ts/src/oss/src/vector_stores/azure_mysql.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/azure_mysql.ts) - [mem0-ts/src/oss/src/vector_stores/baidu.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/baidu.ts) - [mem0/vector_stores/s3_vectors.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/s3_vectors.py) - [mem0-ts/src/oss/src/vector_stores/qdrant.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/qdrant.ts) - [mem0/vector_stores/cassandra.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/cassandra.py) - [mem0/vector_stores/mongodb.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/mongodb.py) - [mem0/vector_stores/supabase.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/supabase.py) - [mem0/embeddings/mock.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/mock.py) - [mem0-ts/src/oss/src/vector_stores/milvus.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/milvus.ts)
## Overview Embedded vector stores provide zero-dependency local indexing solutions that enable developers to run semantic memory systems without configuring external database services or cloud infrastructure. By embedding lightweight vector engines directly into application runtimes, these components facilitate rapid local prototyping, offline development, and deterministic unit testing. They bridge the gap between abstract vector embedding generation and persistent local storage by abstracting away low-level file I/O, memory buffering, and similarity index construction. Sources: [mem0/vector_stores/chroma.py:23-75](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/chroma.py#L23-L75) Across both Python and TypeScript packages, embedded stores handle vector insertions, approximate nearest neighbor (ANN) lookups, payload metadata association, and collection lifecycle management locally. For instance, implementations like Chroma support persistent file-backed directory storage through Python and TypeScript client bindings, while native in-memory stores utilize local SQLite file layers with Float32 buffer serialization. These lightweight backends interact directly with core memory orchestrators [Vector Store Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/vector-store-architecture) while keeping administrative overhead minimal. Sources: [mem0-ts/src/oss/src/vector_stores/chroma.ts:28-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/chroma.ts#L28-L100) The following sections examine the specific architectural mechanics, client initialization sequences, data serialization patterns, and security guardrails governing embedded vector stores. By understanding these internals, developers can effectively choose between local file persistence (such as Chroma or FAISS) and zero-config embedded runtimes (such as SQLite-backed memory stores) during test-driven development and local prototyping. Sources: [mem0-ts/src/oss/src/vector_stores/memory.ts:231-245](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/memory.ts#L231-L245) ## ChromaDB Integration and Client Lifecycle The ChromaDB vector store driver provides both Python and TypeScript implementations that mirror each other to deliver seamless local persistence and server connectivity. In Python, `ChromaDB` extends `VectorStoreBase` to manage collections via `chromadb.Client` or `chromadb.CloudClient`. In TypeScript, `ChromaDB` implements the `VectorStore` interface, utilizing lazy peer loading of the `chromadb` package to ensure consumers who do not use Chroma are not forced to install the optional peer dependency. Sources: [mem0/vector_stores/chroma.py:23-45](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/chroma.py#L23-L45) During initialization, if an existing client instance is omitted, the constructor configures local persistence settings (disabling anonymized telemetry by default) and defaults the local storage directory to `"db"` if no path is provided. It then invokes `get_or_create_collection` with an explicit null embedding function in TypeScript, ensuring that embeddings are pre-computed externally by mem0 prior to persistence. Sources: [mem0/vector_stores/chroma.py:46-75](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/chroma.py#L46-L75) ```mermaid flowchart TD A["Initialize ChromaDB"] --> B{"Client Provided?"} B -- Yes --> C["Use Existing Client"] B -- No --> D{"API Key & Tenant?"} D -- Yes --> E["Initialize CloudClient"] D -- No --> F["Configure Settings
Persist Directory: path"] F --> G["Initialize ChromaClient"] C --> H["Get or Create Collection"] E --> H G --> H ``` Sources: [mem0-ts/src/oss/src/vector_stores/chroma.ts:35-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/chroma.ts#L35-L100) ## SQLite-Backed In-Memory Vector Store For lightweight unit testing and zero-configuration execution, the TypeScript runtime provides an in-memory vector store backed by SQLite [Vector Store Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/vector-store-architecture). This implementation ensures that developers can execute tests or lightweight applications without external daemon processes or native vector binaries. Sources: [mem0-ts/src/oss/src/vector_stores/memory.ts:231-240](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/memory.ts#L231-L240) Vectors are serialized into binary buffers using `Float32Array` views mapped directly onto Node.js `Buffer` instances before insertion into SQLite tables. During vector similarity searches, rows are fetched and converted back into floating-point arrays to compute exact cosine similarity against the query vector. Sources: [mem0-ts/src/oss/src/vector_stores/memory.ts:241-245](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/memory.ts#L241-L245) ```typescript // Example usage of SQLite-backed memory store in TypeScript import { MemoryVectorStore } from "mem0-ts/oss"; const store = new MemoryVectorStore({ collectionName: "test_memories", dimension: 3 }); await store.insert( [[0.1, 0.2, 0.3]], ["mem_1"], [{ data: "Hello local vector world" }] ); const results = await store.search([0.1, 0.2, 0.3], 5); console.log(results); ``` Sources: [mem0-ts/src/oss/src/vector_stores/memory.ts:352-391](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/memory.ts#L352-L391) ## FAISS Local Indexing and Security Controls The FAISS vector store integration (`FAISS`) provides high-performance local similarity search utilizing Facebook AI Similarity Search bindings. It supports distance strategies including `'euclidean'`, `'inner_product'`, and `'cosine'`, along with optional L2 normalization. Sources: [mem0/vector_stores/faiss.py:127-146](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/faiss.py#L127-L146) To protect against arbitrary code execution vulnerabilities during local index deserialization, the FAISS driver implements a restricted unpickler (`SafeUnpickler`). This security guard strictly limits allowed pickle modules to `builtins` and restricts deserialized types to a strict whitelist. Sources: [mem0/vector_stores/faiss.py:34-61](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/faiss.py#L34-L61) > [!CAUTION] > Loading untrusted local FAISS index snapshots without `SafeUnpickler` active can lead to arbitrary code execution via standard Python `pickle` deserialization. The `SafeUnpickler` class enforces that only safe basic types (`dict`, `list`, `str`, `int`, `float`, `bool`, `tuple`, `set`, `frozenset`, `NoneType`) are instantiated during load operations. Sources: [mem0/vector_stores/faiss.py:62-78](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/faiss.py#L62-L78) ## Output Parsing and Score Normalization Embedded vector stores return uniform results encapsulated in structured `OutputData` models. Because different underlying libraries compute raw metrics differently (such as unbounded Euclidean distances versus cosine similarity), embedded stores normalize raw scores into consistent similarity metrics. Sources: [mem0/vector_stores/chroma.py:17-21](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/chroma.py#L17-L21) For instance, both Chroma and FAISS wrappers parse raw distance outputs using an inverse distance transformation formula: score equals one divided by one plus raw distance. This guarantees that similarity scores fall within a predictable range where higher values indicate closer proximity. Sources: [mem0/vector_stores/chroma.py:76-102](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/chroma.py#L76-L102) The execution path for output parsing across local drivers processes raw results through array flattening and metric transformation before yielding structured output entries. Sources: [mem0/vector_stores/chroma.py:103-110](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/chroma.py#L103-L110) ```mermaid sequenceDiagram participant Caller participant Store as VectorStoreDriver participant Parser as _parse_output() Caller->>Store: query(vectors, top_k) Store-->>Store: Execute local ANN search Store->>Parser: Pass raw query results (ids, distances, metadatas) Parser->>Parser: Flatten nested arrays & calculate 1 / (1 + distance) Parser-->>Store: Return List[OutputData] Store-->>Caller: Return standardized results ``` Sources: [mem0-ts/src/oss/src/vector_stores/chroma.ts:120-144](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/chroma.ts#L120-L144) ## Configuration and Initialization Parameters Embedded vector stores accept specific configuration parameters to adjust persistence directories, dimension constraints, and distance metrics. The following table outlines key initialization arguments supported across embedded implementations: Sources: [mem0/vector_stores/chroma.py:23-45](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/chroma.py#L23-L45) | Parameter Name | Target Driver | Default Value | Purpose | | :--- | :--- | :--- | :--- | | `collection_name` | All Drivers | Required | Unique identifier for the vector collection/index. | | `path` | Chroma / FAISS | `None` / `"db"` | Local filesystem directory path for persistent database storage. | | `distance_strategy` | FAISS | `"euclidean"` | Metric strategy used for similarity calculations. | | `normalize_L2` | FAISS | `False` | Flag indicating whether to perform L2 normalization on vectors. | | `embedding_model_dims` | FAISS / Memory | `1536` | Expected dimensionality of input vector embeddings. | Sources: [mem0/vector_stores/faiss.py:127-146](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/faiss.py#L127-L146) > [!NOTE] > When initializing local embedded stores like Chroma, omitting the `path` configuration automatically falls back to creating or utilizing a local `./db` directory relative to the execution context. Sources: [mem0/vector_stores/chroma.py:64-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/chroma.py#L64-L71) ## Error Handling and Graceful Fallbacks Local embedded stores handle missing optional peer dependencies by raising informative installation instructions. For example, if `chromadb` or `faiss` packages are absent from the Python environment, import blocks catch `ImportError` exceptions and prompt the user to install the appropriate package via `pip`. Sources: [mem0/vector_stores/chroma.py:6-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/chroma.py#L6-L11) Similarly, in TypeScript implementations, peer dependencies such as `chromadb` are loaded lazily via dynamic `import()` statements wrapped in peer loaders. This design choice prevents application boot failures when importing modules that do not utilize local vector backends. Sources: [mem0-ts/src/oss/src/vector_stores/chroma.ts:49-61](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/chroma.ts#L49-L61) > [!WARNING] > Ensure that matching native build tools (such as C++ build dependencies for FAISS CPU/GPU wheels) are present in your local environment when installing zero-dependency vector stores for local execution. Sources: [mem0/vector_stores/faiss.py:21-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/faiss.py#L21-L28) ## Related - [Vector Store Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/vector-store-architecture) --- ## Technical docs: POST Register URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/auth/register ## Request Body Register Request ## Responses ## Try It --- ## Technical docs: FastAPI Application URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application
Relevant source files The following files were used as context for generating this wiki page: - [server/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py) - [server/dashboard/src/app/setup/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx) - [server/routers/auth.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/auth.py) - [integrations/antigravity-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/hook_runner.py) - [integrations/agent-plugin-core/python/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py) - [cli/python/src/mem0_cli/app.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py) - [server/routers/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/__init__.py) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [server/routers/entities.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py) - [server/errors.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/errors.py) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [server/rate_limit.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/rate_limit.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [server/server_state.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/server_state.py) - [server/auth.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py) - [server/routers/api_keys.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py) - [mem0/client/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py) - [integrations/hermes-plugin-mem0/_setup.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py) - [integrations/agent-plugin-core/conformance/run.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py) - [server/routers/requests.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/requests.py) - [mem0/memory/notices.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/notices.py) - [server/models.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/models.py) - [mem0/client/utils.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/utils.py) - [server/Dockerfile](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/Dockerfile) - [mem0/exceptions.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/exceptions.py) - [server/schemas.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/schemas.py) - [mem0/proxy/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/proxy/__init__.py) - [server/db.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/db.py)
## Overview The Mem0 FastAPI application serves as the core backend service for managing, searching, and organizing AI memory artifacts through a robust REST API. Built on FastAPI and SQLAlchemy, the server orchestrates application lifecycle initialization, dynamic runtime configuration changes across vector and LLM backends, and multi-tenant authentication supporting JWT bearer tokens, user API keys, and legacy admin credentials. It integrates custom request logging, rate limiting via Slowapi, and upstream error translation to expose clean endpoints for user management, API key rotation, request telemetry browsing, and entity manipulation. Sources: [server/main.py:146-173](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L146-L173), [server/errors.py:71-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/errors.py#L71-L84), [server/auth.py:144-176](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L144-L176) ## Application Bootstrap and State Initialization ### Server State and Session Wiring The server bootstrap sequence initializes environment configuration, default connection parameters, and global thread-safe state before instantiating the FastAPI application. Environment variables govern Postgres vector storage connection targets, OpenAI API keys, history paths, and model selections. | Configuration Parameter | Default Value / Source | Purpose | | :--- | :--- | :--- | | `POSTGRES_HOST` | `postgres` (`os.environ`) | Hostname for the pgvector database backend | | `POSTGRES_PORT` | `5432` (`os.environ`) | Port number for the pgvector database | | `POSTGRES_DB` | `postgres` (`os.environ`) | Database name for pgvector memory persistence | | `POSTGRES_USER` | `postgres` (`os.environ`) | Database username | | `POSTGRES_PASSWORD` | `postgres` (`os.environ`) | Database password | | `POSTGRES_COLLECTION_NAME` | `memories` (`os.environ`) | Collection name inside pgvector | | `OPENAI_API_KEY` | `os.environ.get("OPENAI_API_KEY")` | API key for OpenAI LLM and embedder providers | | `HISTORY_DB_PATH` | `/app/history/history.db` (`os.environ`) | File path for the sqlite history database | | `MEM0_DEFAULT_LLM_MODEL` | `gpt-5-mini` (`os.environ`) | Default language model identifier | | `MEM0_DEFAULT_EMBEDDER_MODEL` | `text-embedding-3-small` (`os.environ`) | Default embedding model identifier | Sources: [server/main.py:108-139](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L108-L139) Server state management relies on thread-locked configuration containers and factory registration in `server_state.py`. Before the FastAPI application object is created, `set_session_factory` binds the SQLAlchemy `SessionLocal` maker, and `initialize_state` loads any persisted configuration overrides from the database before building the initial `Memory` instance via `Memory.from_config`. ```python def initialize_state(default_config: Dict[str, Any]) -> None: global _current_config, _memory_instance with _state_lock: _current_config = deepcopy(default_config) overrides = _load_overrides() if overrides: _current_config = _merge_config(_current_config, overrides) _memory_instance = Memory.from_config(_current_config) ``` Sources: [server/server_state.py:76-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/server_state.py#L76-L84) > [!WARNING] > Accessing `get_memory_instance()` before `initialize_state()` has completed will raise a `RuntimeError("Mem0 runtime has not been initialized.")`. Ensure session factory wiring and configuration loading occur sequentially during startup. Sources: [server/server_state.py:103-107](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/server_state.py#L103-L107) ### Configuration Validation and Unconfigured Warnings Startup checks enforce authentication constraints based on `AUTH_DISABLED`, `JWT_SECRET`, and `ADMIN_API_KEY`. If `AUTH_DISABLED` is false and `JWT_SECRET` is missing, initialization aborts immediately with a `RuntimeError`. If `ADMIN_API_KEY` is not provided, `_warn_if_unconfigured()` queries the user table via the session factory to verify whether any administrator accounts exist. ```python def _warn_if_unconfigured() -> None: """Pre-auth deployments upgrading into this build will 401 everywhere until an admin key or admin user exists. Surface the fix before the support tickets.""" try: with SessionLocal() as session: if session.scalar(select(func.count(User.id))) > 0: return except Exception: return logging.warning( "\n%s\n" " Auth is enabled by default and this server has no admin configured.\n" " Protected endpoints will return 401 until you either:\n" " 1. Set ADMIN_API_KEY= (fastest, no client changes)\n" " 2. Register an admin at http://:3000/setup\n" " 3. Set AUTH_DISABLED=true (local development only)\n" " Docs: https://docs.mem0.ai/open-source/features/rest-api#authentication\n" "%s", "=" * 72, "=" * 72, ) ``` Sources: [server/main.py:66-88](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L66-L88) ## CORS and Request Pipeline Middleware ### CORS Policy and Telemetry Nudging The FastAPI application integrates CORS policies through `CORSMiddleware`, restricting allowed origins to the configured dashboard URL (`DASHBOARD_URL`), which defaults to `http://localhost:3000`. Credentials and all HTTP methods and headers are permitted for cross-origin interactions. ```python DASHBOARD_URL = os.environ.get("DASHBOARD_URL", "http://localhost:3000") app.add_middleware( CORSMiddleware, allow_origins=[DASHBOARD_URL], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) ``` Sources: [server/main.py:160-167](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L160-L167) Before the HTTP pipeline processes incoming connections, telemetry logging status is initialized via `telemetry.log_status()`. ```python telemetry.log_status() ``` Sources: [server/main.py:106-106](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L106-L106) ### Request ID Tagging and Logging Filters Request traceability is enforced across execution contexts using a `ContextVar` named `request_id_var` defaulting to `"-"`. Unique 8-character hexadecimal identifiers are generated via `new_request_id()` using UUID4. ```python request_id_var: ContextVar[str] = ContextVar("request_id", default="-") … def new_request_id() -> str: return uuid.uuid4().hex[:8] ``` Sources: [server/errors.py:9-9](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/errors.py#L9-L9), [server/errors.py:67-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/errors.py#L67-L68) The logging record factory is monkey-patched via `install_request_id_logging()` to automatically inject the current context's `request_id` into every log record. Standard logging is configured with a formatting string that includes the request identifier tag. ```python def install_request_id_logging() -> None: old_factory = logging.getLogRecordFactory() def factory(*args, **kwargs): record = old_factory(*args, **kwargs) record.request_id = request_id_var.get() return record logging.setLogRecordFactory(factory) … install_request_id_logging() logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - [%(request_id)s] %(message)s") ``` Sources: [server/errors.py:87-95](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/errors.py#L87-L95), [server/main.py:45-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L45-L46) Certain paths and prefixes are explicitly excluded from request logging telemetry to prevent noise from health inspections and documentation endpoints. | Exclusion Rule Type | Values | Purpose | | :--- | :--- | :--- | | `SKIPPED_REQUEST_LOG_PATHS` | `"/api/health"`, `"/docs"`, `"/redoc"`, `"/openapi.json"` | Bypasses logging for health checks and OpenAPI UI routes | | `SKIPPED_REQUEST_LOG_PREFIXES` | `"/requests"` | Bypasses logging for request telemetry inspection routes | Sources: [server/main.py:59-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L59-L60) > [!NOTE] > Request logs associated with routes matching `SKIPPED_REQUEST_LOG_PATHS` or starting with `SKIPPED_REQUEST_LOG_PREFIXES` are filtered out to keep telemetry tables focused on core memory operations. Sources: [server/main.py:59-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L59-L60) ## Modular Subsystem Router Composition ### Subsystem Router Integration The FastAPI application instance consolidates its API surface by including modular subsystem routers defined across the project architecture. These include authentication management, API key provisioning, entity browsing, and request telemetry inspection routes. Each imported router is mounted directly onto the central `app` instance. ```python from routers import api_keys as api_keys_router from routers import auth as auth_router from routers import entities as entities_router from routers import requests as requests_router … app.include_router(auth_router.router) app.include_router(api_keys_router.router) app.include_router(entities_router.router) app.include_router(requests_router.router) ``` Sources: [server/main.py:25-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L25-L28), [server/main.py:169-172](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L169-L172) | Router Module | Variable Alias | Inclusion Target | Purpose | | :--- | :--- | :--- | :--- | | `routers.auth` | `auth_router` | `auth_router.router` | Handles user authentication, registration, and login sessions | | `routers.api_keys` | `api_keys_router` | `api_keys_router.router` | Manages creation, rotation, and revocation of per-user API keys | | `routers.entities` | `entities_router` | `entities_router.router` | Exposes endpoints for entity browsing and memory relationship management | | `routers.requests` | `requests_router` | `requests_router.router` | Provides access to request telemetry logs and execution metrics | Sources: [server/main.py:25-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L25-L28), [server/main.py:169-172](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L169-L172) > [!IMPORTANT] > Trailing slashes are explicitly normalized across all mounted subsystem routes by setting `redirect_slashes=False` during application initialization, preventing unexpected redirection behavior on core memory endpoints. Sources: [server/main.py:146-156](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L146-L156) ## Dynamic Memory Configuration Management ### Runtime State and Configuration Merging The application manages runtime configuration through thread-safe state wrappers using `threading.RLock()` to coordinate access across concurrent requests. Base configurations defined during server bootstrap are merged recursively with persistent overrides stored in the database. The `_merge_config()` function executes a deep copy of the base dictionary and iterates through update keys, recursively updating nested dictionaries such as vector store and LLM provider parameters while overwriting scalar properties. ```python _state_lock = threading.RLock() _current_config: Dict[str, Any] = {} … def _merge_config(base: Dict[str, Any], updates: Dict[str, Any]) -> Dict[str, Any]: merged = deepcopy(base) for key, value in updates.items(): if isinstance(value, dict) and isinstance(merged.get(key), dict): merged[key] = _merge_config(merged[key], value) else: merged[key] = value return merged ``` Sources: [server/server_state.py:9-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/server_state.py#L9-L10), [server/server_state.py:64-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/server_state.py#L64-L73) > [!WARNING] > Configuration updates must be executed through `update_config()` rather than direct mutation to ensure thread safety under `_state_lock` and immediate re-instantiation of the underlying `Memory` runtime instance. Sources: [server/server_state.py:86-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/server_state.py#L86-L96) ### Database Persistence and Redaction Persistent configuration overrides are serialized to JSON format and stored in the database using the `Settings` model under the primary key `"config_overrides"`. The `_save_overrides()` function utilizes SQLAlchemy's PostgreSQL upsert construct (`on_conflict_do_update`) to insert or update override records atomically. If the session factory is unconfigured or a database exception occurs, the failure is caught and logged without crashing the request flow. ```python def _save_overrides(overrides: Dict[str, Any]) -> None: try: if _session_factory is None: return from models import Settings from sqlalchemy.dialects.postgresql import insert session = _session_factory() try: serialized = json.dumps(overrides) stmt = ( insert(Settings) .values(key="config_overrides", value=serialized) .on_conflict_do_update( index_elements=[Settings.key], set_={"value": serialized}, ) ) session.execute(stmt) session.commit() finally: session.close() except Exception: logging.warning("Failed to persist config overrides to database", exc_info=True) ``` Sources: [server/server_state.py:38-62](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/server_state.py#L38-L62) To prevent accidental exposure of sensitive keys in logs and API responses, the server defines a strict set of sensitive configuration keys that must be redacted or filtered during configuration inspection. | Sensitive Key Identifier | Category | Purpose | | :--- | :--- | :--- | | `admin_api_key` | Authentication | Administrative master key configuration | | `api_key` | Authentication | Provider or user API credentials | | `authorization` | Header/Token | HTTP authorization tokens | | `jwt_secret` | Authentication | JSON Web Token signing secret | | `password` | Credential | Database or user password strings | | `password_hash` | Credential | Stored cryptographic password hashes | | `secret` | Credential | General cryptographic secrets | | `token` | Credential | Access and refresh tokens | Sources: [server/main.py:49-58](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L49-L58) ## Rate Limiting Architecture ### Limiter Integration and Client Identification The application integrates rate limiting functionality through the `slowapi` library to control request volume across the API endpoints. A global `Limiter` instance is constructed by leveraging `get_remote_address` as the key function to identify incoming client requests based on their remote network address. ```python from slowapi import Limiter from slowapi.util import get_remote_address ``` Sources: [server/rate_limit.py:1-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/rate_limit.py#L1-L3) This rate limiter instance is bound directly to the FastAPI application state during bootstrap and configured with an exception handler to intercept rate violation events. ```python app.state.limiter = limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) ``` Sources: [server/main.py:157-158](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L157-L158) > [!NOTE] > The application registers `_rate_limit_exceeded_handler` against the `RateLimitExceeded` exception class, ensuring clients receive standardized error responses whenever quota thresholds are breached. Sources: [server/main.py:37-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L37-L38), [server/main.py:158](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L158) ## Exception Classification and Error Translation ### Upstream Provider Fault Mapping The server intercepts runtime exceptions raised by upstream LLM providers, embedding services, vector databases, and underlying storage backends, translating them into structured HTTP `502 Bad Gateway` responses via the `UpstreamError` class. The classification engine inspects exception types and exception class names by walking the cause and context chains (`__cause__` and `__context__`) to correctly identify wrapped provider faults. ```python class UpstreamError(HTTPException): def __init__(self, code: str, detail: str, request_id: str) -> None: super().__init__(status_code=502, detail=detail) self.code = code self.request_id = request_id … def _classify(exc: BaseException | None) -> tuple[str, str]: # Walk the cause/context chain so wrapped provider errors still classify correctly. seen: set[int] = set() current = exc while current is not None and id(current) not in seen: seen.add(id(current)) result = _classify_one(current) if result[0] != "unknown": return result current = current.__cause__ or current.__context__ return ("unknown", "Upstream provider error.") ``` Sources: [server/errors.py:12-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/errors.py#L12-L17), [server/errors.py:54-65](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/errors.py#L54-L65) | Exception Class or Indicator | Upstream Error Code | Default User-Facing Message | | :--- | :--- | :--- | | `AuthenticationError`, `PermissionDeniedError`, status `401`/`403` | `provider_auth_failed` | Provider rejected the request (authentication). Check your LLM provider API key on the Configuration page. | | `RateLimitError`, status `429` | `provider_rate_limited` | Provider rate limit hit. Retry shortly. | | `APITimeoutError`, `TimeoutError` | `provider_timeout` | Provider timed out. Retry shortly. | | `APIConnectionError`, `ConnectionError`, status `>=500` | `provider_unavailable` | Provider is unreachable or returned a server error. | | `BadRequestError`, `UnprocessableEntityError`, status `400`/`422` | `provider_bad_request` | Provider rejected the request as malformed. | | `OperationalError`, `DBAPIError`, `DisconnectionError` | `datastore_unavailable` | The memory database is unreachable. | | `UnexpectedResponse`, `ResponseHandlingException`, `qdrant_client` | `vector_store_unavailable` | The vector store is unreachable or returned an error. | | Fallback (`unknown`) | `unknown` | Upstream provider error. | Sources: [server/errors.py:19-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/errors.py#L19-L51) > [!WARNING] > When an exception is evaluated, `_classify` walks the entire cause and context chain using `id()` tracking to prevent infinite loops, ensuring inner provider exceptions nested inside transport wrappers are correctly categorized. Sources: [server/errors.py:56-64](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/errors.py#L56-L64) ### Error Response Handlers and Correlation When an `UpstreamError` is intercepted by `upstream_error_handler`, the FastAPI application formats a JSON response containing the error detail, error code, and request correlation ID, while attaching the request ID as an `X-Request-ID` header. ```python async def upstream_error_handler(_: Request, exc: UpstreamError) -> JSONResponse: return JSONResponse( status_code=exc.status_code, content={"detail": exc.detail, "code": exc.code, "request_id": exc.request_id}, headers={"X-Request-ID": exc.request_id}, ) ``` Sources: [server/errors.py:79-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/errors.py#L79-L84) The request identifier is managed across asynchronous boundaries using context variables (`request_id_var`) and injected into all log records via a custom logging record factory installed during application startup. ```python request_id_var: ContextVar[str] = ContextVar("request_id", default="-") ``` Sources: [server/errors.py:9-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/errors.py#L87-L94) ```python app.add_exception_handler(UpstreamError, upstream_error_handler) ``` Sources: [server/main.py:159](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L159) ## Related - [Server Authentication](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/server-authentication) - [Server API Endpoints](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/server-api-endpoints) - [Database and Migrations](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/database-and-migrations) --- ## Technical docs: POST Login URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/auth/login ## Request Body Login Request ## Responses ## Try It --- ## Technical docs: POST Refresh Token URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/auth/refresh ## Request Body Refresh Request ## Responses ## Try It --- ## Technical docs: Server Authentication URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/server-authentication
Relevant source files The following files were used as context for generating this wiki page: - [server/auth.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py) - [server/routers/auth.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/auth.py) - [cli/python/src/mem0_cli/commands/init_cmd.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/init_cmd.py) - [integrations/openclaw/cli/commands.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts) - [server/dashboard/src/app/root/dashboard/api-keys/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx) - [server/dashboard/src/app/setup/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx) - [server/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py) - [cli/node/src/commands/init.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/init.ts) - [server/dashboard/src/app/api/auth/refresh/route.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/api/auth/refresh/route.ts) - [server/routers/api_keys.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py) - [server/dashboard/src/lib/auth.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/lib/auth.tsx) - [server/models.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/models.py) - [server/dashboard/src/middleware.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts) - [server/alembic/versions/001_create_users_and_api_keys.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/001_create_users_and_api_keys.py) - [server/dashboard/src/app/auth/login/login-form.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(auth)/login/login-form.tsx) - [server/scripts/reset_admin_password.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/scripts/reset_admin_password.py) - [server/routers/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/__init__.py) - [mem0-ts/src/oss/src/vector_stores/databricks.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/databricks.ts) - [server/scripts/seed.sh](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/scripts/seed.sh) - [server/dashboard/src/utils/api.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api.ts) - [mem0/client/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py) - [mem0-ts/src/oss/src/vector_stores/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/memory.ts) - [server/alembic/versions/005_refresh_token_jtis.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/005_refresh_token_jtis.py) - [server/dashboard/src/utils/api-endpoints.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts) - [server/routers/requests.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/requests.py) - [server/Makefile](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/Makefile) - [server/rate_limit.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/rate_limit.py) - [server/dashboard/src/hooks/use-auth.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-auth.ts) - [server/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/telemetry.py)
## Overview Server Authentication manages credential verification, JWT lifecycle management, API key issuance, and session routing for the self-hosted [FastAPI application](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application) and Next.js admin dashboard. It implements bcrypt password hashing with timing attack mitigation, secure access and refresh token generation, atomic refresh token rotation via database `jti` tracking, and multi-scheme request resolution supporting Bearer tokens, custom `X-API-Key` headers, and legacy administrator keys. Sources: [server/auth.py:11-165](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L11-L165), [server/routers/auth.py:94-165](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/auth.py#L94-L165) ## Authentication Architecture and Credential Verification ### Credential Hashing and Timing Mitigation The server utilizes `passlib` with a `CryptContext` configured for the `bcrypt` hashing scheme with automatic deprecation handling. Passwords and API keys are processed through explicit hashing and verification routines. ```python pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def hash_password(password: str) -> str: return pwd_context.hash(password) def verify_password(plain: str, hashed: str) -> bool: return pwd_context.verify(plain, hashed) ``` Sources: [server/auth.py:22-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L22-L30) To prevent email enumeration via timing attacks during login requests where a target email address does not exist in the database, the authentication flow invokes a dummy password verification method that executes identical bcrypt computation cycles. ```python def dummy_verify_password() -> None: """Burn the same bcrypt cycles as a real verify so login timing doesn't leak whether an email exists.""" pwd_context.dummy_verify() ``` Sources: [server/auth.py:33-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L33-L36) > [!NOTE] > `dummy_verify_password` ensures that missing email lookups consume equivalent CPU time to legitimate password verifications, neutralizing response-time side-channel analysis. > Sources: [server/auth.py:33-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L33-L36), [server/routers/auth.py:128-131](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/auth.py#L128-L131) ### User Resolution Mechanisms Incoming requests can be authenticated via three distinct mechanisms: JSON Web Tokens supplied via Bearer authorization headers, custom API keys transmitted in the `X-API-Key` header, or legacy admin keys. User resolution is decoupled into specific helper functions operating on isolated database sessions. ```mermaid flowchart TD A["Incoming Request"] --> B{"Bearer Token Present?"} B -- Yes --> C["_resolve_user_from_jwt"] B -- No --> D{"X-API-Key Present?"} D -- Yes --> E{"Matches ADMIN_API_KEY?"} E -- Yes --> F["Return None (Admin bypass)"] E -- No --> G["_resolve_user_from_api_key"] D -- No --> H{"AUTH_DISABLED = True?"} H -- Yes --> I["Return None (No-auth mode)"] H -- No --> J["Raise 401 Unauthorized"] ``` Sources: [server/auth.py:144-175](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L144-L175) JWT verification extracts the payload, validates that the token type is explicitly set to `access`, and queries the database for the corresponding user ID stored in the subject (`sub`) claim. ```python def _resolve_user_from_jwt(token: str, db: Session) -> User: payload = decode_token(token) if payload.get("type") != "access": raise HTTPException(status_code=401, detail="Invalid token type.") user = db.get(User, payload.get("sub")) if user is None: raise HTTPException(status_code=401, detail="User not found.") return user ``` Sources: [server/auth.py:116-123](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L116-L123) API key resolution inspects the first 12 characters of the supplied key to isolate candidate records matching the stored `key_prefix`. It then iterates through candidates to verify the plaintext key against stored hashes using `verify_api_key_hash`, updating `last_used_at` upon a successful match. ```python def _resolve_user_from_api_key(key: str, db: Session) -> User: prefix = key[:12] if len(key) >= 12 else key candidates = ( db.execute(select(APIKey).where(APIKey.key_prefix == prefix, APIKey.revoked_at.is_(None))).scalars().all() ) for candidate in candidates: if verify_api_key_hash(key, candidate.key_hash): candidate.last_used_at = datetime.now(timezone.utc) db.commit() user = db.get(User, candidate.created_by) if user is None: raise HTTPException(status_code=401, detail="API key owner not found.") return user raise HTTPException(status_code=401, detail="Invalid API key.") ``` Sources: [server/auth.py:126-141](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L126-L141) > [!CAUTION] > If an API key hash matches a candidate record but the associated creator user (`candidate.created_by`) no longer exists in the database, a `401` HTTP exception is raised with the detail `API key owner not found`. > Sources: [server/auth.py:136-139](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L136-L139) ## User Registration and Setup Status ### Setup Status Inspection and Password Policies The initial deployment workflow relies on determining whether an administrator account has been provisioned. The server exposes a setup status route configured at `/auth/setup-status` via the dashboard API endpoint collection, querying the database row count for `User` records. If the user count is precisely zero, the server responds with a boolean indicator signaling that setup is required. ```python @router.get("/setup-status", response_model=SetupStatusResponse) def setup_status(db: Session = Depends(get_db)): count = db.scalar(select(func.count(User.id))) return SetupStatusResponse(needsSetup=count == 0) ``` Sources: [server/routers/auth.py:88-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/auth.py#L88-L91), [server/dashboard/src/utils/api-endpoints.ts:2](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L2) Password length enforcement is handled uniformly across registration and password modification paths. A mandatory validation helper inspects plain text strings against a fixed constant defining the minimum required character length. | Constant / Function | Value / Type | Purpose | | :--- | :--- | :--- | | `MIN_PASSWORD_LENGTH` | `8` (integer) | Enforces the minimum character requirement for administrator passwords | | `_require_password_length` | Function (`str -> None`) | Evaluates password length and raises HTTP 400 if constraints are violated | Sources: [server/routers/auth.py:28-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/auth.py#L28-L36) > [!WARNING] > If a password supplied during administrator registration contains fewer than 8 characters, `_require_password_length` immediately aborts execution by raising an `HTTPException` with status code 400. > Sources: [server/routers/auth.py:31-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/auth.py#L31-L36), [server/routers/auth.py:98](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/auth.py#L98) ### Admin Account Registration and Telemetry Execution Admin registration is permanently locked down after the first account is successfully persisted. The registration endpoint checks if any user already exists, hashes the verified password, assigns the `"admin"` role, and commits the transaction. To prevent race conditions from concurrent registration requests, database integrity errors trigger a rollback and raise a forbidden exception. ```python @router.post("/register", response_model=TokenResponse) @limiter.limit("5/minute") def register(request: Request, body: RegisterRequest, db: Session = Depends(get_db)): """Create the first admin account. Blocked once any user exists.""" _require_password_length(body.password) if db.scalar(select(func.count(User.id))) > 0: raise HTTPException(status_code=403, detail="Registration is closed. An admin account already exists.") user = User( name=body.name, email=body.email, password_hash=hash_password(body.password), role="admin", ) db.add(user) try: db.commit() except IntegrityError: db.rollback() raise HTTPException(status_code=403, detail="Registration is closed. An admin account already exists.") db.refresh(user) capture_admin_registered(email=body.email) return TokenResponse( access_token=create_access_token(str(user.id), user.role), refresh_token=create_refresh_token(str(user.id), db), ) ``` Sources: [server/routers/auth.py:94-122](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/auth.py#L94-L122) Upon successful creation of the administrator account, the execution flow triggers anonymous telemetry reporting via a specific function sequence. This sequence captures the event state, loads persistent state settings, generates installation identifiers, and initializes the PostHog client interface. 1. `register` — Invokes `capture_admin_registered(email=body.email)` after committing the new user to the database. 2. `capture_admin_registered` — Passes arguments to `_capture_once(email, "admin_registered", "admin_registered_sent_at")`. 3. `_capture_once` — Verifies telemetry enablement, thread locks state access, and checks if the event was previously dispatched. 4. `_get_client` — Resolves or instantiates the PostHog tracking client using `PROJECT_API_KEY` and `HOST`. 5. `_load_state` — Reads persistent metrics configuration from `STATE_PATH`. 6. `_install_id` — Generates or retrieves the unique installation UUID. 7. `_save_state` — Serializes updated telemetry status back to disk. ```mermaid sequenceDiagram autonumber participant Router as server/routers/auth.py participant Telemetry as server/telemetry.py participant State as telemetry.json participant PostHog as PostHog Client Router->>Telemetry: capture_admin_registered(email) Telemetry->>Telemetry: _capture_once(email, "admin_registered", ...) Telemetry->>Telemetry: _load_state() Telemetry->>State: Read JSON file State-->>Telemetry: Return state dict Telemetry->>Telemetry: _install_id(state) Telemetry->>Telemetry: _get_client() Telemetry->>PostHog: client.capture(distinct_id, event, properties) Telemetry->>Telemetry: _save_state(state) Telemetry->>State: Write updated JSON ``` Sources: [server/routers/auth.py:117](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/auth.py#L117), [server/telemetry.py:35-109](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/telemetry.py#L35-L109) > [!NOTE] > Anonymous telemetry transmits domain-level email characteristics, server version strings, and installation UUIDs through PostHog, but can be completely disabled by setting the environment variable `MEM0_TELEMETRY=false`. > Sources: [server/telemetry.py:1-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/telemetry.py#L1-L27) ## JWT Token Generation and Invalidation ### Access and Refresh Token Issuance Authentication sessions utilize JSON Web Tokens encoded using the HS256 algorithm with a secret key retrieved via `_get_secret()`. If `JWT_SECRET` is unconfigured, the helper raises an HTTP 500 error. Access tokens carry claims for the user ID (`sub`), role, expiration timestamp (`exp`), and a type identifier set to `"access"`, with a fixed lifetime defined by `ACCESS_TOKEN_EXPIRE_MINUTES`. ```python def create_access_token(user_id: str, role: str) -> str: expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) payload = {"sub": user_id, "role": role, "exp": expire, "type": "access"} return jwt.encode(payload, _get_secret(), algorithm=JWT_ALGORITHM) ``` Sources: [server/auth.py:51-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L51-L60) Refresh tokens operate with an extended expiration window governed by `REFRESH_TOKEN_EXPIRE_DAYS` and incorporate a unique JWT ID (`jti`) generated as a UUID. When a refresh token is created, a corresponding record is persisted in the database via the `RefreshTokenJti` model to track its usage and expiration state. ```python def create_refresh_token(user_id: str, db: Session) -> str: expire = datetime.now(timezone.utc) + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS) jti = uuid.uuid4() db.add(RefreshTokenJti(jti=jti, user_id=uuid.UUID(user_id), expires_at=expire)) db.commit() payload = {"sub": user_id, "exp": expire, "jti": str(jti), "type": "refresh"} return jwt.encode(payload, _get_secret(), algorithm=JWT_ALGORITHM) ``` Sources: [server/auth.py:63-70](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L63-L70) ### Refresh Token JTI Database Schema The persistence layer for single-use refresh token tracking is defined by the `refresh_token_jtis` table created in Alembic migration version `005`. The table stores the primary key `jti`, a cascade-deleting foreign key to `users.id`, expiration timestamps with timezone support, and a `used_at` timestamp marking consumption. | Column | Type | Constraints / Attributes | Purpose | | :--- | :--- | :--- | :--- | | `jti` | `sa.Uuid()` | Primary Key | Unique JWT identifier for the refresh token | | `user_id` | `sa.Uuid()` | Foreign Key (`users.id`, ondelete="CASCADE"), `nullable=False` | Owner account associated with the refresh token | | `expires_at` | `sa.DateTime(timezone=True)` | `nullable=False`, Indexed (`ix_refresh_token_jtis_expires_at`) | Expiration boundary for token validity | | `used_at` | `sa.DateTime(timezone=True)` | `nullable=True` | Timestamp recording when the token was consumed | | `created_at` | `sa.DateTime(timezone=True)` | `nullable=False`, Default `sa.func.now()` | Audit timestamp of JTI record creation | ```python def upgrade() -> None: op.create_table( "refresh_token_jtis", sa.Column("jti", sa.Uuid(), primary_key=True), sa.Column( "user_id", sa.Uuid(), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False, ), sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False), sa.Column("used_at", sa.DateTime(timezone=True), nullable=True), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), ) op.create_index("ix_refresh_token_jtis_expires_at", "refresh_token_jtis", ["expires_at"]) ``` Sources: [server/alembic/versions/005_refresh_token_jtis.py:20-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/005_refresh_token_jtis.py#L20-L35) ### Single-Use Token Rotation and Concurrency Control To prevent token replay attacks and enforce single-use mechanics, refresh tokens are consumed atomically using a conditional database update. The `consume_refresh_jti` function attempts to set `used_at` only on rows where the `jti` matches, `used_at` is currently null, and `expires_at` exceeds the current UTC time. ```python def consume_refresh_jti(jti: str, db: Session) -> None: try: jti_uuid = uuid.UUID(jti) except (TypeError, ValueError): raise HTTPException(status_code=401, detail="Refresh token is no longer valid.") now = datetime.now(timezone.utc) result = db.execute( update(RefreshTokenJti) .where( RefreshTokenJti.jti == jti_uuid, RefreshTokenJti.used_at.is_(None), RefreshTokenJti.expires_at > now, ) .values(used_at=now) ) if result.rowcount == 0: raise HTTPException(status_code=401, detail="Refresh token is no longer valid.") db.commit() ``` Sources: [server/auth.py:72-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L72-L94) > [!WARNING] > The conditional `UPDATE` statement in `consume_refresh_jti` closes read-check-write race conditions during concurrent refresh requests. Because concurrent replays target the exact same row, at most one update affects a row and any concurrent replayed requests observe a `rowcount` of 0, immediately triggering an HTTP 401 exception. > Sources: [server/auth.py:72-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L72-L94) The token refresh router endpoint validates the incoming refresh token payload type, extracts the `jti` claim, checks user existence, invokes `consume_refresh_jti`, and issues a brand new access and refresh token pair upon success. ```python @router.post("/refresh", response_model=TokenResponse) @limiter.limit("20/minute") def refresh(request: Request, body: RefreshRequest, db: Session = Depends(get_db)): payload = decode_token(body.refresh_token) if payload.get("type") != "refresh": raise HTTPException(status_code=401, detail="Invalid token type.") jti = payload.get("jti") if not jti: raise HTTPException(status_code=401, detail="Refresh token is no longer valid.") user = db.get(User, payload["sub"]) if user is None: raise HTTPException(status_code=401, detail="User not found.") consume_refresh_jti(jti, db) return TokenResponse( access_token=create_access_token(str(user.id), user.role), refresh_token=create_refresh_token(str(user.id), db), ) ``` Sources: [server/routers/auth.py:144-164](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/auth.py#L144-L164) ## API Key Generation and Resolution ### Key Generation and Hashing Mechanics The server generates new API keys using cryptographic randomness combined with a fixed string prefix for identification. The `generate_api_key` function generates 32 bytes of URL-safe random data via `secrets.token_urlsafe`, prepends the `m0sk_` format identifier, extracts a 12-character prefix for database indexing, and computes a secure bcrypt hash of the full plaintext key using `CryptContext`. ```python def generate_api_key() -> tuple[str, str, str]: """Returns (full_key, prefix, hash).""" raw = secrets.token_urlsafe(32) full_key = f"m0sk_{raw}" prefix = full_key[:12] key_hash = pwd_context.hash(full_key) return full_key, prefix, key_hash ``` Sources: [server/auth.py:38-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L38-L44) | Component | Function / Value | Purpose | | :--- | :--- | :--- | | Randomness | `secrets.token_urlsafe(32)` | Generates 32 bytes of secure entropy | | Full Key | `f"m0sk_{raw}"` | Complete plaintext API key returned to the caller once | | Prefix | `full_key[:12]` | First 12 characters used for efficient key lookup | | Hashing Scheme | `pwd_context.hash(full_key)` | Secure bcrypt digest stored in the database | Sources: [server/auth.py:38-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L38-L44) ### Request Header Resolution and Authentication Flow Incoming requests can authenticate through multiple headers inspected by `verify_auth` and dependency helpers. The server reads credentials from either the `Authorization: Bearer ` header via `bearer_scheme` or the `X-API-Key` header via `api_key_header`. ```python bearer_scheme = HTTPBearer(auto_error=False) api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False) ``` Sources: [server/auth.py:104-105](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L104-L105) When an `X-API-Key` header is present, the resolver checks against the global environment variable `ADMIN_API_KEY` using constant-time comparison `secrets.compare_digest`. If it does not match the admin override, it queries the database for active records matching the key's 12-character prefix where `revoked_at` is null. It then verifies each candidate hash, updates `last_used_at` upon success, and resolves the owning user. ```python def _resolve_user_from_api_key(key: str, db: Session) -> User: prefix = key[:12] if len(key) >= 12 else key candidates = ( db.execute(select(APIKey).where(APIKey.key_prefix == prefix, APIKey.revoked_at.is_(None))).scalars().all() ) for candidate in candidates: if verify_api_key_hash(key, candidate.key_hash): candidate.last_used_at = datetime.now(timezone.utc) db.commit() user = db.get(User, candidate.created_by) if user is None: raise HTTPException(status_code=401, detail="API key owner not found.") return user raise HTTPException(status_code=401, detail="Invalid API key.") ``` Sources: [server/auth.py:126-141](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L126-L141) > [!NOTE] > Database sessions for authentication are opened lazily inside short-lived context managers only on branches requiring database queries. This ensures pooled connections are not held open for the entire lifetime of long-running requests. > Sources: [server/auth.py:149-156](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py#L149-L156) ## Dashboard Session Refresh and Middleware ### Session Cookie Rotation and Refresh Route The Next.js dashboard manages authentication persistence via an HTTP-only cookie containing the refresh token (`mem0_refresh_token`). The server route handler exposes `POST`, `PUT`, and `DELETE` methods at `/api/auth/refresh` to rotate tokens, update sessions, and clear credentials. ```typescript const COOKIE_NAME = "mem0_refresh_token"; const COOKIE_OPTIONS = { httpOnly: true, secure: shouldUseSecureCookie(), sameSite: "lax" as const, path: "/", maxAge: 30 * 24 * 60 * 60, // 30 days }; ``` Sources: [server/dashboard/src/app/api/auth/refresh/route.ts:6-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/api/auth/refresh/route.ts#L6-L27) When a `POST` request is received, the route extracts the refresh token from the cookie store and forwards it to the backend server endpoint. If the backend accepts the token, a new refresh token is stored in the cookie, and a short-lived access token is returned in the response body. If verification fails, the refresh token cookie is deleted and a 401 status is returned. | HTTP Method | Route Handler Action | Response Behavior | | :--- | :--- | :--- | | `POST` | Forwards stored `mem0_refresh_token` to backend refresh endpoint | Sets rotated refresh token cookie; returns JSON with new `access_token` | | `PUT` | Validates and sets `refresh_token` from request body into cookie | Returns `{ ok: true }` or 400 error on missing token | | `DELETE` | Deletes `mem0_refresh_token` cookie | Returns `{ ok: true }` | Sources: [server/dashboard/src/app/api/auth/refresh/route.ts:29-75](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/api/auth/refresh/route.ts#L29-L75) > [!WARNING] > If `DASHBOARD_URL` is omitted from the environment, `shouldUseSecureCookie()` falls back to checking `process.env.NODE_ENV === "production"`. In local environments configured with custom proxy setups, ensure `DASHBOARD_URL` explicitly reflects the protocol scheme to prevent cookie transmission failures over secure transport. > Sources: [server/dashboard/src/app/api/auth/refresh/route.ts:8-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/api/auth/refresh/route.ts#L8-L19) ### Next.js Middleware Token Routing and Setup Enforcements The Next.js middleware intercepts incoming requests across the application, bypassing static assets and authentication endpoints defined in `PUBLIC_PATHS`. It inspects the presence of the `mem0_refresh_token` cookie and queries the backend setup status when users visit root or setup routes. ```typescript const PUBLIC_PATHS = [ "/_next", "/api/auth", "/api/health", "/fonts", "/favicon", ]; ``` Sources: [server/dashboard/src/middleware.ts:5-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L5-L11) The matcher configuration excludes static assets and icons from middleware evaluation, while routing protected navigation targets appropriately. ```typescript export const config = { matcher: ["/((?!_next/static|_next/image|favicon.ico|fonts|images|icons).*)"], }; ``` Sources: [server/dashboard/src/middleware.ts:65-67](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L65-L67) > [!NOTE] > When requests hit the root path `/`, the middleware inspects `hasRefreshToken`. Unauthenticated users are redirected to `/login`, while authenticated users are directed straight to `/dashboard/requests`. > Sources: [server/dashboard/src/middleware.ts:46-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L46-L50) ## Administrative Password Reset Script ### Utility Execution and Credential Updates The administrative password reset utility is designed to run directly inside the running container environment via Python. It reads the target user's `EMAIL` and new `PASSWORD` values directly from environment variables. Sources: [server/scripts/reset_admin_password.py:1-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/scripts/reset_admin_password.py#L1-L17) ```python MIN_PASSWORD_LENGTH = 8 ``` Sources: [server/scripts/reset_admin_password.py:12-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/scripts/reset_admin_password.py#L12-L12) The script opens a database session using `SessionLocal`, queries the `User` model by email address, computes a fresh bcrypt password hash via `hash_password`, and commits the updated credential to the database. Sources: [server/scripts/reset_admin_password.py:27-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/scripts/reset_admin_password.py#L27-L35) | Exit Code | Condition | Action / Stderr Output | | :--- | :--- | :--- | | `0` | Password successfully updated | Prints confirmation message: `Password reset for .` | | `1` | User email not found in database | Prints error: `error: no user found with email .` | | `2` | Missing variables or validation failure | Prints error: `error: EMAIL and PASSWORD environment variables are required.` or minimum length violation | Sources: [server/scripts/reset_admin_password.py:19-37](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/scripts/reset_admin_password.py#L19-L37) > [!WARNING] > The password update script enforces a strict minimum length of 8 characters. Providing a shorter password causes the script to abort with exit code 2 before querying or modifying the database. > Sources: [server/scripts/reset_admin_password.py:12-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/scripts/reset_admin_password.py#L12-L25) ### Makefile Integration Container orchestration commands expose the reset script through a dedicated `reset-admin-password` Makefile target. This target validates that both `EMAIL` and `PASSWORD` parameters are supplied before executing the command inside the docker compose container environment. Sources: [server/Makefile:50-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/Makefile#L50-L54) ```Makefile reset-admin-password: @test -n "$(EMAIL)" || (echo "usage: make reset-admin-password EMAIL= PASSWORD=" && exit 2) @test -n "$(PASSWORD)" || (echo "usage: make reset-admin-password EMAIL= PASSWORD=" && exit 2) @docker compose exec -T -e EMAIL="$(EMAIL)" -e PASSWORD="$(PASSWORD)" -e PYTHONPATH=/app mem0 python scripts/reset_admin_password.py ``` Sources: [server/Makefile:50-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/Makefile#L50-L54) ## Related - [FastAPI Application](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application) - [Database and Migrations](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/database-and-migrations) --- ## Technical docs: Database and Migrations URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/database-and-migrations
Relevant source files The following files were used as context for generating this wiki page: - [server/models.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/models.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [server/alembic/versions/001_create_users_and_api_keys.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/001_create_users_and_api_keys.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [server/alembic/env.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/env.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [server/routers/auth.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/auth.py) - [server/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py) - [server/alembic/versions/002_create_request_logs.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/002_create_request_logs.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [server/routers/api_keys.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py) - [server/db.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/db.py) - [server/alembic/versions/003_create_settings.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/003_create_settings.py) - [server/auth.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/auth.py) - [server/alembic.ini](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic.ini) - [mem0/memory/storage.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py) - [server/alembic/versions/005_refresh_token_jtis.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/005_refresh_token_jtis.py) - [server/routers/requests.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/requests.py) - [server/server_state.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/server_state.py) - [server/alembic/versions/006_request_logs_brin.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/006_request_logs_brin.py) - [server/scripts/prune_request_logs.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/scripts/prune_request_logs.py) - [mem0/configs/vector_stores/oracledb.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/vector_stores/oracledb.py) - [mem0/configs/base.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/base.py) - [server/init-db.sh](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/init-db.sh) - [server/alembic/versions/004_unique_admin_role.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/004_unique_admin_role.py)
## Overview The relational database architecture relies on SQLAlchemy Object-Relational Mapping (ORM) and Alembic migrations to manage persistence, connection pooling, and schema evolution for user authentication, API keys, audit logs, and system settings. Using declarative base classes and dynamic connection string resolution, the engine connects to PostgreSQL instances while maintaining session lifecycles through FastAPI dependency injection. Sources: [server/db.py:1-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/db.py#L1-L31) Alembic coordinates schema versioning through structured migration scripts that track incremental database modifications, supporting both offline SQL generation and online transactional execution against target metadata. Sources: [server/alembic/env.py:1-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/env.py#L1-L44), [server/alembic/versions/001_create_users_and_api_keys.py:1-49](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/001_create_users_and_api_keys.py#L1-L49) ## Database Engine Configuration and Session Lifecycle ### Dynamic URL Resolution and Engine Initialization The database engine constructs its connection string dynamically by reading environment variables with default fallback values. The `_build_database_url()` function inspects `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `APP_DB_NAME`, falling back respectively to `postgres`, `5432`, `postgres`, `postgres`, and `mem0_app`. It formats these components into a PostgreSQL connection URI using the `postgresql+psycopg` driver scheme. ```python def _build_database_url() -> str: host = os.environ.get("POSTGRES_HOST", "postgres") port = os.environ.get("POSTGRES_PORT", "5432") user = os.environ.get("POSTGRES_USER", "postgres") password = os.environ.get("POSTGRES_PASSWORD", "postgres") db = os.environ.get("APP_DB_NAME", "mem0_app") return f"postgresql+psycopg://{user}:{password}@{host}:{port}/{db}" ``` Sources: [server/db.py:7-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/db.py#L7-L13) The global `engine` instance is created by passing the generated database URL to `create_engine()` with `pool_pre_ping=True` enabled to verify connection health before checkout. ```python engine = create_engine(_build_database_url(), pool_pre_ping=True) ``` Sources: [server/db.py:16-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/db.py#L16-L16) > [!NOTE] > The database initialization script (`init-db.sh`) independently provisions the `mem0_app` application database using `psql` conditional execution (`WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mem0_app')`), while the default `postgres` database is reserved for pgvector memory storage. > Sources: [server/init-db.sh:4-9](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/init-db.sh#L4-L9) ### Session Factory and Dependency Injection Session management relies on `SessionLocal`, configured as a sessionmaker bound to the global engine with `autoflush=False` and `expire_on_commit=False`. ```python SessionLocal = sessionmaker(bind=engine, autoflush=False, expire_on_commit=False) ``` Sources: [server/db.py:18-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/db.py#L18-L18) Models inherit from a declarative base class `Base` constructed using `DeclarativeBase`. ```python class Base(DeclarativeBase): pass ``` Sources: [server/db.py:21-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/db.py#L21-L23) The `get_db()` FastAPI dependency yields a transactional `Session` instance and guarantees closure in a `finally` block regardless of request outcomes. ```python def get_db(): """FastAPI dependency that yields a SQLAlchemy session.""" db: Session = SessionLocal() try: yield db finally: db.close() ``` Sources: [server/db.py:25-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/db.py#L25-L31) > [!WARNING] > Failing to consume `get_db()` within a FastAPI route or background task dependency injection context leaves connections unreturned to the pool, risking connection exhaustion under concurrent request load. > Sources: [server/db.py:25-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/db.py#L25-L31) ## Identity and Authentication Models ### User Accounts and Role Constraints The `User` model defines the primary authentication entity managed within the application database (`server/models.py`). Each user record utilizes a UUID primary key, stores a string representation of their name and email, and retains a hashed password string alongside role permissions. ```python class User(Base): __tablename__ = "users" id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=_new_uuid) name: Mapped[str] = mapped_column(String(255)) email: Mapped[str] = mapped_column(String(255), unique=True, index=True) password_hash: Mapped[str] = mapped_column(Text) role: Mapped[str] = mapped_column(String(20), default="admin") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow) last_login_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) ``` Sources: [server/models.py:18-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/models.py#L18-L28) Database schema evolution supports administrative scoping via migration `004_unique_admin_role.py`, which applies a partial unique index on the `role` column where the role equals `'admin'`. This ensures at most one administrator account can exist concurrently. ```python def upgrade() -> None: op.create_index( "ix_users_only_one_admin", "users", ["role"], unique=True, postgresql_where=sa.text("role = 'admin'"), ) ``` Sources: [server/alembic/versions/004_unique_admin_role.py:20-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/004_unique_admin_role.py#L20-L27) > [!WARNING] > Attempting to insert a second user with the `admin` role directly into PostgreSQL violates the `ix_users_only_one_admin` partial unique index constraint, triggering a database integrity error. > Sources: [server/alembic/versions/004_unique_admin_role.py:20-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/004_unique_admin_role.py#L20-L27) ### API Keys and Relational Mappings The `APIKey` model manages programmatic access credentials linked to specific user accounts. Each key entry stores a short prefix for identification, a secure cryptographic hash of the full key value, an administrative label, and timestamps tracking usage and revocation. ```python class APIKey(Base): __tablename__ = "api_keys" id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=_new_uuid) key_prefix: Mapped[str] = mapped_column(String(12)) key_hash: Mapped[str] = mapped_column(Text) label: Mapped[str] = mapped_column(String(255)) created_by: Mapped[uuid.UUID] = mapped_column(ForeignKey("users.id", ondelete="CASCADE")) last_used_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow) ``` Sources: [server/models.py:30-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/models.py#L30-L41) The initial schema migration establishes both the `users` and `api_keys` tables concurrently, enforcing foreign key cascades on deletion. ```python op.create_table( "api_keys", sa.Column("id", sa.Uuid(), primary_key=True), sa.Column("key_prefix", sa.String(12), nullable=False), sa.Column("key_hash", sa.Text(), nullable=False), sa.Column("label", sa.String(255), nullable=False), sa.Column("created_by", sa.Uuid(), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False), sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=True), sa.Column("revoked_at", sa.DateTime(timezone=True), nullable=True), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), ) ``` Sources: [server/alembic/versions/001_create_users_and_api_keys.py:34-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/001_create_users_and_api_keys.py#L34-L44) ### Single-Use Refresh Token JTIs The `RefreshTokenJti` model implements storage for JWT JWT-ID (`jti`) claims to enforce single-use refresh token semantics. ```python class RefreshTokenJti(Base): __tablename__ = "refresh_token_jtis" jti: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=_new_uuid) user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("users.id", ondelete="CASCADE")) expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True)) used_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow) ``` Sources: [server/models.py:55-63](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/models.py#L55-L63) Migration `005_refresh_token_jtis.py` adds this table along with an expiration index to accelerate token cleanups and validation lookups. ```python def upgrade() -> None: op.create_table( "refresh_token_jtis", sa.Column("jti", sa.Uuid(), primary_key=True), sa.Column( "user_id", sa.Uuid(), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False, ), sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False), sa.Column("used_at", sa.DateTime(timezone=True), nullable=True), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), ) op.create_index("ix_refresh_token_jtis_expires_at", "refresh_token_jtis", ["expires_at"]) ``` Sources: [server/alembic/versions/005_refresh_token_jtis.py:20-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/005_refresh_token_jtis.py#L20-L34) > [!IMPORTANT] > When a refresh token is successfully exchanged, its corresponding `used_at` timestamp is populated, preventing any subsequent reuse attempt even if the token remains cryptographically valid before its `expires_at` threshold. > Sources: [server/models.py:55-63](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/models.py#L55-L63), [server/alembic/versions/005_refresh_token_jtis.py:20-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/005_refresh_token_jtis.py#L20-L34) ## Request Logging and Settings Models ### Request Logging Model and Migration The `RequestLog` SQLAlchemy model captures telemetry for incoming HTTP requests [Server API Endpoints](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/server-api-endpoints). Each log record tracks the HTTP method, request path, response status code, latency in milliseconds, authentication mechanism used, and creation timestamp. ```python class RequestLog(Base): __tablename__ = "request_logs" id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=_new_uuid) method: Mapped[str] = mapped_column(String(16)) path: Mapped[str] = mapped_column(String(512)) status_code: Mapped[int] = mapped_column(Integer) latency_ms: Mapped[float] = mapped_column(Float) auth_type: Mapped[str] = mapped_column(String(32), default="none") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow) ``` Sources: [server/models.py:43-53](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/models.py#L43-L53) Migration `002_create_request_logs.py` provisions the `request_logs` relation and creates a B-tree index on `created_at` to support time-range queries and pruning operations. ```python def upgrade() -> None: op.create_table( "request_logs", sa.Column("id", sa.Uuid(), primary_key=True), sa.Column("method", sa.String(16), nullable=False), sa.Column("path", sa.String(512), nullable=False), sa.Column("status_code", sa.Integer(), nullable=False), sa.Column("latency_ms", sa.Float(), nullable=False), sa.Column("auth_type", sa.String(32), nullable=False, server_default="none"), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), ) op.create_index("ix_request_logs_created_at", "request_logs", ["created_at"]) ``` Sources: [server/alembic/versions/002_create_request_logs.py:20-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/002_create_request_logs.py#L20-L31) > [!NOTE] > The `auth_type` column defaults to `"none"` at the database schema level via a server default expression, ensuring unauthenticated or public routes are explicitly labeled even when no credential extractor populates the context. > Sources: [server/models.py:43-53](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/models.py#L43-L53), [server/alembic/versions/002_create_request_logs.py:28-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/002_create_request_logs.py#L28-L28) ### Settings Model and Migration The `Settings` model defines a key-value store used to persist dynamic application configurations [Dashboard Views](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/dashboard-views). The model uses `key` as the primary key string and updates the `updated_at` timestamp automatically via SQLAlchemy's `onupdate` hook. ```python class Settings(Base): __tablename__ = "settings" key: Mapped[str] = mapped_column(String(255), primary_key=True) value: Mapped[str] = mapped_column(Text) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, ) ``` Sources: [server/models.py:65-74](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/models.py#L65-L74) Migration `003_create_settings.py` creates the `settings` table with a corresponding `updated_at` server timestamp default. ```python def upgrade() -> None: op.create_table( "settings", sa.Column("key", sa.String(255), primary_key=True), sa.Column("value", sa.Text(), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), ) ``` Sources: [server/alembic/versions/003_create_settings.py:20-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/003_create_settings.py#L20-L26) ## Alembic Migration Environment and Versioning ### Configuration and Metadata Initialization The Alembic migration environment is configured via `server/alembic.ini` and orchestrated by `server/alembic/env.py`. The `alembic.ini` file sets the default script location to `alembic`, prepends the current working directory to `sys.path`, and defines a default PostgreSQL connection string pointing to `postgresql+psycopg://postgres:postgres@postgres:5432/mem0_app`. Sources: [server/alembic.ini:1-4](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic.ini#L1-L4) ```ini [alembic] script_location = alembic prepend_sys_path = . sqlalchemy.url = postgresql+psycopg://postgres:postgres@postgres:5432/mem0_app ``` Sources: [server/alembic.ini:1-4](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic.ini#L1-L4) The `env.py` script imports `Base` and `_build_database_url` from `db`, along with the `models` module to ensure `Base.metadata` registers all relational table definitions. It configures logging handlers from `alembic.ini` and dynamically overrides `sqlalchemy.url` using `_build_database_url()`. Sources: [server/alembic/env.py:1-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/env.py#L1-L20) ```python from db import Base, _build_database_url import models # noqa: F401 config = context.config if config.config_file_name is not None: fileConfig(config.config_file_name) target_metadata = Base.metadata config.set_main_option("sqlalchemy.url", _build_database_url()) ``` Sources: [server/alembic/env.py:6-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/env.py#L6-L20) ### Offline and Online Runner Execution Alembic execution branches depending on whether `context.is_offline_mode()` evaluates to true. In offline mode, migrations generate SQL scripts without establishing a live database connection, configuring `context` with `literal_binds=True`. Sources: [server/alembic/env.py:22-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/env.py#L22-L27) ```python def run_migrations_offline() -> None: url = config.get_main_option("sqlalchemy.url") context.configure(url=url, target_metadata=target_metadata, literal_binds=True) with context.begin_transaction(): context.run_migrations() ``` Sources: [server/alembic/env.py:22-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/env.py#L22-L27) In online mode, Alembic builds an engine using `engine_from_config` with `poolclass=pool.NullPool`, acquires a live connection, and executes migration operations within a transactional block. Sources: [server/alembic/env.py:29-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/env.py#L29-L39) ```python def run_migrations_online() -> None: connectable = engine_from_config( config.get_section(config.config_ini_section, {}), prefix="sqlalchemy.", poolclass=pool.NullPool, ) with connectable.connect() as connection: context.configure(connection=connection, target_metadata=target_metadata) with context.begin_transaction(): context.run_migrations() ``` Sources: [server/alembic/env.py:29-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/env.py#L29-L39) > [!NOTE] > Database initialization is bootstrapped prior to migration runs using the `server/init-db.sh` script, which executes an idempotent `psql` command to create the `mem0_app` database if it does not already exist. > Sources: [server/init-db.sh:1-9](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/init-db.sh#L1-L9) ## Schema Evolution and Index Optimization ### Migration History Across Releases The database schema evolves through six sequential Alembic migration revisions that progressively establish core application tables, audit logs, dynamic settings, authentication components, administrative constraints, and storage optimizations. Sources: [server/alembic/versions/001_create_users_and_api_keys.py:1-49](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/001_create_users_and_api_keys.py#L1-L49), [server/alembic/versions/002_create_request_logs.py:1-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/002_create_request_logs.py#L1-L36), [server/alembic/versions/003_create_settings.py:1-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/003_create_settings.py#L1-L30), [server/alembic/versions/004_unique_admin_role.py:1-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/004_unique_admin_role.py#L1-L31), [server/alembic/versions/005_refresh_token_jtis.py:1-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/005_refresh_token_jtis.py#L1-L39), [server/alembic/versions/006_request_logs_brin.py:1-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/006_request_logs_brin.py#L1-L26) | Revision ID | Revises | Description | Core Tables / Operations | | :--- | :--- | :--- | :--- | | `001` | `None` | Create users and api_keys tables | `users`, `api_keys`, `ix_users_email` | | `002` | `001` | Create request_logs table | `request_logs`, `ix_request_logs_created_at` | | `003` | `002` | Create settings table | `settings` | | `004` | `003` | Enforce at-most-one admin via partial unique index | `ix_users_only_one_admin` (`postgresql_where=sa.text("role = 'admin'")`) | | `005` | `004` | Create refresh_token_jtis table for single-use refresh tokens | `refresh_token_jtis`, `ix_refresh_token_jtis_expires_at` | | `006` | `005` | Swap request_logs.created_at btree index for a BRIN index | Drop `ix_request_logs_created_at`, create BRIN index on `request_logs(created_at)` | Sources: [server/alembic/versions/001_create_users_and_api_keys.py:1-49](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/001_create_users_and_api_keys.py#L1-L49), [server/alembic/versions/002_create_request_logs.py:1-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/002_create_request_logs.py#L1-L36), [server/alembic/versions/003_create_settings.py:1-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/003_create_settings.py#L1-L30), [server/alembic/versions/004_unique_admin_role.py:1-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/004_unique_admin_role.py#L1-L31), [server/alembic/versions/005_refresh_token_jtis.py:1-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/005_refresh_token_jtis.py#L1-L39), [server/alembic/versions/006_request_logs_brin.py:1-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/006_request_logs_brin.py#L1-L26) ### Role Constraints and Partial Indexing Revision `004` introduces a restrictive uniqueness invariant on user roles to ensure that at most one administrator account can exist within the system simultaneously. This is implemented via a PostgreSQL partial unique index named `ix_users_only_one_admin` scoped exclusively to rows where `role = 'admin'`. Sources: [server/alembic/versions/004_unique_admin_role.py:20-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/004_unique_admin_role.py#L20-L27) ```python def upgrade() -> None: op.create_index( "ix_users_only_one_admin", "users", ["role"], unique=True, postgresql_where=sa.text("role = 'admin'"), ) ``` Sources: [server/alembic/versions/004_unique_admin_role.py:20-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/004_unique_admin_role.py#L20-L27) > [!IMPORTANT] > The partial unique index `ix_users_only_one_admin` relies on PostgreSQL-specific `postgresql_where` syntax. Attempting to run migration `004` on a non-PostgreSQL backend will fail because standard SQL does not support filtered unique indexes in the same manner. > Sources: [server/alembic/versions/004_unique_admin_role.py:20-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/004_unique_admin_role.py#L20-L27) ### Swapping B-tree for BRIN Indexes on Time-Series Logs The `request_logs` table accumulates high-volume append-only telemetry data ordered naturally by insertion timestamp. Initially created with a standard B-tree index on `created_at` in revision `002`, migration `006` explicitly drops `ix_request_logs_created_at` and replaces it with a Block Range Index (BRIN) via raw SQL execution. Sources: [server/alembic/versions/002_create_request_logs.py:31-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/002_create_request_logs.py#L31-L31), [server/alembic/versions/006_request_logs_brin.py:19-21](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/006_request_logs_brin.py#L19-L21) ```python def upgrade() -> None: op.drop_index("ix_request_logs_created_at", table_name="request_logs") op.execute("CREATE INDEX ix_request_logs_created_at ON request_logs USING BRIN (created_at)") ``` Sources: [server/alembic/versions/006_request_logs_brin.py:19-21](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/006_request_logs_brin.py#L19-L21) > [!TIP] > BRIN indexes consume a fraction of the disk space required by B-tree indexes on large append-only time-series tables like `request_logs`, because they store summary metadata per physical page range instead of maintaining individual keys per row. > Sources: [server/alembic/versions/006_request_logs_brin.py:19-21](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/alembic/versions/006_request_logs_brin.py#L19-L21) ## Log Retention and Maintenance Automation ### Retention Window Policies and Execution The server maintains audit telemetry through a dedicated pruning utility designed to purge old records from the `request_logs` table based on a configurable age threshold. The retention duration is controlled by the `REQUEST_LOG_RETENTION_DAYS` environment variable, which defaults to 30 days if left unspecified or empty. Sources: [server/scripts/prune_request_logs.py:1-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/scripts/prune_request_logs.py#L1-L17) ```python def main() -> int: raw = os.environ.get("REQUEST_LOG_RETENTION_DAYS", "").strip() or "30" try: retention_days = int(raw) except ValueError: sys.stderr.write("REQUEST_LOG_RETENTION_DAYS must be an integer.\n") return 1 if retention_days < 1: sys.stderr.write("REQUEST_LOG_RETENTION_DAYS must be >= 1.\n") return 1 cutoff = datetime.now(timezone.utc) - timedelta(days=retention_days) with SessionLocal() as session: result = session.execute(delete(RequestLog).where(RequestLog.created_at < cutoff)) session.commit() sys.stdout.write(f"Deleted {result.rowcount or 0} request_logs rows older than {cutoff.isoformat()}.\n") return 0 ``` Sources: [server/scripts/prune_request_logs.py:16-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/scripts/prune_request_logs.py#L16-L32) > [!CAUTION] > The pruning script strictly validates that `REQUEST_LOG_RETENTION_DAYS` parses to an integer greater than or equal to 1. Providing a non-integer value or a number less than 1 prints an error message to standard error and terminates execution with exit code 1. > Sources: [server/scripts/prune_request_logs.py:18-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/scripts/prune_request_logs.py#L18-L25) The maintenance script can be executed directly inside the container or integrated into external scheduling facilities such as cron or systemd timers on the host system. Sources: [server/scripts/prune_request_logs.py:3-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/scripts/prune_request_logs.py#L3-L3) ## Related - [FastAPI Application](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application) - [Server Authentication](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/server-authentication) --- ## Technical docs: GET Get Current User URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/auth/me ## Responses ## Try It --- ## Technical docs: PATCH Update Current User URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/auth/update-me ## Request Body Update Profile Request ## Responses ## Try It --- ## Technical docs: Server API Endpoints URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/server-api-endpoints
Relevant source files The following files were used as context for generating this wiki page: - [server/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/openclaw/cli/commands.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts) - [server/routers/entities.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [server/dashboard/src/utils/api-endpoints.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [server/dashboard/src/types/api.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/types/api.ts) - [integrations/antigravity-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py) - [server/routers/api_keys.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py) - [mem0/client/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py) - [integrations/claude-code-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py) - [integrations/kimi-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/mcp_server.py) - [integrations/agent-plugin-core/python/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [integrations/claude-code-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/telemetry.py) - [integrations/mem0-agent-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/telemetry.py) - [integrations/antigravity-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/telemetry.py) - [integrations/codex-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/mcp_server.py) - [integrations/agent-plugin-core/python/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/telemetry.py) - [integrations/mem0-agent-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py) - [integrations/cursor-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/mcp_server.py) - [integrations/cursor-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/telemetry.py) - [integrations/codex-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/telemetry.py) - [integrations/kimi-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/telemetry.py) - [integrations/mem0-agent-plugin/mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/mcp.json)
## Overview The self-hosted server provides a FastAPI application bootstrap layer that initializes application state, registers routers, enforces rate limits, handles CORS, and maps validation errors. This HTTP interface powers remote memory synchronization, project scoping, and administrative management across client SDKs and dashboard integrations. Sources: [server/main.py:178-342](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py#L178-L342), [FastAPI Application](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application) ## REST API Architecture and Routing ### Endpoint Inventory and Path Conventions The FastAPI server exposes modular REST endpoints grouped into distinct API submodules, each mounted under dedicated path prefixes. These route definitions integrate with Pydantic schemas and database models to handle authentication, manage programmatic keys, inspect aggregated entities, and record audit requests. The route endpoints map across core functional domains including authentication (`/auth`), memory management (`/memories`), system configuration (`/configure`), API key administration (`/api-keys`), request log telemetry (`/requests`), and entity browsing (`/entities`). Client integrations and dashboard interfaces reference these exact paths via centralized utility configurations and typed API client definitions. | Submodule | Path Prefix | Key Methods | Description | | :--- | :--- | :--- | :--- | | Auth | `/auth` | GET, POST | Setup status, registration, login, token refresh, profile inspection, password change, and onboarding completion | | Memories | `/memories` | GET, POST, DELETE, PUT | Memory CRUD operations, history inspection, reset triggers, and generation instructions | | Configure | `/configure` | GET, POST | System configuration and provider settings | | API Keys | `/api-keys` | GET, POST, DELETE | Active key enumeration, creation of new API keys with secret generation, and key revocation | | Requests | `/requests` | GET | Request log telemetry and audit querying | | Entities | `/entities` | GET, DELETE | Aggregated entity listing across user, agent, and run scopes, and cascading memory deletions | Sources: [server/routers/entities.py:12-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py#L12-L16), [server/routers/api_keys.py:14](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py#L14), [server/dashboard/src/utils/api-endpoints.ts:1-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L1-L34) ### Pydantic Schemas and Data Models Request validation and response serialization rely on explicit Pydantic models across all routers. Submodules enforce specific payload contracts for entity grouping, API key lifecycle management, and telemetry logging. ```python class Entity(BaseModel): id: str type: EntityType total_memories: int created_at: Optional[datetime] = None updated_at: Optional[datetime] = None ``` Sources: [server/routers/entities.py:20-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py#L20-L26) ```python class CreateKeyRequest(BaseModel): label: str class CreateKeyResponse(BaseModel): id: str key: str label: str key_prefix: str created_at: datetime class KeyListItem(BaseModel): id: str label: str key_prefix: str created_at: datetime last_used_at: datetime | None ``` Sources: [server/routers/api_keys.py:17-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py#L17-L35) ```typescript export interface ApiRequestLog { id: string; created_at: string; method: string; path: string; status_code: number; latency_ms: number; auth_type: string; } export type EntityType = "user" | "agent" | "run"; export interface Entity { id: string; type: EntityType; total_memories: number; created_at: string | null; updated_at: string | null; } ``` Sources: [server/dashboard/src/types/api.ts:26-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/types/api.ts#L26-L44) ## Memory Operations and Data Schemas ### Endpoint Routing and Payload Structures Memory management endpoints provide full CRUD capabilities alongside specialized sub-routes for inspecting history, configuring providers, resetting data stores, and generating system instructions. The base path for memory operations is defined at `/memories`, with dynamic sub-routes supporting resource identification via unique memory identifiers and entity categorization. ```typescript export const MEMORY_ENDPOINTS = { BASE: "/memories", BY_ID: (memoryId: string) => `/memories/${memoryId}`, HISTORY: (memoryId: string) => `/memories/${memoryId}/history`, CONFIGURE: "/configure", CONFIGURE_PROVIDERS: "/configure/providers", RESET: "/reset", GENERATE_INSTRUCTIONS: "/generate-instructions", } as const; ``` Sources: [server/dashboard/src/utils/api-endpoints.ts:11-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L11-L19) The `Memory` data structure standardizes serialization for both single-item retrievals and collection responses. Each memory object exposes an unique identifier, the memory text payload, optional scoping metadata for user or agent associations, and timestamps tracking creation and modification events. ```typescript export interface Memory { id: string; memory: string; user_id?: string; agent_id?: string; created_at?: string; updated_at?: string; } ``` Sources: [server/dashboard/src/types/api.ts:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/types/api.ts#L1-L8) | Endpoint Path | Parameter / Payload | Description | | :--- | :--- | :--- | | `/memories` | POST / GET | Create a new memory record or list stored memories across scoped contexts | | `/memories/{memoryId}` | GET / PUT / DELETE | Retrieve, update, or delete a specific memory entry by its unique identifier | | `/memories/{memoryId}/history` | GET | Fetch the complete modification history log for a designated memory record | | `/configure` | GET / POST | Inspect or update global system configuration settings | | `/configure/providers` | GET / POST | Manage external LLM and vector store provider integrations | | `/reset` | POST | Purge system storage and reset persistent memory stores | | `/generate-instructions` | POST | Automatically synthesize agent instruction sets from recorded memory contexts | Sources: [server/dashboard/src/utils/api-endpoints.ts:11-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L11-L19), [server/dashboard/src/types/api.ts:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/types/api.ts#L1-L8) > [!NOTE] > Scoping identifiers such as `user_id` and `agent_id` are optional on the base `Memory` interface, allowing global memory creation when neither parameter is provided during ingestion. > Sources: [server/dashboard/src/types/api.ts:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/types/api.ts#L1-L8) ## Entity Browsing and Management ### Entity Routing and Schemas The entity routing module exposes endpoints prefixed at `/entities` under the `entities` router tag to inspect, aggregate, and purge vector store payloads by their scoping fields. Authentication is enforced via dependency injection, requiring standard verification for listing operations and administrative privileges for deletion requests. ```python router = APIRouter(prefix="/entities", tags=["entities"]) SCAN_LIMIT = 10_000 EntityType = Literal["user", "agent", "run"] TYPE_TO_FIELD: dict[EntityType, str] = {"user": "user_id", "agent": "agent_id", "run": "run_id"} ``` Sources: [server/routers/entities.py:12-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py#L12-L18) Entity categories are defined by the `EntityType` literal union supporting `"user"`, `"agent"`, and `"run"` scopes. The `TYPE_TO_FIELD` dictionary maps each entity type to its corresponding payload metadata key: `"user"` maps to `"user_id"`, `"agent"` maps to `"agent_id"`, and `"run"` maps to `"run_id"`. ```typescript export type EntityType = "user" | "agent" | "run"; export interface Entity { id: string; type: EntityType; total_memories: number; created_at: string | null; updated_at: string | null; } ``` Sources: [server/dashboard/src/types/api.ts:36-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/types/api.ts#L36-L44) The Pydantic `Entity` model and its TypeScript dashboard equivalent structure the serialized representation returned to clients. Each entity record contains a unique string identifier, its category type, the total memory count aggregated across matching payloads, and ISO-8601 timestamps marking creation and update events. ```python class Entity(BaseModel): id: str type: EntityType total_memories: int created_at: Optional[datetime] = None updated_at: Optional[datetime] = None ``` Sources: [server/routers/entities.py:20-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py#L20-L26) | Endpoint Path | Method | Auth Dependency | Description | | :--- | :--- | :--- | :--- | | `/entities` | GET | `verify_auth` | Scan vector store payloads and return aggregated entity summaries | | `/entities/{entity_type}/{entity_id}` | DELETE | `require_admin` | Purge all memories linked to the specified entity type and identifier | Sources: [server/routers/entities.py:43-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py#L43-L44), [server/routers/entities.py:70-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py#L70-L71), [server/dashboard/src/utils/api-endpoints.ts:30-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L30-L33) > [!NOTE] > The `list_entities` endpoint scans vector store payloads up to a hardcoded `SCAN_LIMIT` of 10,000 records, building dynamic aggregation buckets across user, agent, and run identifier fields. > Sources: [server/routers/entities.py:14-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py#L14-L15), [server/routers/entities.py:45-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py#L45-L47) ### Aggregation and Deletion Logic ```python def _iter_payloads() -> list[dict[str, Any]]: results = get_memory_instance().vector_store.list(top_k=SCAN_LIMIT) rows = results[0] if results and isinstance(results, list) and isinstance(results[0], list) else results or [] return [getattr(row, "payload", None) or {} for row in rows] ``` Sources: [server/routers/entities.py:28-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py#L28-L32) ```python for payload in _iter_payloads(): created = _parse_timestamp(payload.get("created_at")) updated = _parse_timestamp(payload.get("updated_at")) or created for entity_type, field in TYPE_TO_FIELD.items(): value = payload.get(field) if not value: continue bucket = buckets[(entity_type, str(value))] bucket["total_memories"] += 1 if created and (bucket["created_at"] is None or created < bucket["created_at"]): bucket["created_at"] = created if updated and (bucket["updated_at"] is None or updated > bucket["updated_at"]): bucket["updated_at"] = updated return [ Entity(id=entity_id, type=entity_type, **data) for (entity_type, entity_id), data in sorted(buckets.items(), key=lambda item: (item[0][0], item[0][1])) ] ``` Sources: [server/routers/entities.py:49-67](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py#L49-L67) ```python @router.delete("/{entity_type}/{entity_id}", response_model=MessageResponse) def delete_entity(entity_type: EntityType, entity_id: str, _auth=Depends(require_admin)): try: get_memory_instance().delete_all(**{TYPE_TO_FIELD[entity_type]: entity_id}) except Exception: raise upstream_error() return MessageResponse(message="Entity deleted") ``` Sources: [server/routers/entities.py:70-76](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py#L70-L76) > [!WARNING] > Deleting an entity via `delete_entity` invokes `get_memory_instance().delete_all(...)` filtered by the mapped field name, permanently removing every memory associated with that identifier across the underlying data store. > Sources: [server/routers/entities.py:70-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py#L70-L73) ## API Key Lifecycle and Rotation ### Key Management Endpoints and Schemas The API key router manages administrative credential generation, prefix enumeration, and revocation under the `/api-keys` route prefix with the `api-keys` tag. All operations require authenticated user sessions via dependency injection. ```python router = APIRouter(prefix="/api-keys", tags=["api-keys"]) class CreateKeyRequest(BaseModel): label: str class CreateKeyResponse(BaseModel): id: str key: str label: str key_prefix: str created_at: datetime class KeyListItem(BaseModel): id: str label: str key_prefix: str created_at: datetime last_used_at: datetime | None model_config = {"from_attributes": True} ``` Sources: [server/routers/api_keys.py:14-37](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py#L14-L37) | Endpoint Path | Method | Status Code | Description | | :--- | :--- | :--- | :--- | | `/api-keys` | GET | `200` | List all active API keys created by the authenticated user | | `/api-keys` | POST | `201` | Generate and persist a new API key with a given label | | `/api-keys/{key_id}` | DELETE | `200` | Revoke an existing API key by marking its revocation timestamp | Sources: [server/routers/api_keys.py:39-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py#L39-L40), [server/routers/api_keys.py:62-63](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py#L62-L63), [server/routers/api_keys.py:79-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py#L79-L80) > [!NOTE] > The `list_keys` endpoint filters out revoked API keys where `revoked_at` is not null, ordering active credentials by creation date in descending order. > Sources: [server/routers/api_keys.py:41-49](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py#L41-L49) ### Key Generation and Revocation Logic ```python @router.post("", response_model=CreateKeyResponse, status_code=201) def create_key(body: CreateKeyRequest, user: User = Depends(require_auth), db: Session = Depends(get_db)): full_key, prefix, key_hash = generate_api_key() api_key = APIKey(key_prefix=prefix, key_hash=key_hash, label=body.label, created_by=user.id) db.add(api_key) db.commit() db.refresh(api_key) return CreateKeyResponse( id=str(api_key.id), key=full_key, label=api_key.label, key_prefix=prefix, created_at=api_key.created_at, ) ``` Sources: [server/routers/api_keys.py:62-76](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py#L62-L76) ```python @router.delete("/{key_id}", response_model=MessageResponse) def revoke_key(key_id: str, user: User = Depends(require_auth), db: Session = Depends(get_db)): try: key_uuid = uuid.UUID(key_id) except (TypeError, ValueError): raise HTTPException(status_code=404, detail="API key not found.") api_key = db.get(APIKey, key_uuid) if api_key is None or api_key.created_by != user.id: raise HTTPException(status_code=404, detail="API key not found.") if api_key.revoked_at is not None: raise HTTPException(status_code=400, detail="API key is already revoked.") api_key.revoked_at = datetime.now(timezone.utc) db.commit() return MessageResponse(message="API key revoked.") ``` Sources: [server/routers/api_keys.py:79-93](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py#L79-L93) > [!WARNING] > The `revoke_key` endpoint validates that the target identifier parses as a valid UUID and that the key belongs to the requesting user before setting `revoked_at` to the current UTC time. Attempting to revoke an already revoked key raises a `400` status error. > Sources: [server/routers/api_keys.py:81-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/api_keys.py#L81-L91) ## Telemetry and Request Logging Endpoints ### Request Log Aggregation and Schemas Request logging endpoints capture runtime telemetry across server operations. The dashboard client references these endpoints through the `REQUEST_ENDPOINTS` utility mapping. ```typescript export const REQUEST_ENDPOINTS = { BASE: "/requests", } as const; ``` Sources: [server/dashboard/src/utils/api-endpoints.ts:26-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L26-L28) Telemetry data structures are represented in the frontend via the `ApiRequestLog` interface, capturing audit metadata including execution path, HTTP method, response status, processing latency, and authentication scheme. ```typescript export interface ApiRequestLog { id: string; created_at: string; method: string; path: string; status_code: number; latency_ms: number; auth_type: string; } ``` Sources: [server/dashboard/src/types/api.ts:26-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/types/api.ts#L26-L34) | Field Name | Type | Description | | :--- | :--- | :--- | | `id` | `string` | Unique identifier for the recorded request log entry | | `created_at` | `string` | Timestamp indicating when the request was processed | | `method` | `string` | HTTP request method (e.g. `GET`, `POST`, `DELETE`) | | `path` | `string` | Request path or route target | | `status_code` | `number` | HTTP response status code returned by the server | | `latency_ms` | `number` | Processing duration measured in milliseconds | | `auth_type` | `string` | Authentication mechanism utilized for the request | Sources: [server/dashboard/src/types/api.ts:27-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/types/api.ts#L27-L33) > [!NOTE] > Request logs aggregate transactional metrics across all incoming API calls, enabling administrative audit trails and performance monitoring within the [dashboard views](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/dashboard-views). > Sources: [server/dashboard/src/types/api.ts:26-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/types/api.ts#L26-L34) ## Health Inspection and System Status ### Setup Status and Health Probes The server provides endpoints to inspect application readiness, configuration state, and setup initialization requirements. The dashboard client accesses setup verification via authentication endpoint paths mapped in the utility configuration. ```typescript export const AUTH_ENDPOINTS = { SETUP_STATUS: "/auth/setup-status", REGISTER: "/auth/register", LOGIN: "/auth/login", REFRESH: "/auth/refresh", ME: "/auth/me", CHANGE_PASSWORD: "/auth/change-password", ONBOARDING_COMPLETE: "/auth/onboarding-complete", } as const; ``` Sources: [server/dashboard/src/utils/api-endpoints.ts:1-9](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L1-L9) | Endpoint Constant | Path Value | Description | | :--- | :--- | :--- | | `SETUP_STATUS` | `/auth/setup-status` | Inspects whether the server requires initial admin user registration | | `REGISTER` | `/auth/register` | Registers the initial administrator account during setup | | `LOGIN` | `/auth/login` | Authenticates existing users and issues access credentials | | `REFRESH` | `/auth/refresh` | Refreshes authentication tokens | | `ME` | `/auth/me` | Retrieves profile details for the currently authenticated user | | `CHANGE_PASSWORD` | `/auth/change-password` | Updates user credentials | | `ONBOARDING_COMPLETE` | `/auth/onboarding-complete` | Marks initial onboarding flows as complete | Sources: [server/dashboard/src/utils/api-endpoints.ts:2-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L2-L8) > [!NOTE] > The `/auth/setup-status` endpoint allows unauthenticated clients to determine whether the server has completed its initial administrator provisioning, guiding the frontend [Dashboard Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/dashboard-architecture) setup wizard. > Sources: [server/dashboard/src/utils/api-endpoints.ts:2](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L2) ## Related - [FastAPI Application](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application) - [Python Client SDK](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/python-client-sdk) --- ## Technical docs: Dashboard Architecture URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/dashboard-architecture
Relevant source files The following files were used as context for generating this wiki page: - [server/dashboard/src/app/setup/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx) - [server/dashboard/src/app/root/dashboard-client-layout.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard-client-layout.tsx) - [server/dashboard/src/app/root/dashboard/memories/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/memories/page.tsx) - [server/dashboard/src/app/root/dashboard/configuration/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/configuration/page.tsx) - [server/dashboard/src/app/root/dashboard/api-keys/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx) - [server/dashboard/src/lib/auth.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/lib/auth.tsx) - [server/dashboard/src/app/root/dashboard/requests/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/requests/page.tsx) - [server/dashboard/src/app/setup/layout.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/layout.tsx) - [server/dashboard/src/app/root/dashboard/settings/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/settings/page.tsx) - [server/dashboard/src/app/root/dashboard/entities/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/entities/page.tsx) - [server/dashboard/src/store/store.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/store/store.ts) - [server/dashboard/src/app/root/dashboard/components/main-nav.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/main-nav.tsx) - [server/dashboard/src/app/root/dashboard/components/nav-wrapper.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/nav-wrapper.tsx) - [server/dashboard/src/middleware.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts) - [server/dashboard/src/app/auth/layout.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(auth)/layout.tsx) - [server/dashboard/src/app/root/dashboard/layout.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/layout.tsx) - [server/dashboard/src/hooks/use-auth.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-auth.ts) - [server/dashboard/src/app/auth/login/login-form.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(auth)/login/login-form.tsx) - [server/dashboard/src/app/root/clientLayout.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/clientLayout.tsx) - [server/dashboard/src/store/rootReducer.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/store/rootReducer.ts) - [server/dashboard/src/components/ui/form.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/components/ui/form.tsx) - [server/dashboard/src/app/root/layout.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/layout.tsx) - [server/dashboard/package.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json) - [server/dashboard/src/app/root/dashboard/analytics/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/analytics/page.tsx) - [server/dashboard/src/utils/api.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api.ts) - [server/dashboard/src/hooks/use-api-query.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts) - [server/dashboard/src/app/auth/login/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(auth)/login/page.tsx) - [server/dashboard/src/app/api/auth/refresh/route.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/api/auth/refresh/route.ts) - [server/dashboard/src/components/ui/accordion.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/components/ui/accordion.tsx) - [server/dashboard/src/components/theme-provider.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/components/theme-provider.tsx)
## Overview The Mem0 self-hosted dashboard is built as a Next.js 15 single-page application that provides a comprehensive web interface for managing memories, system configuration, API keys, and administrative operations. Powered by React 19, Redux Toolkit, and Tailwind CSS, the dashboard architecture implements a modular client layout alongside a secure Next.js middleware routing layer. It orchestrates state management, automated Axios token interception, and first-run initialization flows to seamlessly connect administrators with the underlying FastAPI backend. Sources: [server/dashboard/package.json:1-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/package.json#L1-L84), [server/dashboard/src/middleware.ts:1-63](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L1-L63) ## Application Layout and Provider Hierarchy ### Root Layout Structure and Font Integration The dashboard architecture organizes layouts across distinct Next.js route groups including root, authentication, and setup. The root client layout defined in `DashboardClientLayout` injects typography variables and provider trees into the DOM. It loads five font configurations (`Inter`, `InterDisplay`, `Roboto`, `Fustat`, and `DMMono`) from local font utilities and applies their CSS variable declarations alongside base font styles directly onto the document body. ```tsx export function DashboardClientLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( ``` Sources: [server/dashboard/src/app/root/dashboard-client-layout.tsx:22-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard-client-layout.tsx#L22-L38) Similarly, the setup and authentication route layouts establish parallel HTML document roots that load the identical font configuration sets and enforce suppress hydration warnings to prevent server-client attribute mismatches. ```tsx return ( ``` Sources: [server/dashboard/src/app/setup/layout.tsx:17-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/layout.tsx#L17-L29), [server/dashboard/src/app/auth/layout.tsx:18-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(auth)/layout.tsx#L18-L30) ### Theme Injection and Provider Composition Provider hierarchy wraps application route trees in a specific nested order to ensure state stores, authentication contexts, theme properties, and tooltips are available to all child components. In the root client layout, the Redux store provider sits at the outermost boundary, followed in sequence by the authentication provider, next-themes provider, and the custom client layout. ```tsx {children} ``` Sources: [server/dashboard/src/app/root/dashboard-client-layout.tsx:39-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard-client-layout.tsx#L39-L51) Theme injection is handled via `ThemeProvider`, a wrapper around `next-themes` that accepts attributes, default settings, and transition rules. Across route layouts, theme configuration values differ based on authentication or installation state requirements: | Route Group / Layout | Default Theme | Enable System | Disable Transition On Change | Attribute | | :--- | :--- | :--- | :--- | :--- | | **Root Dashboard** (`dashboard-client-layout.tsx`) | `light` | `true` | `true` | `class` | | **Setup Wizard** (`setup/layout.tsx`) | `light` | `false` | `true` | `class` | | **Authentication** (`auth/layout.tsx`) | `system` | `true` | `true` | `class` | Sources: [server/dashboard/src/app/root/dashboard-client-layout.tsx:41-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard-client-layout.tsx#L41-L46), [server/dashboard/src/app/setup/layout.tsx:31-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/layout.tsx#L31-L35), [server/dashboard/src/app/auth/layout.tsx:32-37](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(auth)/layout.tsx#L32-L37) > [!NOTE] > The setup wizard layout intentionally disables system theme detection (`enableSystem={false}`), forcing a default light theme during initial installation, whereas authentication and dashboard layouts support dynamic system preference matching. Sources: [server/dashboard/src/app/setup/layout.tsx:31-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/layout.tsx#L31-L35), [server/dashboard/src/app/auth/layout.tsx:32-37](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(auth)/layout.tsx#L32-L37) ### Client Layout and Sidebar Geometry The inner `ClientLayout` component enforces session validation logic. It evaluates the current pathname against public prefixes (`/login` and `/setup`) and monitors authentication state. If a user is unauthenticated on a protected path, it renders an `AuthLoadingState` containing a theme-aware logo and linear progress indicator while executing a router replacement to `/login`. ```tsx const isPublicPage = pathname.startsWith("/login") || pathname.startsWith("/setup"); useEffect(() => { if (!isPublicPage && !isLoading && !user) { router.replace("/login"); } }, [isLoading, isPublicPage, router, user]); if (isLoading && !isPublicPage) { return ; } if (!isPublicPage && !user) { return ; } ``` Sources: [server/dashboard/src/app/root/clientLayout.tsx:31-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/clientLayout.tsx#L31-L46) Within the dashboard sub-layout, structural dimensions for navigation and content containers are governed by fixed constants exported from the client layout module. ```tsx export const SIDEBAR_WIDTH = 180; export const COLLAPSED_SIDEBAR_WIDTH = 64; export const COLLAPSED_SIDEBAR_PADDING = 16; export const COLLAPSED_SIDEBAR_WIDTH_WITHOUT_PADDING = COLLAPSED_SIDEBAR_WIDTH - COLLAPSED_SIDEBAR_PADDING; ``` Sources: [server/dashboard/src/app/root/clientLayout.tsx:10-14](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/clientLayout.tsx#L10-L14) The dashboard view layout reads sidebar collapse state from the Redux store (`state.layout.isSidebarCollapsed`) to dynamically calculate horizontal offsets and content area width styles for the main scrollable viewport container. ```tsx const isSidebarCollapsed = useSelector( (state: RootState) => state.layout.isSidebarCollapsed, ); return ( <>
Promise; register: (name: string, email: string, password: string) => Promise; logout: () => Promise; refreshUser: () => Promise; } export const AuthContext = createContext({ user: null, isLoading: true, isAdmin: false, login: async () => {}, register: async () => {}, logout: async () => {}, refreshUser: async () => {}, }); ``` Sources: [server/dashboard/src/lib/auth.tsx:13-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/lib/auth.tsx#L13-L39) When the component mounts, an effect hook invokes `refreshSession()` to exchange an HTTP-only refresh cookie for an access token. If successful, it calls `loadUser()` to fetch user details from `AUTH_ENDPOINTS.ME`. In case of failure or network rejection, `user` resets to `null`, and `isLoading` sets to `false`. ```tsx export function AuthProvider({ children }: { children: React.ReactNode }) { const [user, setUser] = useState(null); const [isLoading, setIsLoading] = useState(true); const loadUser = useCallback(async () => { const res = await api.get(AUTH_ENDPOINTS.ME); setUser(res.data); }, []); useEffect(() => { let active = true; (async () => { try { const ok = await refreshSession(); if (ok && active) await loadUser(); } catch { if (active) setUser(null); } finally { if (active) setIsLoading(false); } })(); return () => { active = false; }; }, [loadUser]); ``` Sources: [server/dashboard/src/lib/auth.tsx:64-88](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/lib/auth.tsx#L13-L88) > [!WARNING] > Component unmounting sets `active = false` to prevent asynchronous state updates on unmounted component instances during initial session validation. Sources: [server/dashboard/src/lib/auth.tsx:73-87](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/lib/auth.tsx#L73-L87) ### API Token Handlers and Cookie Routes Client authentication helpers communicate with Next.js API route handlers to read, write, and purge session tokens. The backend refresh route manages secure cookies named `mem0_refresh_token` using parameters configured in `COOKIE_OPTIONS`. ```typescript const COOKIE_NAME = "mem0_refresh_token"; function shouldUseSecureCookie() { const dashboardUrl = process.env.DASHBOARD_URL; if (!dashboardUrl) { return process.env.NODE_ENV === "production"; } try { return new URL(dashboardUrl).protocol === "https:"; } catch { return process.env.NODE_ENV === "production"; } } const COOKIE_OPTIONS = { httpOnly: true, secure: shouldUseSecureCookie(), sameSite: "lax" as const, path: "/", maxAge: 30 * 24 * 60 * 60, // 30 days }; ``` Sources: [server/dashboard/src/app/api/auth/refresh/route.ts:6-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/api/auth/refresh/route.ts#L6-L27) | Route Method | Action | Request Payload | Response | Error Code | | :--- | :--- | :--- | :--- | :--- | | **POST** | Validates existing refresh token and issues new tokens | None (reads cookie) | `{ access_token }` | `401` | | **PUT** | Stores refresh token in HTTP-only cookie | `{ refresh_token }` | `{ ok: true }` | `400` | | **DELETE** | Clears refresh token cookie | None | `{ ok: true }` | — | Sources: [server/dashboard/src/app/api/auth/refresh/route.ts:29-75](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/api/auth/refresh/route.ts#L29-L75) Authentication hooks like `useAuth` provide clean access to the context throughout component trees by wrapping `useContext(AuthContext)`. ```typescript export function useAuth() { return useContext(AuthContext); } ``` Sources: [server/dashboard/src/hooks/use-auth.ts:6-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-auth.ts#L6-L8) ## Route Protection and Edge Middleware ### Middleware Execution and Path Interception The dashboard relies on Next.js edge middleware running prior to route resolution to inspect incoming requests and enforce authentication policies. The middleware matches requests against paths excluding static assets and static files as defined by the matcher configuration. ```typescript export const config = { matcher: ["/((?!_next/static|_next/image|favicon.ico|fonts|images|icons).*)"], }; ``` Sources: [server/dashboard/src/middleware.ts:65-67](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L65-L67) Incoming path evaluation begins by checking against a static array of public prefixes. If a request pathname starts with any entry in `PUBLIC_PATHS`, the middleware bypasses all checks and allows execution to proceed. ```typescript const PUBLIC_PATHS = [ "/_next", "/api/auth", "/api/health", "/fonts", "/favicon", ]; export async function middleware(request: NextRequest) { const { pathname } = request.nextUrl; if (PUBLIC_PATHS.some((p) => pathname.startsWith(p))) { return NextResponse.next(); } ``` Sources: [server/dashboard/src/middleware.ts:5-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L5-L18) | Public Path Prefix | Purpose | | :--- | :--- | | `/_next` | Next.js internal static chunks and assets | | `/api/auth` | Authentication endpoints and token refresh routes | | `/api/health` | System health check inspection route | | `/fonts` | Application font files | | `/favicon` | Browser icon assets | Sources: [server/dashboard/src/middleware.ts:5-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L5-L11) ### Setup Status Verification and Routing When a request targets root paths (`/`, `/login`, or `/setup`), the middleware queries the backend server setup status endpoint to determine whether initial deployment registration is required. ```typescript if (pathname === "/" || pathname === "/login" || pathname === "/setup") { try { const res = await fetch( `${getServerApiUrl()}${AUTH_ENDPOINTS.SETUP_STATUS}`, ); ``` Sources: [server/dashboard/src/middleware.ts:22-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L22-L26) > [!WARNING] > If the backend API is unreachable during setup status checks, the network request throws an error which is caught locally, causing the middleware to gracefully fall through to standard navigation rules without blocking requests. Sources: [server/dashboard/src/middleware.ts:37-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L37-L39) Depending on the boolean flag `needsSetup` returned from the API, the middleware enforces routing rules: ```typescript if (res.ok) { const { needsSetup } = await res.json(); if (needsSetup && pathname !== "/setup") { return NextResponse.redirect(new URL("/setup", request.url)); } if (!needsSetup && pathname === "/setup") { return NextResponse.redirect(new URL("/login", request.url)); } } ``` Sources: [server/dashboard/src/middleware.ts:27-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L27-L36) ### Token Verification and Protected Navigation For routes outside the public or setup pathways, the middleware checks for the presence of the HTTP-only refresh token cookie (`mem0_refresh_token`). ```typescript const hasRefreshToken = request.cookies.has("mem0_refresh_token"); ``` Sources: [server/dashboard/src/middleware.ts:20-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L20-L20) If a user visits the application root (`/`), redirection behavior branches based on whether the refresh token cookie exists. If present, the user is forwarded to `/dashboard/requests`; otherwise, they are routed to `/login`. Similarly, base dashboard paths (`/dashboard` or `/dashboard/`) automatically rewrite to `/dashboard/requests`. ```typescript if (pathname === "/") { return NextResponse.redirect( new URL(hasRefreshToken ? "/dashboard/requests" : "/login", request.url), ); } if (pathname === "/dashboard" || pathname === "/dashboard/") { return NextResponse.redirect(new URL("/dashboard/requests", request.url)); } ``` Sources: [server/dashboard/src/middleware.ts:46-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L46-L54) When a protected route is accessed without the `mem0_refresh_token` cookie, the middleware constructs a redirect response targeting `/login` and appends the original destination to a `next` query parameter so the client can resume its path after authentication. ```typescript if (!hasRefreshToken) { const loginUrl = new URL("/login", request.url); loginUrl.searchParams.set("next", pathname); return NextResponse.redirect(loginUrl); } ``` Sources: [server/dashboard/src/middleware.ts:56-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/middleware.ts#L56-L60) ## Axios Client and Token Interception ### Centralized API Client Setup and Bearer Token Injection The dashboard instantiates a centralized Axios instance configured with a base URL sourced from `process.env.NEXT_PUBLIC_API_URL`. To authenticate outgoing requests, a request interceptor inspects the in-memory token cache and injects a Bearer authorization header if a token exists. ```typescript let cachedToken: string | null = null; const LOGIN_PATH = "/login"; export const setAccessToken = (token: string | null) => { cachedToken = token; }; export const getAccessToken = (): string | null => { return cachedToken; }; ``` Sources: [server/dashboard/src/utils/api.ts:3-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api.ts#L3-L13) ```typescript const api = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, }); api.interceptors.request.use( async (config) => { if (cachedToken) { config.headers = config.headers ?? {}; config.headers.Authorization = `Bearer ${cachedToken}`; } return config; }, (error) => { return Promise.reject(error); }, ); ``` Sources: [server/dashboard/src/utils/api.ts:42-57](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api.ts#L42-L57) > [!NOTE] > Tokens are held exclusively in runtime memory via `cachedToken` and manipulated using `setAccessToken` and `getAccessToken` helpers, preventing persistent storage vulnerabilities on the client side. Sources: [server/dashboard/src/utils/api.ts:3-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api.ts#L3-L12) ### Response Interception and Token Refresh Flow When an API request encounters a 401 Unauthorized response, the response interceptor triggers an automated token recovery sequence. It first clears the stored token via `handleTokenError` and issues a POST request to `/api/auth/refresh` with credentials included. ```typescript const handleTokenError = () => { cachedToken = null; }; ``` Sources: [server/dashboard/src/utils/api.ts:14-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api.ts#L14-L16) ```typescript const refreshAccessToken = async () => { const refreshResponse = await fetch("/api/auth/refresh", { method: "POST", credentials: "include", }); if (!refreshResponse.ok) { return null; } const data = await refreshResponse.json(); setAccessToken(data.access_token); return data.access_token as string; }; ``` Sources: [server/dashboard/src/utils/api.ts:24-37](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api.ts#L24-L37) If the refresh endpoint returns a valid new access token, the client updates its cache, attaches the new Bearer token to the original request configuration, and retries the failed call. If the refresh attempt fails or throws an exception, the client clears the token cache and executes a window redirect to `/login`. ```typescript api.interceptors.response.use( (response) => response, async (error: AxiosError<{ error?: string }>) => { if (error.response?.status === 401) { handleTokenError(); try { const nextToken = await refreshAccessToken(); if (nextToken && error.config) { error.config.headers = error.config.headers ?? {}; error.config.headers.Authorization = `Bearer ${nextToken}`; return api.request(error.config); } } catch {} handleTokenError(); redirectToLogin(); } ``` Sources: [server/dashboard/src/utils/api.ts:59-76](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api.ts#L59-L76) ```typescript const redirectToLogin = () => { if (typeof window !== "undefined") { window.location.href = LOGIN_PATH; } }; ``` Sources: [server/dashboard/src/utils/api.ts:18-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api.ts#L18-L22) ### Streaming Request Handling Alongside the standard Axios instance, the API module exposes a custom `postStream` helper designed to handle server-sent event or streaming payload requests via the native `fetch` API. ```typescript const postStream = async (url: string, data: unknown): Promise => { const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}${url}`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: cachedToken ? `Bearer ${cachedToken}` : "", }, body: JSON.stringify(data), }); ``` Sources: [server/dashboard/src/utils/api.ts:86-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api.ts#L86-L94) The `postStream` function evaluates response statuses explicitly, throwing an unauthorized error and triggering `redirectToLogin` if a 401 status code occurs, or parsing error payloads when requests fail. ```typescript if (response.status === 401) { handleTokenError(); redirectToLogin(); throw new Error("Unauthorized"); } if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.error || "Request failed"); } return response; }; ``` Sources: [server/dashboard/src/utils/api.ts:96-108](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api.ts#L96-L108) ## Data Fetching Hook Lifecycle ### The `useApiQuery` Execution Model The `useApiQuery` custom hook manages asynchronous data fetching, loading flags, error strings, and manual refetch capabilities for dashboard components like ApiKeysPage and ConfigurationPage. It accepts a generic `fetcher` function alongside a `UseApiQueryOptions` configuration object specifying `enabled`, `errorToast`, and `initialData`. ```typescript interface UseApiQueryOptions { enabled?: boolean; errorToast?: string; initialData?: T; } interface UseApiQueryResult { data: T | undefined; isLoading: boolean; error: string; refetch: () => Promise; } export function useApiQuery( fetcher: () => Promise, options: UseApiQueryOptions = {}, ): UseApiQueryResult { ``` Sources: [server/dashboard/src/hooks/use-api-query.ts:5-21](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts#L5-L21) State management tracks resolved items via `data`, active request status via `isLoading`, and failure messages via `error`. A React `useRef` tracks the latest `fetcher` reference so callbacks execute the most up-to-date query logic without capturing stale closures. ```typescript const [data, setData] = useState(initialData); const [isLoading, setIsLoading] = useState(enabled); const [error, setError] = useState(""); const fetcherRef = useRef(fetcher); fetcherRef.current = fetcher; ``` Sources: [server/dashboard/src/hooks/use-api-query.ts:24-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts#L24-L30) ### Call-Chain Execution Walkthrough 1. `ApiKeysPage` or `ConfigurationPage` invokes `useApiQuery` during component initialization, passing a custom async fetcher and options. 2. `useApiQuery` defines the `run` callback via `useCallback`, which marks `isLoading` as true and clears any existing `error` string. 3. `run` executes `fetcherRef.current()` to retrieve network data, storing the resolved value in `data` upon success. 4. If an exception occurs, `getErrorMessage` processes the error alongside any provided `errorToast` label, populates `error`, and invokes `toast()` if an error toast option was specified. 5. The `finally` block sets `isLoading` to false, and a `useEffect` hook triggers `run` automatically on mount if `enabled` is true. ```typescript const run = useCallback(async () => { setIsLoading(true); setError(""); try { setData(await fetcherRef.current()); } catch (err) { const message = getErrorMessage(err, errorToast || "Request failed"); setError(message); if (errorToast) { toast({ title: errorToast, description: message, variant: "destructive", }); } } finally { setIsLoading(false); } }, [errorToast]); useEffect(() => { if (enabled) void run(); }, [enabled, run]); ``` Sources: [server/dashboard/src/hooks/use-api-query.ts:31-53](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts#L31-L53) ```mermaid sequenceDiagram participant Page as ApiKeysPage / ConfigurationPage participant Hook as useApiQuery participant Run as run() participant Fetcher as fetcherRef.current() Page->>Hook: Invoke useApiQuery(fetcher, options) Hook->>Run: useEffect / refetch triggers run() Run->>Fetcher: Execute fetcherRef.current() Fetcher-->>Run: Resolve data or throw error Run->>Hook: Update data, isLoading, error state & toast ``` Sources: [server/dashboard/src/hooks/use-api-query.ts:31-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts#L31-L55), [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:41-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L41-L47), [server/dashboard/src/app/root/dashboard/configuration/page.tsx:41-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/configuration/page.tsx#L41-L47) ### Options and Return Signature Reference | Property / Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `enabled` | `boolean` | `true` | Controls whether the query executes automatically on initial component mount. | | `errorToast` | `string` | `undefined` | Title string passed to the centralized notification toast if the request fails. | | `initialData` | `T` | `undefined` | Fallback data value assigned before the initial fetch completes. | | `data` | `T \| undefined` | `initialData` | The successfully resolved payload returned by the fetcher function. | | `isLoading` | `boolean` | `enabled` | Boolean flag tracking whether an asynchronous fetch operation is currently in flight. | | `error` | `string` | `""` | Formatted error message string populated when a fetch exception occurs. | | `refetch` | `() => Promise` | — | Manual trigger function to re-run the asynchronous fetcher on demand. | ```typescript interface UseApiQueryOptions { enabled?: boolean; errorToast?: string; initialData?: T; } interface UseApiQueryResult { data: T | undefined; isLoading: boolean; error: string; refetch: () => Promise; } export function useApiQuery( fetcher: () => Promise, options: UseApiQueryOptions = {}, ): UseApiQueryResult { ``` Sources: [server/dashboard/src/hooks/use-api-query.ts:5-21](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts#L5-L21) ```typescript return { data, isLoading, error, refetch: run }; ``` Sources: [server/dashboard/src/hooks/use-api-query.ts:55-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts#L55-L55) > [!NOTE] > If `errorToast` is omitted from `UseApiQueryOptions`, failure messages update the local `error` state variable without dispatching an intrusive toast notification to the user interface. Sources: [server/dashboard/src/hooks/use-api-query.ts:37-45](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts#L37-L45) ## Redux Toolkit State and Navigation ### Redux Store Configuration and Root Reducer The application state container is established using Redux Toolkit's `configureStore` helper, importing a consolidated `rootReducer` that merges all feature slices. Currently, the store manages a single slice under the `layout` key, and exports standard TypeScript definitions for `RootState` and `AppDispatch`. ```typescript import { configureStore } from "@reduxjs/toolkit"; import rootReducer from "./rootReducer"; const store = configureStore({ reducer: rootReducer, }); export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; export default store; ``` Sources: [server/dashboard/src/store/store.ts:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/store/store.ts#L1-L10) ```typescript import { combineReducers } from "redux"; import { layoutReducer } from "./reducers/layoutReducer"; const rootReducer = combineReducers({ layout: layoutReducer, }); export default rootReducer; ``` Sources: [server/dashboard/src/store/rootReducer.ts:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/store/rootReducer.ts#L1-L8) | Export Name | Return Type / Definition | Purpose | | :--- | :--- | :--- | | `store` | Return type of `configureStore` | The central Redux store instance wrapping `rootReducer`. | | `RootState` | `ReturnType` | Global state type used by selectors across components. | | `AppDispatch` | `typeof store.dispatch` | Dispatch type for triggering slice actions. | Sources: [server/dashboard/src/store/store.ts:8-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/store/store.ts#L8-L10) > [!NOTE] > `rootReducer` uses Redux's `combineReducers` function, making it easy to append new feature slices alongside `layout` as the dashboard architecture expands. Sources: [server/dashboard/src/store/rootReducer.ts:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/store/rootReducer.ts#L1-L6) ### Collapsible Sidebar Navigation and Layout State The sidebar navigation layout relies on the `isSidebarCollapsed` boolean flag retrieved from `state.layout.isSidebarCollapsed` via Redux's `useSelector`. Depending on this state, components like `MainNav` and `NavWrapper` dynamically adapt their width, hide or display text labels, and toggle collapsible trigger groups. ```tsx const pathname = usePathname(); const isSidebarCollapsed = useSelector( (state: RootState) => state.layout.isSidebarCollapsed, ); const [isCloudOpen, setIsCloudOpen] = React.useState(true); ``` Sources: [server/dashboard/src/app/root/dashboard/components/main-nav.tsx:43-48](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/main-nav.tsx#L43-L48) The navigation structure is divided into three primary sections: **Activity**, **Cloud Features** (wrapped in a local `Collapsible` component controlled by `isCloudOpen`), and **Account**. When the sidebar is collapsed, group labels are hidden, and menu items center their icons with tooltip text enabled. ```tsx
{!isSidebarCollapsed && ( ACTIVITY )} {[ { title: "Requests", url: "/dashboard/requests", icon: Activity, active: pathname === "/dashboard/requests", }, { title: "Memories", url: "/dashboard/memories", icon: GalleryVerticalEnd, active: pathname === "/dashboard/memories", }, { title: "Entities", url: "/dashboard/entities", icon: Users, active: pathname === "/dashboard/entities", }, ].map((item) => ( {!isSidebarCollapsed && {item.title}} ))}
{isSidebarCollapsed && (
)} {!isSidebarCollapsed && ( CLOUD FEATURES )} {[ { title: "Categories", url: "/dashboard/categories", icon: Tags, }, { ``` Sources: [server/dashboard/src/app/root/dashboard/components/main-nav.tsx:59-242](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/main-nav.tsx#L59-L242) ```tsx export default function NavWrapper() { const dispatch = useDispatch(); const isSidebarCollapsed = useSelector( (state: RootState) => state.layout.isSidebarCollapsed, ); const { user, logout } = useAuth(); const instanceName = process.env.NEXT_PUBLIC_INSTANCE_NAME || "Mem0"; const handleToggle = useCallback(() => { dispatch(toggleSidebar()); }, [dispatch]); ``` Sources: [server/dashboard/src/app/root/dashboard/components/nav-wrapper.tsx:31-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/nav-wrapper.tsx#L31-L43) > [!WARNING] > Toggling the sidebar state updates both the fixed container width and the adjacent content offset calculations using constants like `COLLAPSED_SIDEBAR_WIDTH` and `SIDEBAR_WIDTH`. Sources: [server/dashboard/src/app/root/dashboard/components/nav-wrapper.tsx:46-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/nav-wrapper.tsx#L46-L51), [server/dashboard/src/app/root/dashboard/components/nav-wrapper.tsx:136-141](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/nav-wrapper.tsx#L136-L141) ## First-Run Setup Wizard Flow ### Multi-Step Wizard and Account Registration The first-run setup wizard is orchestrated by `SetupPage`, which guides uninitialized server instances through a sequential five-step flow. The wizard tracks the active step index via local state and references an array of step titles defining the initialization lifecycle. ```tsx const STEP_TITLES = [ "Create your admin account", "Review provider configuration", "Your API key", "Tell us your use case", "Test your setup", ]; ``` Sources: [server/dashboard/src/app/setup/page.tsx:45-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx#L45-L51) | Step Index | Title | Primary Action / Handler | | :--- | :--- | :--- | | `0` | Create your admin account | `handleStep1` validates email format, confirms password matching, enforces a minimum length of 8 characters, and invokes `register(name, email, password)`. | | `1` | Review provider configuration | `handleStep2` checks if configuration fields are dirty, builds provider payloads, and posts updates to `MEMORY_ENDPOINTS.CONFIGURE`. | | `2` | Your API key | `handleStep3` requests a new API key from `API_KEY_ENDPOINTS.BASE` using the specified key label. | | `3` | Tell us your use case | Captures user use case selection from preset options and custom instructions. | | `4` | Test your setup | Executes a validation test message against the configured backend. | Sources: [server/dashboard/src/app/setup/page.tsx:45-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx#L45-L51), [server/dashboard/src/app/setup/page.tsx:157-186](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx#L157-L186), [server/dashboard/src/app/setup/page.tsx:188-235](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx#L188-L235), [server/dashboard/src/app/setup/page.tsx:237-246](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx#L237-L246) > [!WARNING] > Step 1 registration enforces strict client-side validation: `email` must satisfy `isValidEmail`, `password` must equal `confirmPassword`, and `password.length` must be at least 8 characters before submitting credentials to the authentication provider. Sources: [server/dashboard/src/app/setup/page.tsx:160-173](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx#L160-L173) ### Provider Configuration and Credentials When the wizard advances to step index `1`, `useEffect` triggers an asynchronous configuration loader. This routine fetches the effective server configuration and bundled providers concurrently using `MEMORY_ENDPOINTS.CONFIGURE` and `MEMORY_ENDPOINTS.CONFIGURE_PROVIDERS`. ```tsx const loadConfig = async () => { try { const [configRes, providersRes] = await Promise.all([ api.get(MEMORY_ENDPOINTS.CONFIGURE), api.get(MEMORY_ENDPOINTS.CONFIGURE_PROVIDERS), ]); ``` Sources: [server/dashboard/src/app/setup/page.tsx:112-117](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx#L112-L117) The configuration loader extracts provider identifiers and model names for both Language Model (`llm`) and `embedder` subsystems, storing both current state values and initial baselines to detect subsequent modifications. ```tsx const llmProv = config?.llm?.provider || ""; const llmMod = config?.llm?.config?.model || ""; const embProv = config?.embedder?.provider || ""; const embMod = config?.embedder?.config?.model || ""; setLlmProvider(llmProv); setLlmModel(llmMod); setEmbedderProvider(embProv); setEmbedderModel(embMod); setInitialLlmProvider(llmProv); setInitialLlmModel(llmMod); setInitialEmbedderProvider(embProv); setInitialEmbedderModel(embMod); setServerHasLlmKey(!!config?.llm?.config?.api_key); setProviders(providersRes.data); ``` Sources: [server/dashboard/src/app/setup/page.tsx:124-138](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx#L124-L138) ```tsx const USE_CASE_PRESETS = [ "Personal assistant", "Coding agent", "Customer support", "Research", "Therapy / journaling", ]; ``` Sources: [server/dashboard/src/app/setup/page.tsx:55-61](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx#L55-L61) > [!TIP] > If no changes are detected when submitting step 2 (`dirty` evaluates to `false`), the wizard bypasses the API save request and immediately advances to step 2. Sources: [server/dashboard/src/app/setup/page.tsx:192-202](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx#L192-L202) ## Related - [Dashboard Views](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/dashboard-views) - [FastAPI Application](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application) --- ## Technical docs: POST Change Password URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/auth/change-password ## Request Body Change Password Request ## Responses ## Try It --- ## Technical docs: Dashboard Views URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/dashboard-views
Relevant source files The following files were used as context for generating this wiki page: - [server/dashboard/src/app/root/dashboard/memories/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/memories/page.tsx) - [server/dashboard/src/app/setup/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx) - [server/dashboard/src/app/root/dashboard/api-keys/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx) - [server/dashboard/src/app/root/dashboard/entities/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/entities/page.tsx) - [server/dashboard/src/app/root/dashboard/configuration/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/configuration/page.tsx) - [server/dashboard/src/app/root/dashboard/requests/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/requests/page.tsx) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [server/dashboard/src/app/root/dashboard/components/main-nav.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/main-nav.tsx) - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [server/dashboard/src/app/root/dashboard/settings/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/settings/page.tsx) - [server/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py) - [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts) - [server/dashboard/src/app/root/dashboard/analytics/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/analytics/page.tsx) - [server/routers/entities.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/routers/entities.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [server/dashboard/src/app/root/dashboard/components/nav-wrapper.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/nav-wrapper.tsx) - [cli/node/src/commands/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/memory.ts) - [server/dashboard/src/app/root/dashboard/webhooks/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/webhooks/page.tsx) - [cli/node/src/commands/entities.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/entities.ts) - [server/dashboard/src/utils/api-endpoints.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts) - [server/dashboard/src/app/root/dashboard/export/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/export/page.tsx) - [server/dashboard/src/types/api.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/types/api.ts) - [mem0/client/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py) - [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts) - [server/dashboard/src/components/ui/dialog.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/components/ui/dialog.tsx) - [server/dashboard/src/components/ui/use-toast.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/components/ui/use-toast.ts) - [server/dashboard/src/hooks/use-api-query.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts) - [server/dashboard/src/lib/error-message.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/lib/error-message.ts)
## Overview The administrative dashboard provides a centralized Next.js interface for managing self-hosted instances, monitoring runtime memory operations, inspecting stored entities, and configuring system-level parameters through authenticated API query hooks and state management providers. Sources: [server/dashboard/src/app/setup/page.tsx](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/setup/page.tsx#L45-L64), [server/dashboard/src/hooks/use-api-query.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts#L18-L56) ## Navigation and Shell Layout ### Sidebar and Shell Structure The dashboard layout combines a fixed sidebar shell container managed by Redux layout state with collapsible navigation groups, instance branding, and a top-bar control header. The primary navigation shell uses `NavWrapper` to compute responsive positioning based on whether `isSidebarCollapsed` is active, utilizing layout widths defined by `SIDEBAR_WIDTH` and `COLLAPSED_SIDEBAR_WIDTH`. A toggle button rendered in the top header triggers the `toggleSidebar` Redux action, altering both the fixed container width and the adjacent main content offset. Sources: [server/dashboard/src/app/root/dashboard/components/nav-wrapper.tsx:31-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/nav-wrapper.tsx#L31-L51) > [!NOTE] > When the sidebar is collapsed, collapsible triggers and textual labels (such as group titles and user email identifiers) are hidden, while tooltips are automatically passed to `SidebarMenuButton` components to display the item titles on hover. Sources: [server/dashboard/src/app/root/dashboard/components/main-nav.tsx:44-64](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/main-nav.tsx#L44-L64) ### Navigation Item Routing and Categories The `MainNav` component organizes dashboard links into three distinct functional sections: **ACTIVITY**, **CLOUD FEATURES**, and **ACCOUNT**. Each section groups route definitions that map to Lucide icons and match the current router path via `usePathname`. | Section | Title | URL | Icon | | :--- | :--- | :--- | :--- | | **ACTIVITY** | Requests | `/dashboard/requests` | `Activity` | | **ACTIVITY** | Memories | `/dashboard/memories` | `GalleryVerticalEnd` | | **ACTIVITY** | Entities | `/dashboard/entities` | `Users` | | **CLOUD FEATURES** | Categories | `/dashboard/categories` | `Tags` | | **CLOUD FEATURES** | Webhooks | `/dashboard/webhooks` | `WebhookIcon` | | **CLOUD FEATURES** | Analytics | `/dashboard/analytics` | `ChartLine` | | **CLOUD FEATURES** | Export | `/dashboard/export` | `FolderInput` | | **ACCOUNT** | API Keys | `/dashboard/api-keys` | `KeyRound` | | **ACCOUNT** | Configuration | `/dashboard/configuration` | `Wrench` | | **ACCOUNT** | Settings | `/dashboard/settings` | `Settings` | ```tsx {[ { title: "Requests", url: "/dashboard/requests", icon: Activity, active: pathname === "/dashboard/requests", }, { title: "Memories", url: "/dashboard/memories", icon: GalleryVerticalEnd, active: pathname === "/dashboard/memories", }, { title: "Entities", url: "/dashboard/entities", icon: Users, active: pathname === "/dashboard/entities", }, ``` Sources: [server/dashboard/src/app/root/dashboard/components/main-nav.tsx:65-83](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/main-nav.tsx#L65-L83), [server/dashboard/src/app/root/dashboard/components/main-nav.tsx:132-152](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/main-nav.tsx#L132-L152), [server/dashboard/src/app/root/dashboard/components/main-nav.tsx:199-217](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/main-nav.tsx#L199-L217) ### User Profile and Dropdown Menu At the base of the expanded sidebar shell, `NavWrapper` renders a user profile widget connected to `useAuth`. Clicking the user trigger opens a `DropdownMenu` providing direct navigation to user settings and a `logout` handler. ```tsx

{user?.name}

{user?.email}

Settings Log out
``` Sources: [server/dashboard/src/app/root/dashboard/components/nav-wrapper.tsx:82-130](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/components/nav-wrapper.tsx#L82-L130) ## Memories Inspection and Deletion ### Data Table Rendering and Pagination The `MemoriesPage` component retrieves records from the backend API using `useApiQuery`, querying `MEMORY_ENDPOINTS.BASE` with an optional `user_id` query parameter and a maximum fetch limit (`MEMORY_FETCH_LIMIT`) set to `1000`. The raw response extracts results via `res.data?.results ?? res.data ?? []`. When total records reach or exceed `MEMORY_FETCH_LIMIT`, an `UpgradeBanner` renders with a cloud upgrade CTA. Client-side pagination slices the fetched list using a fixed `PAGE_SIZE` of `20` records per page. ```tsx const PAGE_SIZE = 20; // Keep in sync with ALL_MEMORIES_LIMIT in server/main.py. const MEMORY_FETCH_LIMIT = 1000; export default function MemoriesPage() { const [userId, setUserId] = useState(""); const [selectedMemory, setSelectedMemory] = useState(null); const [memoryToDelete, setMemoryToDelete] = useState(null); const [page, setPage] = useState(0); const apiUrl = process.env.NEXT_PUBLIC_API_URL || ""; const { data: memories = [], isLoading, refetch, } = useApiQuery( async () => { const params = userId.trim() ? { user_id: userId.trim(), top_k: MEMORY_FETCH_LIMIT } : { top_k: MEMORY_FETCH_LIMIT }; const res = await api.get(MEMORY_ENDPOINTS.BASE, { params }); const raw = res.data?.results ?? res.data ?? []; return Array.isArray(raw) ? raw : []; }, { errorToast: "Failed to load memories", initialData: [] }, ); const totalPages = Math.ceil(memories.length / PAGE_SIZE); const paginatedMemories = memories.slice( page * PAGE_SIZE, (page + 1) * PAGE_SIZE, ); ``` Sources: [server/dashboard/src/app/root/dashboard/memories/page.tsx:29-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/memories/page.tsx#L29-L60) The table columns display specific memory attributes mapped from the `Memory` interface: | Column Label | Key | Width | Render Behavior | | :--- | :--- | :--- | :--- | | **Content** | `memory` | `400` | Clamped to 2 lines (`line-clamp-2 text-sm`) | | **User** | `user_id` | `100` | Raw string rendering | | **Agent** | `agent_id` | `100` | Raw string rendering | | **Created** | `created_at` | `120` | Formatted via date-fns (`MMM d, yyyy`) or `--` | ```tsx const columns = [ { key: "memory" as keyof Memory, label: "Content", width: 400, render: (value: string) => ( {value} ), }, { key: "user_id" as keyof Memory, label: "User", width: 100 }, { key: "agent_id" as keyof Memory, label: "Agent", width: 100 }, { key: "created_at" as keyof Memory, label: "Created", width: 120, render: (value: string) => value ? format(new Date(value), "MMM d, yyyy") : "--", }, ]; ``` Sources: [server/dashboard/src/app/root/dashboard/memories/page.tsx:79-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/memories/page.tsx#L79-L97) > [!NOTE] > Pressing the Enter key while typing inside the User ID filter input resets the pagination index to `0` and triggers an immediate query refetch. ```tsx setUserId(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { setPage(0); refetch(); } }} className="w-64" />
``` Sources: [server/dashboard/src/app/root/dashboard/memories/page.tsx:114-126](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/memories/page.tsx#L114-L126) ### Inspection Sheet and Deletion Flow Clicking any row in the `DataTable` assigns the selected `Memory` object to local state via `onRowClick`, activating the inspection `Sheet` component. The sheet displays metadata and details for the selected memory item while highlighting the active row with a background utility class (`bg-surface-default-tertiary`). ```tsx row.id} onRowClick={(row) => setSelectedMemory(row)} getRowClassName={(row) => selectedMemory?.id === row.id ? "bg-surface-default-tertiary" : undefined } /> ``` Sources: [server/dashboard/src/app/root/dashboard/memories/page.tsx:152-164](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/memories/page.tsx#L152-L164), [server/dashboard/src/app/root/dashboard/memories/page.tsx:195-208](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/memories/page.tsx#L195-L208) Deletion actions execute against `MEMORY_ENDPOINTS.BY_ID(memoryToDelete.id)` using `api.delete`. Success responses trigger a toast notification with a `"success"` variant, clear the inspection sheet if the deleted memory was currently open, reset `memoryToDelete`, and invoke `refetch()`. Errors are intercepted by `getErrorMessage` and displayed via a destructive toast. ```tsx const handleDelete = async () => { if (!memoryToDelete) return; try { await api.delete(MEMORY_ENDPOINTS.BY_ID(memoryToDelete.id)); toast({ title: "Memory deleted", variant: "success" }); if (selectedMemory?.id === memoryToDelete.id) setSelectedMemory(null); setMemoryToDelete(null); void refetch(); } catch (error) { toast({ title: "Failed to delete memory", description: getErrorMessage(error), variant: "destructive", }); } }; ``` Sources: [server/dashboard/src/app/root/dashboard/memories/page.tsx:62-77](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/memories/page.tsx#L62-L77) ## Entity Exploration and Graph Cleanup ### Entities List Presentation and Metadata The `EntitiesPage` component queries `ENTITY_ENDPOINTS.BASE` via `useApiQuery` to fetch a collection of `Entity` records. While loading, it renders a `TableSkeleton` component with 5 rows and 5 columns. If the resulting entity array is empty, it displays an `EmptyState` component indicating that entities appear once memories are stored with a `user_id`, `agent_id`, or `run_id`. Otherwise, it wraps a `DataTable` component inside a styled `Card` element. ```tsx export default function EntitiesPage() { const [entityToDelete, setEntityToDelete] = useState(null); const { data: entities = [], isLoading, refetch, } = useApiQuery( async () => { const res = await api.get(ENTITY_ENDPOINTS.BASE); return res.data ?? []; }, { errorToast: "Failed to load entities", initialData: [] }, ); ``` Sources: [server/dashboard/src/app/root/dashboard/entities/page.tsx:20-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/entities/page.tsx#L20-L33), [server/dashboard/src/app/root/dashboard/entities/page.tsx:102-121](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/entities/page.tsx#L102-L121) The table structures entity records using the following column schema: | Column Label | Key | Width | Render Behavior | | :--- | :--- | :--- | :--- | | **Type** | `type` | `100` | Rendered as a capitalized outline `Badge` | | **ID** | `id` | `280` | Monospace truncated text (`font-mono text-sm truncate`) | | **Memories** | `total_memories` | `100` | Right-aligned numeric counter | | **Last Active** | `updated_at` | `140` | Formatted via date-fns (`MMM d, yyyy`) or `--` | | **Actions** | `id` | `40` | Ghost icon button rendering a `Trash2` trigger | ```tsx const columns = [ { key: "type" as keyof Entity, label: "Type", width: 100, render: (value: Entity["type"]) => ( {value} ), }, { key: "id" as keyof Entity, label: "ID", width: 280, render: (value: string) => ( {value} ), }, { key: "total_memories" as keyof Entity, label: "Memories", width: 100, align: "right" as const, }, { key: "updated_at" as keyof Entity, label: "Last Active", width: 140, render: (value: string | null) => value ? format(new Date(value), "MMM d, yyyy") : "--", }, { key: "id" as keyof Entity, label: "", width: 40, render: (_: string, row: Entity) => ( ), }, ]; ``` Sources: [server/dashboard/src/app/root/dashboard/entities/page.tsx:53-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/entities/page.tsx#L53-L100) > [!NOTE] > The `EntityType` definition strictly restricts entity classification values to three types: `"user"`, `"agent"`, and `"run"`. ```typescript export type EntityType = "user" | "agent" | "run"; ``` Sources: [server/dashboard/src/types/api.ts:36-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/types/api.ts#L36-L36) ### Entity Deletion and Cleanup Flow Clicking the trash icon button on any entity row sets that specific `Entity` object into the `entityToDelete` local state variable. This action opens the `DeleteConfirmationModal` component, which displays a warning description stating that all memories associated with the entity will be permanently removed. ```tsx key: "id" as keyof Entity, label: "", width: 40, render: (_: string, row: Entity) => ( ), }, ``` Sources: [server/dashboard/src/app/root/dashboard/entities/page.tsx:86-99](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/entities/page.tsx#L86-L99), [server/dashboard/src/app/root/dashboard/entities/page.tsx:123-131](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/entities/page.tsx#L123-L131) Confirming deletion triggers the `handleDelete` function, which issues an HTTP DELETE request to `ENTITY_ENDPOINTS.BY_ID(entityToDelete.type, entityToDelete.id)`. The endpoint dynamically formats the URL path using `encodeURIComponent(id)` to handle identifiers safely. On success, a toast notification displays `"Entity deleted"` with a `"success"` variant, the deletion modal state is cleared, and `refetch` is invoked to update the table data. ```tsx const handleDelete = async () => { if (!entityToDelete) return; try { await api.delete( ENTITY_ENDPOINTS.BY_ID(entityToDelete.type, entityToDelete.id), ); toast({ title: "Entity deleted", variant: "success" }); setEntityToDelete(null); void refetch(); } catch (error) { toast({ title: "Failed to delete entity", description: getErrorMessage(error), variant: "destructive", }); } }; ``` Sources: [server/dashboard/src/app/root/dashboard/entities/page.tsx:35-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/entities/page.tsx#L35-L51), [server/dashboard/src/utils/api-endpoints.ts:30-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L30-L34) > [!WARNING] > If the deletion request fails, `getErrorMessage(error)` extracts the server error detail or fallback message, displaying it inside a destructive toast notification. ```tsx } catch (error) { toast({ title: "Failed to delete entity", description: getErrorMessage(error), variant: "destructive", }); } ``` Sources: [server/dashboard/src/app/root/dashboard/entities/page.tsx:44-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/entities/page.tsx#L44-L50), [server/dashboard/src/lib/error-message.ts:3-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/lib/error-message.ts#L3-L19) ## API Key Management and Dialogs ### Table Listing and Column Configuration The `ApiKeysPage` component renders a data table displaying existing API keys retrieved from the backend. The underlying data query utilizes `useApiQuery` targeting `API_KEY_ENDPOINTS.BASE`, falling back to an empty array if no keys are returned. When three or more keys exist, an `UpgradeBanner` element is displayed above the table layout. ```tsx const { data: keys = [], isLoading, refetch, } = useApiQuery( async () => { const res = await api.get(API_KEY_ENDPOINTS.BASE); return res.data ?? []; }, { errorToast: "Failed to load API keys", initialData: [] }, ); ``` Sources: [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:37-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L37-L47), [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:206-210](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L206-L210) The table structures key records using the following column schema: | Column Label | Key | Width | Render Behavior | | :--- | :--- | :--- | :--- | | **Label** | `label` | `150` | Plain text label | | **Key** | `key_prefix` | `120` | Monospace code snippet rendering prefix with ellipsis (`...`) | | **Created** | `created_at` | `120` | Formatted via date-fns (`MMM d, yyyy`) | | **Last Used** | `last_used_at` | `120` | Formatted via date-fns or rendered as `"Never"` | | **Actions** | `id` | `40` | Ghost icon button rendering a `Trash2` trigger to assign `keyToRevoke` | ```tsx const columns = [ { key: "label" as keyof ApiKey, label: "Label", width: 150 }, { key: "key_prefix" as keyof ApiKey, label: "Key", width: 120, render: (value: string) => ( {value}... ), }, { key: "created_at" as keyof ApiKey, label: "Created", width: 120, render: (value: string) => format(new Date(value), "MMM d, yyyy"), }, { key: "last_used_at" as keyof ApiKey, label: "Last Used", width: 120, render: (value: string | null) => value ? format(new Date(value), "MMM d, yyyy") : "Never", }, { key: "id" as keyof ApiKey, label: "", width: 40, render: (_: string, row: ApiKey) => ( ), }, ]; ``` Sources: [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:90-128](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L90-L128) > [!NOTE] > The `ApiKey` TypeScript interface defines properties for `id`, `label`, `key_prefix`, `created_at`, and an optional `last_used_at` timestamp. ```typescript export interface ApiKey { id: string; label: string; key_prefix: string; created_at: string; last_used_at: string | null; } ``` Sources: [server/dashboard/src/types/api.ts:10-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/types/api.ts#L10-L16) ### Creation Dialog Modals and Clipboard Copying Clicking the "Create Key" button opens a Radix-backed `Dialog` component controlled by `createOpen` state. The dialog content transitions between two distinct states based on whether `newKey` has been populated by a successful creation response. ```tsx return (

API Keys

Create API Key ``` Sources: [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:130-143](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L130-L143) When `newKey` is empty, the modal presents a form input for the `newLabel` string and a disabled-until-typed "Create" action button. Executing `handleCreate` posts to `API_KEY_ENDPOINTS.BASE`, assigns the returned plain-text secret to `newKey`, and triggers a table `refetch`. Once generated, the view swaps to display the raw key inside a read-only input paired with a `CopyToClipboard` component that temporarily toggles check icon feedback. ```tsx const handleCreate = async () => { try { const res = await api.post(API_KEY_ENDPOINTS.BASE, { label: newLabel, }); setNewKey(res.data.key); void refetch(); } catch (error) { toast({ title: "Failed to create key", description: getErrorMessage(error), variant: "destructive", }); } }; ``` Sources: [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:49-63](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L49-L63), [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:144-201](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L144-L201) > [!WARNING] > Closing the creation dialog via `handleDialogClose` completely resets `newKey`, `newLabel`, and copy states back to their defaults. ```tsx const handleDialogClose = (open: boolean) => { if (!open) { setNewKey(""); setNewLabel(""); setCopied(false); } setCreateOpen(open); }; ``` Sources: [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:81-88](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L81-L88) ### Revocation and Toast Feedback Handling Revoking an API key is initiated by clicking the trash action icon on a table row, which stores the target entity in `keyToRevoke`. Triggering `handleRevoke` issues an HTTP DELETE request to `API_KEY_ENDPOINTS.BY_ID(keyToRevoke.id)`, clears the revocation target, triggers a data refetch, and fires a success toast. ```tsx const handleRevoke = async () => { if (!keyToRevoke) return; try { await api.delete(API_KEY_ENDPOINTS.BY_ID(keyToRevoke.id)); toast({ title: "API key revoked", variant: "success" }); setKeyToRevoke(null); void refetch(); } catch (error) { toast({ title: "Failed to revoke key", description: getErrorMessage(error), variant: "destructive", }); } }; ``` Sources: [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:65-79](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L65-L79), [server/dashboard/src/utils/api-endpoints.ts:21-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L21-L24) The dashboard error handling architecture maps exception responses across operations using specific utility functions. The following sequence traces execution paths for dialog rendering, error serialization, and toast notification delivery: 1. `ApiKeysPage` → `DialogContent` → `DialogOverlay`: Renders modal dialog layers with fixed positioning and background overlay styling. Sources: [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:140-140](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L140-L140), [server/dashboard/src/components/ui/dialog.tsx:17-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/components/ui/dialog.tsx#L17-L39) 2. `ApiKeysPage` → `useApiQuery` → `run` → `getErrorMessage`: Intercepts query failures, extracts axios error details, and formats fallback strings. Sources: [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:37-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L37-L47), [server/dashboard/src/hooks/use-api-query.ts:31-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts#L31-L38), [server/dashboard/src/lib/error-message.ts:3-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/lib/error-message.ts#L3-L16) 3. `ApiKeysPage` → `useApiQuery` → `run` → `toast`: Dispatches formatted error notifications through the Sonner toast wrapper interface. Sources: [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:37-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L37-L47), [server/dashboard/src/hooks/use-api-query.ts:39-45](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts#L39-L45), [server/dashboard/src/components/ui/use-toast.ts:16-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/components/ui/use-toast.ts#L16-L35) ```mermaid sequenceDiagram participant ApiKeysPage as ApiKeysPage participant DialogContent as DialogContent participant DialogOverlay as DialogOverlay participant useApiQuery as useApiQuery participant run as run participant getErrorMessage as getErrorMessage participant toast as toast ApiKeysPage->>DialogContent: Render dialog container DialogContent->>DialogOverlay: Mount backdrop overlay ApiKeysPage->>useApiQuery: Initialize query hook useApiQuery->>run: Execute fetcher function run->>getErrorMessage: Parse caught error details run->>toast: Dispatch notification variant ``` Sources: [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:37-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L37-L47), [server/dashboard/src/app/root/dashboard/api-keys/page.tsx:140-140](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/api-keys/page.tsx#L140-L140), [server/dashboard/src/components/ui/dialog.tsx:17-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/components/ui/dialog.tsx#L17-L39), [server/dashboard/src/hooks/use-api-query.ts:31-45](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/hooks/use-api-query.ts#L31-L45), [server/dashboard/src/lib/error-message.ts:3-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/lib/error-message.ts#L3-L16), [server/dashboard/src/components/ui/use-toast.ts:16-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/components/ui/use-toast.ts#L16-L35) ## Configuration and System Settings ### Provider Configuration Forms The server configuration view provides administrative forms for managing LLM and embedding providers alongside user profile and credential controls. The configuration page (`ConfigurationPage`) loads effective server settings from `MEMORY_ENDPOINTS.CONFIGURE` via `useApiQuery` and fetches supported provider lists from `MEMORY_ENDPOINTS.CONFIGURE_PROVIDERS`. The form manages provider identifiers, model names, and API keys for both LLM and embedding backends, restricting editing capabilities to users with administrator privileges (`isAdmin`). When saving, provider parameters are packaged into a versioned payload (`version: "v1.1"`) and posted back to the configure endpoint. ```typescript type BundledProviders = { llm: string[]; embedder: string[]; }; ``` Sources: [server/dashboard/src/app/root/dashboard/configuration/page.tsx:27-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/configuration/page.tsx#L27-L30), [server/dashboard/src/app/root/dashboard/configuration/page.tsx:41-57](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/configuration/page.tsx#L41-L57), [server/dashboard/src/app/root/dashboard/configuration/page.tsx:71-98](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/configuration/page.tsx#L71-L98) > [!IMPORTANT] > Selecting a new LLM provider automatically resets the associated API key state to an empty string to prevent credential leaks across provider boundaries. Sources: [server/dashboard/src/app/root/dashboard/configuration/page.tsx:131-134](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/configuration/page.tsx#L131-L134) ### User Profile and Password Mutations The settings view (`SettingsPage`) manages user account details and password rotation through authenticated endpoints. Profile updates are submitted via `AUTH_ENDPOINTS.ME` using HTTP PATCH requests when input values differ from existing user state and contain non-whitespace characters. Password changes require verification of the current password, matching confirmation values, and a minimum length of 8 characters for the new password before submission to `AUTH_ENDPOINTS.CHANGE_PASSWORD`. | Endpoint Constant | Path | Method | Purpose | | :--- | :--- | :--- | :--- | | `AUTH_ENDPOINTS.ME` | `/auth/me` | PATCH | Updates authenticated user name and email attributes | | `AUTH_ENDPOINTS.CHANGE_PASSWORD` | `/auth/change-password` | POST | Rotates the user account password given current credentials | | `MEMORY_ENDPOINTS.CONFIGURE` | `/configure` | GET / POST | Retrieves or persists server LLM and embedding provider settings | | `MEMORY_ENDPOINTS.CONFIGURE_PROVIDERS` | `/configure/providers` | GET | Fetches lists of bundled LLM and embedder providers | Sources: [server/dashboard/src/app/root/dashboard/configuration/page.tsx:43-52](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/configuration/page.tsx#L43-L52), [server/dashboard/src/app/root/dashboard/configuration/page.tsx:97-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/configuration/page.tsx#L97-L97), [server/dashboard/src/app/root/dashboard/settings/page.tsx:43-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/settings/page.tsx#L43-L46), [server/dashboard/src/app/root/dashboard/settings/page.tsx:71-74](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/settings/page.tsx#L71-L74), [server/dashboard/src/utils/api-endpoints.ts:6-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L6-L7), [server/dashboard/src/utils/api-endpoints.ts:15-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L15-L16) ## Requests Audit Telemetry ### Telemetry Data Fetching and Metrics The requests audit log view retrieves access logs from the self-hosted instance through `REQUEST_ENDPOINTS.BASE` (`/requests`) with a maximum query limit defined by `REQUEST_LOG_LIMIT` (200 records). Incoming `ApiRequestLog` entities are mapped via `normalizeLog` into structured `RequestLog` objects comprising unique identifiers, creation timestamps, HTTP methods, paths, status codes, latency measurements in milliseconds, and authentication source types. ```typescript type RequestLog = { id: string; createdAt: string; method: string; path: string; statusCode: number; latencyMs: number; authType: string; }; ``` Sources: [server/dashboard/src/app/root/dashboard/requests/page.tsx:17-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/requests/page.tsx#L17-L27), [server/dashboard/src/app/root/dashboard/requests/page.tsx:71-81](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/requests/page.tsx#L71-L81), [server/dashboard/src/utils/api-endpoints.ts:26-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/utils/api-endpoints.ts#L26-L28) Client-side calculations derive summary metrics from fetched logs including total request counts, successful requests (`statusCode < 400`), overall success percentage rates, and average latency values. Pagination divides the data stream into chunks governed by `PAGE_SIZE` (20 items per view). Sources: [server/dashboard/src/app/root/dashboard/requests/page.tsx:28-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/requests/page.tsx#L28-L28), [server/dashboard/src/app/root/dashboard/requests/page.tsx:103-114](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/requests/page.tsx#L103-L114) > [!NOTE] > Resetting the pagination page index to zero (`setPage(0)`) occurs automatically whenever the manual refresh button is triggered alongside query refetching. Sources: [server/dashboard/src/app/root/dashboard/requests/page.tsx:188-192](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/requests/page.tsx#L188-L192) ### Method and Status Tag Styling Badge presentation utilizes distinct color styles based on HTTP request verbs and numerical response status codes. The `getMethodClassName` helper inspects normalized method strings case-insensitively, assigning sky-blue styles to `POST`, amber tones to `PUT` and `PATCH`, rose styling to `DELETE`, and fallback primary borders to unrecognized verbs. | HTTP Method | Badge Theme Styling | | :--- | :--- | | `POST` | Sky blue (`border-sky-200 bg-sky-50 text-sky-700`) | | `PUT`, `PATCH` | Amber (`border-amber-200 bg-amber-50 text-amber-700`) | | `DELETE` | Rose (`border-rose-200 bg-rose-50 text-rose-700`) | | Other / Default | Surface secondary (`border-memBorder-primary bg-surface-default-secondary`) | Sources: [server/dashboard/src/app/root/dashboard/requests/page.tsx:42-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/requests/page.tsx#L42-L54) Similarly, `getStatusClassName` evaluates numeric HTTP response codes to apply error-severity styling: status codes of 500 and above render in rose tones, codes from 400 to 499 apply amber styling, and successful codes below 400 render with emerald green borders and background tints. Sources: [server/dashboard/src/app/root/dashboard/requests/page.tsx:30-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/requests/page.tsx#L30-L40) ### Authentication Source Inspection The audit table inspects the `authType` property on incoming request entries to identify the verification mechanism used by the client. The `getAuthLabel` utility maps internal authentication identifiers to human-readable labels for presentation in the telemetry data grid. | Raw Auth Type Value | Display Label | | :--- | :--- | | `bearer` | `JWT` | | `api_key` | `API Key` | | `admin_api_key` | `Admin Key` | | `disabled` | `Disabled` | | Any other value | `--` | Sources: [server/dashboard/src/app/root/dashboard/requests/page.tsx:56-69](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/requests/page.tsx#L56-L69) ## Locked Features and Commercial Mockups ### Analytics Feature Wrapper The self-hosted dashboard presents usage insights behind commercial wrappers via the `LockedPage` component. The analytics view specifies `utmMedium="dashboard-locked-analytics"`, rendering a description (`"Track memory operations, latency, and usage patterns over time."`) above an interactive visual mockup. ```typescript export default function AnalyticsPage() { return ( } utmMedium="dashboard-locked-analytics" /> ); } ``` Sources: [server/dashboard/src/app/root/dashboard/analytics/page.tsx:45-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/analytics/page.tsx#L45-L54) The `AnalyticsMockup` layout organizes metrics into two primary structural blocks: a top metric summary grid displaying statistical cards, followed by a time-series operations chart container. | Stat Label | Mock Value | Card Container Style | | :--- | :--- | :--- | | `Total Operations` | `"12,847"` | `border-memBorder-primary` | | `Avg Latency` | `"142ms"` | `border-memBorder-primary` | | `Success Rate` | `"99.7%"` | `border-memBorder-primary` | Sources: [server/dashboard/src/app/root/dashboard/analytics/page.tsx:9-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/analytics/page.tsx#L9-L24) The operations chart renders a 200-pixel-tall flex container mapping twelve discrete height percentages (`[40, 65, 45, 80, 55, 90, 70, 85, 60, 95, 75, 50]`) to individual column elements styled with `bg-surface-default-brand rounded-t`. Sources: [server/dashboard/src/app/root/dashboard/analytics/page.tsx:30-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/analytics/page.tsx#L30-L39) > [!NOTE] > All interactive inputs, buttons, and form controls within locked feature mockups are explicitly disabled (`disabled`) to indicate that the underlying feature requires a commercial license upgrade. Sources: [server/dashboard/src/app/root/dashboard/webhooks/page.tsx:16-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/webhooks/page.tsx#L16-L38), [server/dashboard/src/app/root/dashboard/export/page.tsx:21-49](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/export/page.tsx#L21-L49) ### Webhooks and Export Mockup Configurations The webhooks locked view uses `utmMedium="dashboard-locked-webhooks"` and presents a card containing an endpoint URL input field, a disabled checkbox list of event types, a masked signing secret field, and a submit button. | Event Name | Checkbox Initial State | | :--- | :--- | | `memory.created` | Checked (`true`, disabled) | | `memory.updated` | Unchecked (`false`, disabled) | | `memory.deleted` | Unchecked (`false`, disabled) | | `search.performed` | Unchecked (`false`, disabled) | Sources: [server/dashboard/src/app/root/dashboard/webhooks/page.tsx:21-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/webhooks/page.tsx#L21-L31) Similarly, the export locked view sets `utmMedium="dashboard-locked-export"` and exposes format options alongside a preview schema output box. ```json { "memories": [ { "id": "mem_abc123", "content": "User prefers dark mode", "user_id": "user_1", "created_at": "2026-04-10T12:00:00Z" } ], "total": 1247 } ``` Sources: [server/dashboard/src/app/root/dashboard/export/page.tsx:34-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/export/page.tsx#L34-L44) The export format selector lists three format buttons (`JSON`, `CSV`, and `Pydantic Schema`), where the `JSON` button receives an active border highlight class (`border-memPurple-300`). Sources: [server/dashboard/src/app/root/dashboard/export/page.tsx:16-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/dashboard/src/app/(root)/dashboard/export/page.tsx#L16-L26) ## Related - [Dashboard Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/dashboard-architecture) - [Server API Endpoints](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/server-api-endpoints) --- ## Technical docs: POST Complete Onboarding URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/auth/onboarding-complete ## Request Body Onboarding Complete Request ## Responses ## Try It --- ## Technical docs: Python CLI Architecture URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/python-cli-architecture
Relevant source files The following files were used as context for generating this wiki page: - [cli/python/src/mem0_cli/app.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py) - [integrations/openclaw/cli/commands.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts) - [cli/python/src/mem0_cli/backend/platform.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/platform.py) - [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts) - [cli/python/src/mem0_cli/commands/memory.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/memory.py) - [cli/python/src/mem0_cli/backend/base.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/base.py) - [integrations/mem0-agent-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py) - [cli/python/src/mem0_cli/output.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py) - [integrations/kimi-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_cli.py) - [integrations/codex-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_cli.py) - [integrations/antigravity-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_cli.py) - [integrations/agent-plugin-core/python/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py) - [integrations/claude-code-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_cli.py) - [integrations/cursor-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_cli.py) - [cli/python/src/mem0_cli/branding.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/branding.py) - [cli/python/src/mem0_cli/commands/events_cmd.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/events_cmd.py) - [cli/python/src/mem0_cli/backend/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/__init__.py) - [cli/python/src/mem0_cli/commands/utils.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/utils.py) - [cli/python/src/mem0_cli/commands/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/__init__.py) - [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts) - [scripts/oss-to-platform-migrate.sh](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh) - [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts) - [cli/node/src/backend/base.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/base.ts) - [cli/python/src/mem0_cli/__main__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/__main__.py) - [cli/python/src/mem0_cli/config.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/config.py) - [cli/python/src/mem0_cli/commands/entities.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/entities.py)
## Overview The Python CLI architecture of the Mem0 CLI provides a Typer-powered command-line interface designed for managing AI agent memory layers and platform integrations. It coordinates argument parsing, subcommand routing, piped input handling, and secure configuration persistence while abstracting communication through a pluggable backend layer. By supporting multiple output modes—including human-readable Rich console rendering, tabular displays, and structured JSON envelopes—the CLI serves both interactive operators and programmatic AI agents. Sources: [cli/python/src/mem0_cli/app.py:1-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py#L1-L32), [cli/python/src/mem0_cli/config.py:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/config.py#L1-L8), [cli/python/src/mem0_cli/config.py:147-150](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/config.py#L147-L150), [cli/python/src/mem0_cli/output.py:207-218](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py#L207-L218), [cli/python/src/mem0_cli/output.py:338-342](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py#L338-L342) ## Entrypoint and Dispatch Architecture ### Entrypoint and Execution Dispatch The Mem0 CLI is initialized through `cli/python/src/mem0_cli/__main__.py`, which directly invokes the `main()` function exported by the core application module. Command execution relies on Typer to manage subcommands, arguments, and global options while orchestrating execution dispatch. ```python """Allow running with `python -m mem0_cli`.""" from mem0_cli.app import main main() ``` Sources: [cli/python/src/mem0_cli/__main__.py:1-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/__main__.py#L1-L5) The main Typer application instance (`app`) is configured with specific formatting settings, help behaviors, and version metadata upon startup. ```python app = typer.Typer( name="mem0", help=f"◆ Mem0 CLI v{__version__} · Python SDK\n\n The Memory Layer for AI Agents", no_args_is_help=True, rich_markup_mode="rich", pretty_exceptions_enable=False, add_completion=False, subcommand_metavar=" [options]", options_metavar="", ) ``` Sources: [cli/python/src/mem0_cli/app.py:23-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py#L23-L32) ### Global Options and Argument Resolution Global arguments and options—such as version flags and agent output formatting—are processed via the primary application callback (`main_callback`). Sub-groups for configuration (`config_app`), entity operations (`entity_app`), and event inspection (`event_app`) are defined independently and later registered onto the primary app instance to govern help menu ordering and isolate telemetry triggers. ```python config_app = typer.Typer( name="config", help="Manage mem0 configuration.", no_args_is_help=True, rich_markup_mode="rich", ) entity_app = typer.Typer( name="entity", help="Manage entities.", no_args_is_help=True, rich_markup_mode="rich", ) event_app = typer.Typer( name="event", help="Inspect background processing events.", no_args_is_help=True, rich_markup_mode="rich", ) ``` Sources: [cli/python/src/mem0_cli/app.py:36-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py#L36-L55) Entity IDs are dynamically resolved through `_resolve_ids`, which prioritizes explicit CLI arguments over configuration file defaults unless no explicit identifiers are specified. ```python def _resolve_ids( config, *, user_id: str | None = None, agent_id: str | None = None, app_id: str | None = None, run_id: str | None = None, ): """Resolve entity IDs: CLI flag > config default > None. If any explicit ID is provided, only use explicit IDs (don't mix in defaults for other entity types which would over-filter). If no explicit IDs, fall back to all configured defaults. """ has_explicit = any([user_id, agent_id, app_id, run_id]) if has_explicit: return { "user_id": user_id or None, "agent_id": agent_id or None, "app_id": app_id or None, "run_id": run_id or None, } return { "user_id": config.defaults.user_id or None, "agent_id": config.defaults.agent_id or None, "app_id": config.defaults.app_id or None, "run_id": config.defaults.run_id or None, } ``` Sources: [cli/python/src/mem0_cli/app.py:164-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py#L164-L191) > [!NOTE] > When any explicit entity ID (`user_id`, `agent_id`, `app_id`, or `run_id`) is passed on the command line, `_resolve_ids` completely suppresses fallback defaults for other entity scopes to prevent unintended cross-filtering. ### Piped Input Handling The CLI inspects stream conditions to support piping text directly into memory creation or search commands from standard input. The input handler verifies both file descriptor status and runtime execution context using `_stdin_is_piped` and `_read_stdin`. ```python def _stdin_is_piped() -> bool: """Return True only when stdin is an actual pipe or file redirect — not a bare open fd.""" from mem0_cli.state import is_agent_mode if is_agent_mode(): return False try: mode = os.fstat(sys.stdin.fileno()).st_mode return _stat_mod.S_ISFIFO(mode) or _stat_mod.S_ISREG(mode) except Exception: return False def _read_stdin() -> str | None: """Read from stdin if it is an actual pipe or file redirect (not a TTY, not agent mode).""" if _stdin_is_piped(): return sys.stdin.read().strip() or None return None ``` Sources: [cli/python/src/mem0_cli/app.py:194-211](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py#L194-L211) > [!WARNING] > Piped input extraction via `_stdin_is_piped()` automatically returns `False` if agent mode is active (`is_agent_mode() == True`), preventing programmatic subprocess hangs when standard input is redirected by autonomous agent frameworks. ### Backend Validation and Telemetry Flow Before dispatching commands that require network connectivity, `_get_backend_and_config` initializes the platform client, verifies API tokens against the `/v1/ping/` endpoint with a strict 5-second timeout, and tracks user identifiers for telemetry. ```python def _get_backend_and_config( api_key: str | None = None, base_url: str | None = None, ): """Build and return the Platform backend plus the loaded config. Validates the API key upfront via ``/v1/ping/`` and caches the resolved user email for telemetry. """ global _validated_user_email from mem0_cli.backend import get_backend from mem0_cli.backend.platform import AuthError from mem0_cli.config import load_config, save_config config = load_config() if api_key: config.platform.api_key = api_key if base_url: config.platform.base_url = base_url if not config.platform.api_key: print_error( err_console, "No API key configured.", hint="Run 'mem0 init' or set MEM0_API_KEY environment variable.", ) raise typer.Exit(1) backend = get_backend(config) # Validate the API key upfront with a fast timeout try: ping_data = backend.ping(timeout=5.0) email = ping_data.get("user_email") if isinstance(ping_data, dict) else None if email: _validated_user_email = email if config.platform.user_email != email: config.platform.user_email = email with contextlib.suppress(Exception): save_config(config) except AuthError: print_error( err_console, "Invalid or expired API key.", hint="Run 'mem0 init' or set MEM0_API_KEY environment variable.", ) raise typer.Exit(1) from None except Exception: print_warning(err_console, "Could not validate API key (network issue). Proceeding anyway.") return backend, config ``` Sources: [cli/python/src/mem0_cli/app.py:100-152](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py#L100-L152) ## Configuration Model and State Management ### Configuration Precedence and Directory Structure The Mem0 Python CLI manages persistent user options, authentication keys, default scopes, and telemetry identifiers through a hierarchical configuration store located at `~/.mem0/config.json`. Configuration values and runtime settings are resolved following a strict precedence order where higher-priority inputs override lower-priority defaults: | Precedence | Source Layer | Examples | | :--- | :--- | :--- | | 1 (Highest) | CLI Flags | `--api-key`, `--base-url` | | 2 | Environment Variables | `MEM0_API_KEY`, `MEM0_BASE_URL`, `MEM0_USER_ID`, `MEM0_AGENT_ID`, `MEM0_APP_ID`, `MEM0_RUN_ID` | | 3 | Configuration File | `~/.mem0/config.json` | | 4 (Lowest) | Hardcoded Defaults | `https://api.mem0.ai`, version `1`, empty strings | Sources: [cli/python/src/mem0_cli/config.py:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/config.py#L1-L8) When the configuration directory or file is initialized or updated, `ensure_config_dir` guarantees that `~/.mem0` is created with strict file permissions (`0700`, restricting access exclusively to the file owner), while saved configuration files are explicitly secured with user-only read and write permissions (`0600`). ```python CONFIG_DIR = Path.home() / ".mem0" CONFIG_FILE = CONFIG_DIR / "config.json" DEFAULT_BASE_URL = "https://api.mem0.ai" CONFIG_VERSION = 1 ``` Sources: [cli/python/src/mem0_cli/config.py:19-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/config.py#L19-L23) ```python def ensure_config_dir() -> Path: """Create ~/.mem0 directory with secure permissions if it doesn't exist.""" CONFIG_DIR.mkdir(parents=True, exist_ok=True) os.chmod(CONFIG_DIR, stat.S_IRWXU) # 0700 return CONFIG_DIR ``` Sources: [cli/python/src/mem0_cli/config.py:81-85](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/config.py#L81-L85) ```python with open(CONFIG_FILE, "w") as f: json.dump(data, f, indent=2) os.chmod(CONFIG_FILE, stat.S_IRUSR | stat.S_IWUSR) # 0600 ``` Sources: [cli/python/src/mem0_cli/config.py:177-181](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/config.py#L177-L181) > [!CAUTION] > Both `ensure_config_dir` and `save_config` execute explicit `os.chmod` calls enforcing `0700` on the `.mem0` directory and `0600` on `config.json`. Unprivileged environments or container filesystems lacking POSIX permission support may fail to restrict file modes, though configuration serialization proceeds regardless. ### Dataclass Schemas and Aliases The underlying data model is structured around typed dataclasses that represent distinct domains: `PlatformConfig`, `DefaultsConfig`, `TelemetryConfig`, and `AgentRushConfig`, all aggregated inside `Mem0Config`. Short-form CLI configuration keys map directly to nested attributes via `SHORT_KEY_ALIASES`. ```python @dataclass class PlatformConfig: api_key: str = "" base_url: str = DEFAULT_BASE_URL user_email: str = "" # Agent Mode (unclaimed-shadow signup) agent_mode: bool = False # True while the key is an unclaimed agent-mode key created_via: str = "" # "agent_mode" | "email" | "api_key" | "existing_key" agent_caller: str = ( "" # canonical agent name when created_via == "agent_mode" (e.g. "claude-code") ) claimed_at: str = "" # ISO timestamp once the agent has been claimed by a human default_user_id: str = "" # `user_` returned by bootstrap; used as auto-default @dataclass class DefaultsConfig: user_id: str = "" agent_id: str = "" app_id: str = "" run_id: str = "" @dataclass class TelemetryConfig: anonymous_id: str = "" @dataclass class AgentRushConfig: # ISO timestamp the human acknowledged the "memories are public" warning. # Empty until first interactive `mem0 agent-rush add`. acknowledged_at: str = "" @dataclass class Mem0Config: version: int = CONFIG_VERSION defaults: DefaultsConfig = field(default_factory=DefaultsConfig) platform: PlatformConfig = field(default_factory=PlatformConfig) telemetry: TelemetryConfig = field(default_factory=TelemetryConfig) agent_rush: AgentRushConfig = field(default_factory=AgentRushConfig) SHORT_KEY_ALIASES: dict[str, str] = { "api_key": "platform.api_key", "base_url": "platform.base_url", "user_email": "platform.user_email", "user_id": "defaults.user_id", "agent_id": "defaults.agent_id", "app_id": "defaults.app_id", "run_id": "defaults.run_id", } ``` Sources: [cli/python/src/mem0_cli/config.py:26-78](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/config.py#L26-L78) > [!NOTE] > When `save_config` serializes active options to disk, it also invokes `plugin_sync.sync_api_key(config.platform.api_key)` as an idempotent, best-effort operation to propagate API keys into ecosystem touchpoints like shell RC files and plugin environment files, catching any `IOError` to ensure `config.json` remains the authoritative write target. ### Nested Loading and Type Coercion Configuration loading is implemented by `load_config`, which instantiates a base `Mem0Config`, parses JSON data from disk if present, and layers environment variable overrides on top. ```python def load_config() -> Mem0Config: """Load config from file, applying env var overrides.""" config = Mem0Config() if CONFIG_FILE.exists(): with open(CONFIG_FILE) as f: data = json.load(f) config.version = data.get("version", CONFIG_VERSION) plat = data.get("platform", {}) config.platform.api_key = plat.get("api_key", "") config.platform.base_url = plat.get("base_url", DEFAULT_BASE_URL) config.platform.user_email = plat.get("user_email", "") config.platform.agent_mode = bool(plat.get("agent_mode", False)) config.platform.created_via = plat.get("created_via", "") config.platform.agent_caller = plat.get("agent_caller", "") config.platform.claimed_at = plat.get("claimed_at", "") config.platform.default_user_id = plat.get("default_user_id", "") defaults = data.get("defaults", {}) config.defaults.user_id = defaults.get("user_id", "") config.defaults.agent_id = defaults.get("agent_id", "") config.defaults.app_id = defaults.get("app_id", "") config.defaults.run_id = defaults.get("run_id", "") telemetry = data.get("telemetry", {}) config.telemetry.anonymous_id = telemetry.get("anonymous_id", "") agent_rush = data.get("agent_rush", {}) config.agent_rush.acknowledged_at = agent_rush.get("acknowledged_at", "") # Environment variable overrides env_key = os.environ.get("MEM0_API_KEY") if env_key: config.platform.api_key = env_key env_base = os.environ.get("MEM0_BASE_URL") if env_base: config.platform.base_url = env_base env_user_id = os.environ.get("MEM0_USER_ID") if env_user_id: config.defaults.user_id = env_user_id env_agent_id = os.environ.get("MEM0_AGENT_ID") if env_agent_id: config.defaults.agent_id = env_agent_id env_app_id = os.environ.get("MEM0_APP_ID") if env_app_id: config.defaults.app_id = env_app_id env_run_id = os.environ.get("MEM0_RUN_ID") if env_run_id: config.defaults.run_id = env_run_id return config ``` Sources: [cli/python/src/mem0_cli/config.py:88-144](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/config.py#L88-L144) Property getters and setters (`get_nested_value` and `set_nested_value`) support dotted path strings (such as `platform.api_key` or `defaults.user_id`) along with short aliases. The setter performs automatic type coercion based on the existing attribute type, converting boolean representations and integers safely. ```python def get_nested_value(config: Mem0Config, dotted_key: str) -> Any: """Get a config value by dotted path, e.g. 'platform.api_key' or short form 'api_key'.""" dotted_key = SHORT_KEY_ALIASES.get(dotted_key, dotted_key) parts = dotted_key.split(".") obj: Any = config for part in parts: if hasattr(obj, part): obj = getattr(obj, part) else: return None return obj def set_nested_value(config: Mem0Config, dotted_key: str, value: str) -> bool: """Set a config value by dotted path. Returns True on success.""" dotted_key = SHORT_KEY_ALIASES.get(dotted_key, dotted_key) parts = dotted_key.split(".") obj: Any = config for part in parts[:-1]: if hasattr(obj, part): obj = getattr(obj, part) else: return False final_key = parts[-1] if not hasattr(obj, final_key): return False current = getattr(obj, final_key) # Type coercion if isinstance(current, bool): value = value.lower() in ("true", "1", "yes") # type: ignore[assignment] elif isinstance(current, int): try: value = int(value) # type: ignore[assignment] except ValueError: return False setattr(obj, final_key, value) return True ``` Sources: [cli/python/src/mem0_cli/config.py:205-244](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/config.py#L205-L244) ## Backend Abstraction and Factory Selection ### Abstract Interface Definition The `Backend` abstract base class, defined in `cli/python/src/mem0_cli/backend/base.py`, establishes the core contract that any storage or platform backend must fulfill. It inherits from `ABC` and declares eleven abstract methods covering memory creation, semantic and keyword search, record retrieval, paginated listing, updates, deletion, entity management, status checks, and event inspection. ```python class Backend(ABC): """Abstract interface for mem0 backends.""" @abstractmethod def add( self, content: str | None = None, messages: list[dict] | None = None, *, user_id: str | None = None, agent_id: str | None = None, app_id: str | None = None, run_id: str | None = None, metadata: dict | None = None, immutable: bool = False, infer: bool = True, expires: str | None = None, custom_instructions: str | None = None, agent_custom_instructions: str | None = None, custom_categories: list[dict] | None = None, structured_data_schema: dict | None = None, timestamp: int | None = None, ) -> dict: ... @abstractmethod def search( self, query: str, *, user_id: str | None = None, agent_id: str | None = None, app_id: str | None = None, run_id: str | None = None, top_k: int = 10, threshold: float = 0.3, rerank: bool = False, keyword: bool = False, filters: dict | None = None, fields: list[str] | None = None, show_expired: bool = False, reference_date: str | None = None, latest_only: bool = False, ) -> list[dict]: ... @abstractmethod def get(self, memory_id: str) -> dict: ... @abstractmethod def list_memories( self, *, user_id: str | None = None, agent_id: str | None = None, app_id: str | None = None, run_id: str | None = None, page: int = 1, page_size: int = 100, category: str | None = None, after: str | None = None, before: str | None = None, show_expired: bool = False, latest_only: bool = False, ) -> list[dict]: ... @abstractmethod def update( self, memory_id: str, content: str | None = None, metadata: dict | None = None, *, expiration_date: str | None = None, timestamp: int | None = None, ) -> dict: ... @abstractmethod def delete( self, memory_id: str | None = None, *, ``` Sources: [cli/python/src/mem0_cli/backend/base.py:11-90](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/base.py#L11-L90) Every method enforces explicit keyword arguments and positional scopes such as `user_id`, `agent_id`, `app_id`, and `run_id`. The contract ensures uniform method signatures across different backend targets. | Abstract Method | Return Type | Key Parameters | | :--- | :--- | :--- | | `add` | `dict` | `content`, `messages`, `user_id`, `agent_id`, `app_id`, `run_id`, `metadata`, `immutable`, `infer`, `expires`, `custom_instructions`, `agent_custom_instructions`, `custom_categories`, `structured_data_schema`, `timestamp` | | `search` | `list[dict]` | `query`, `top_k`, `threshold`, `rerank`, `keyword`, `filters`, `fields`, `show_expired`, `reference_date`, `latest_only` | | `get` | `dict` | `memory_id` | | `list_memories` | `list[dict]` | `page`, `page_size`, `category`, `after`, `before`, `show_expired`, `latest_only` | | `update` | `dict` | `memory_id`, `content`, `metadata`, `expiration_date`, `timestamp` | | `delete` | `dict` | `memory_id`, `all`, `delete_linked` | | `delete_entities` | `dict` | `user_id`, `agent_id`, `app_id`, `run_id` | | `status` | `dict[str, Any]` | `user_id`, `agent_id` | | `entities` | `list[dict]` | `entity_type` | | `list_events` | `list[dict]` | *(none)* | | `get_event` | `dict` | `event_id` | Sources: [cli/python/src/mem0_cli/backend/base.py:15-124](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/base.py#L15-L124) > [!NOTE] > The abstract interface does not prescribe storage or serialization details; it defines pure method boundaries so that command handlers interact with memory collections without knowing whether data resides locally or on a remote SaaS cluster. ### Platform Backend Instantiation and Lifecycle The `PlatformBackend` concrete class implements the `Backend` interface by communicating directly with the Mem0 Platform API (`api.mem0.ai`). Its constructor accepts a `PlatformConfig` instance, strips trailing slashes from `base_url`, and configures a persistent `httpx.Client` instance with request headers. ```python class PlatformBackend(Backend): """Backend that talks to the mem0 Platform API.""" def __init__(self, config: PlatformConfig) -> None: self.config = config self.base_url = config.base_url.rstrip("/") self._client = httpx.Client( base_url=self.base_url, headers={ "Authorization": f"Token {config.api_key}", "Content-Type": "application/json", "X-Mem0-Source": "CLI", "X-Mem0-Client": f"mem0-cli-python/{__version__}", "X-Mem0-Client-Language": "python", "X-Mem0-Client-Version": __version__, }, timeout=30.0, ) ``` Sources: [cli/python/src/mem0_cli/backend/platform.py:19-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/platform.py#L19-L36) The HTTP client injects authentication and telemetry headers into every outbound request: | Header Name | Value / Format | | :--- | :--- | | `Authorization` | `Token {config.api_key}` | | `Content-Type` | `application/json` | | `X-Mem0-Source` | `CLI` | | `X-Mem0-Client` | `mem0-cli-python/{__version__}` | | `X-Mem0-Client-Language` | `python` | | `X-Mem0-Client-Version` | `__version__` | Sources: [cli/python/src/mem0_cli/backend/platform.py:27-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/platform.py#L27-L34) The client enforces a default timeout of `30.0` seconds. Path segments are encoded using `_encode_path_segment`, which wraps `urllib.parse.quote` with `safe=""` to prevent path traversal or malformed URL injection. ```python def _encode_path_segment(value: Any) -> str: return quote(str(value), safe="") ``` Sources: [cli/python/src/mem0_cli/backend/platform.py:15-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/platform.py#L15-L16) ### Factory Dispatch Backend creation is centralized in the `get_backend` factory function. It accepts a `Mem0Config` instance and instantiates `PlatformBackend` using `config.platform`. ```python def get_backend(config: Mem0Config) -> Backend: """Return the Platform backend.""" from mem0_cli.backend.platform import PlatformBackend ``` Sources: [cli/python/src/mem0_cli/backend/base.py:127-130](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/backend/base.py#L127-L130) > [!TIP] > `get_backend` performs a local import of `PlatformBackend` inside the function body rather than at the module level. This lazy import prevents circular dependencies between configuration modules, state managers, and platform client definitions during CLI startup. ## Memory Command Workflows and Execution ### Memory Operations and Execution Flows The memory command layer handles user requests by validating input parameters, invoking backend methods within timed status contexts, and formatting responses based on terminal flags or autonomous agent mode. Memory creation, search, retrieval, listing, import, and background event inspection are orchestrated through dedicated command handlers. When searching memories using `cmd_search`, the CLI validates `--top-k` against a minimum value of `1` and ensures `--threshold` falls within the inclusive range `0.0` to `1.0`. Any supplied filter JSON is parsed via `json.loads` before invoking `backend.search`. Similarly, `cmd_list` enforces boundary checks on `page` and `page_size` parameters prior to calling `backend.list_memories`. ```python if top_k < 1: print_error(err_console, "--top-k must be >= 1.") raise typer.Exit(1) if not (0.0 <= threshold <= 1.0): print_error(err_console, "--threshold must be between 0.0 and 1.0.") raise typer.Exit(1) ``` Sources: [cli/python/src/mem0_cli/commands/memory.py:277-283](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/memory.py#L277-L283) > [!WARNING] > Failing to provide valid JSON to `--filter` during a search or passing non-positive integer values to pagination flags results in an immediate termination via `typer.Exit(1)` after writing an error description to the standard error console. ### Execution Call Chains and Agent Envelopes Command execution pipelines leverage shared formatting functions when operating under `--agent` mode or structured JSON output. For list and search operations, raw backend records pass through envelope generation and field projection before rendering. The execution call chain for listing memories proceeds as follows: 1. `cmd_list` invokes `backend.list_memories` and receives raw records. 2. `format_agent_envelope` wraps the command name, duration, scope filters, and data. 3. `sanitize_agent_data` processes the command category and project dataset items. 4. `pick` filters dictionary keys down to the minimal required subset. ```python results = backend.list_memories( user_id=user_id, agent_id=agent_id, app_id=app_id, run_id=run_id, page=page, page_size=page_size, category=category, after=after, before=before, show_expired=show_expired, latest_only=latest_only, ) … def sanitize_agent_data(command: str, data: Any) -> Any: """Project API response data to minimal relevant fields for agent consumption.""" def pick(obj: dict, keys: list) -> dict: return {k: obj[k] for k in keys if k in obj} if data is None: return data if command == "add": items = data if isinstance(data, list) else [data] result = [] for item in items: if item.get("status") == "PENDING": result.append(pick(item, ["status", "event_id"])) else: result.append(pick(item, ["id", "memory", "event"])) return result if command == "search": return [ pick(r, ["id", "memory", "score", "created_at", "categories", "expiration_date"]) for r in data ] if command == "list": return [ pick(r, ["id", "memory", "created_at", "categories", "expiration_date"]) for r in data ] ``` Sources: [cli/python/src/mem0_cli/commands/memory.py:408-420](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/memory.py#L408-L420), [cli/python/src/mem0_cli/output.py:245-274](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py#L245-L274), [cli/python/src/mem0_cli/output.py:329-361](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py#L329-L361) Similarly, the execution call chain for searching memories proceeds as: 1. `cmd_search` queries the backend and measures execution time via `_time.perf_counter()`. 2. `format_agent_envelope` constructs a JSON envelope containing execution duration and scope parameters. 3. `sanitize_agent_data` matches the `search` command type. 4. `pick` extracts only `id`, `memory`, `score`, `created_at`, `categories`, and `expiration_date` fields from each result object. ```python results = backend.search( query, user_id=user_id, agent_id=agent_id, app_id=app_id, run_id=run_id, top_k=top_k, threshold=threshold, rerank=rerank, keyword=keyword, filters=filters, fields=field_list, show_expired=show_expired, reference_date=reference_date, latest_only=latest_only, ) … def format_agent_envelope( console: Console, *, command: str, data: Any, duration_ms: int | None = None, scope: dict | None = None, count: int | None = None, ) -> None: """Output structured JSON envelope for agent/programmatic use (--json/--agent mode).""" envelope: dict[str, Any] = { "status": "success", "command": command, } if duration_ms is not None: envelope["duration_ms"] = duration_ms if scope: filtered = {k: v for k, v in scope.items() if v} if filtered: envelope["scope"] = filtered if count is not None: envelope["count"] = count envelope["data"] = sanitize_agent_data(command, data) # Surface the unclaimed-Agent-Mode notice (if any) in the envelope so an # agent reading the JSON output sees it without inspecting HTTP headers. from mem0_cli.state import take_notice notice = take_notice() if notice: envelope["mem0_notice"] = notice console.print_json(json.dumps(envelope, default=str)) … def sanitize_agent_data(command: str, data: Any) -> Any: """Project API response data to minimal relevant fields for agent consumption.""" def pick(obj: dict, keys: list) -> dict: return {k: obj[k] for k in keys if k in obj} if data is None: return data if command == "add": items = data if isinstance(data, list) else [data] result = [] for item in items: if item.get("status") == "PENDING": result.append(pick(item, ["status", "event_id"])) else: result.append(pick(item, ["id", "memory", "event"])) return result if command == "search": return [ pick(r, ["id", "memory", "score", "created_at", "categories", "expiration_date"]) for r in data ] ``` Sources: [cli/python/src/mem0_cli/commands/memory.py:287-302](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/memory.py#L287-L302), [cli/python/src/mem0_cli/output.py:245-268](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py#L245-L268), [cli/python/src/mem0_cli/output.py:329-361](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py#L329-L361) ```mermaid sequenceDiagram participant memory.py as memory.py participant output.py as output.py memory.py->>output.py: format_agent_envelope() output.py->>output.py: sanitize_agent_data() output.py->>output.py: pick() ``` Sources: [cli/python/src/mem0_cli/commands/memory.py:408-420](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/memory.py#L408-L420), [cli/python/src/mem0_cli/output.py:245-361](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py#L245-L361) ### Background Event Inspection and Status Mapping Asynchronous processing workflows on the platform generate background events that can be inspected via event commands. The `events_cmd` module maps event states to rich terminal styles and formats detailed execution summaries. ```python _STATUS_STYLE = { "SUCCEEDED": f"[{SUCCESS_COLOR}]SUCCEEDED[/]", "PENDING": f"[{ACCENT_COLOR}]PENDING[/]", "FAILED": f"[{ERROR_COLOR}]FAILED[/]", "PROCESSING": f"[{WARNING_COLOR}]PROCESSING[/]", } ``` Sources: [cli/python/src/mem0_cli/commands/events_cmd.py:26-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/events_cmd.py#L26-L31) | Status Constant | Console Style / Color Mapping | | :--- | :--- | | `SUCCEEDED` | `[{SUCCESS_COLOR}]SUCCEEDED[/]` | | `PENDING` | `[{ACCENT_COLOR}]PENDING[/]` | | `FAILED` | `[{ERROR_COLOR}]FAILED[/]` | | `PROCESSING` | `[{WARNING_COLOR}]PROCESSING[/]` | Sources: [cli/python/src/mem0_cli/commands/events_cmd.py:26-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/events_cmd.py#L26-L31) When executing `cmd_event_status`, backend event details are retrieved and formatted into a Rich panel containing metadata, latency figures, creation and update timestamps, and nested result items. ```python with timed_status(err_console, "Fetching event...") as _ts: try: ev = backend.get_event(event_id) … panel = Panel( content, title=f"[{BRAND_COLOR}]Event Status[/]", title_align="left", border_style=BRAND_COLOR, padding=(1, 1), ) console.print() console.print(panel) ``` Sources: [cli/python/src/mem0_cli/commands/events_cmd.py:114-116](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/events_cmd.py#L114-L116), [cli/python/src/mem0_cli/commands/events_cmd.py:167-175](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/events_cmd.py#L167-L175) > [!TIP] > When background memory ingestion returns `PENDING` status entries from the platform API, the output formatter automatically deduplicates events sharing the same `event_id` and prompts the user with status checking instructions. ## Multi-Format Output and Terminal Rendering ### Terminal Styling and Branding The CLI utilizes the Rich library to render decorative console components, ASCII banners, and styled text blocks. Color palettes are centrally defined in the branding module to maintain visual consistency across interactive shells. ```python BRAND_COLOR = "#8b5cf6" # Purple ACCENT_COLOR = "#a78bfa" SUCCESS_COLOR = "#22c55e" ERROR_COLOR = "#ef4444" WARNING_COLOR = "#f59e0b" DIM_COLOR = "#6b7280" ``` Sources: [cli/python/src/mem0_cli/branding.py:29-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/branding.py#L29-L34) | Color Constant | Hex Value | Semantic Purpose | | :--- | :--- | :--- | | `BRAND_COLOR` | `#8b5cf6` | Primary purple branding, welcome panel borders, and info symbols | | `ACCENT_COLOR` | `#a78bfa` | Secondary highlights, updated state icons, and table headers | | `SUCCESS_COLOR` | `#22c55e` | Success indicators, added memory plus icons | | `ERROR_COLOR` | `#ef4444` | Error messages and deleted memory minus symbols | | `WARNING_COLOR` | `#f59e0b` | Warning indicators and processing states | | `DIM_COLOR` | `#6b7280` | Muted text, timestamps, scopes, and no-change symbols | Sources: [cli/python/src/mem0_cli/branding.py:29-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/branding.py#L29-L34) ASCII symbols adapt dynamically based on terminal capabilities and environment flags. The helper function `_sym` evaluates whether `sys.stdout.isatty()` is true and ensures `NO_COLOR` is absent from environment variables before returning rich Unicode glyphs; otherwise, it falls back to plain ASCII text equivalents. ```python def _sym(fancy: str, plain: str) -> str: """Return *fancy* when stdout is a TTY with colour, else *plain*.""" if not sys.stdout.isatty() or os.environ.get("NO_COLOR") is not None: return plain return fancy ``` Sources: [cli/python/src/mem0_cli/branding.py:37-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/branding.py#L37-L41) Welcome banners, constructed using a multi-line ASCII logo and a Python SDK version subtitle fetched via `_get_version()`, are suppressed entirely when agent mode is active. ```python from mem0_cli.state import is_agent_mode if is_agent_mode(): return logo_text = Text(LOGO, style=f"bold {BRAND_COLOR}") tagline = Text(f" {TAGLINE}\n", style=f"{ACCENT_COLOR}") content = Text() content.append_text(logo_text) content.append_text(tagline) panel = Panel( content, border_style=BRAND_COLOR, padding=(0, 2), subtitle=f"[{DIM_COLOR}]Python SDK · v{_get_version()}[/]", subtitle_align="right", ) console.print(panel) ``` Sources: [cli/python/src/mem0_cli/branding.py:46-64](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/branding.py#L46-L64) > [!NOTE] > The `timed_status` context manager directs all spinner animations and execution timing messages to standard error (`_err = Console(stderr=True)`) rather than standard output. This guarantees that background activity indicators never contaminate machine-readable JSON or agent data streams. Sources: [cli/python/src/mem0_cli/branding.py:14-124](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/branding.py#L14-L124) ### Agent Envelope Sanitation Call Chain When formatting outputs for programmatic consumers or AI agent workflows, data structures pass through strict projection functions to filter out extraneous database fields. The entity listing workflow follows a precise execution chain. 1. `cmd_entities_list` invokes the backend to retrieve raw entity records and captures performance metrics via `_time.perf_counter()`. 2. `format_agent_envelope` wraps the resulting dataset inside a structured dictionary containing command metadata, counts, execution duration, and active scopes. 3. `sanitize_agent_data` inspects the command string (`entity list`) and iterates over records to project them. 4. `pick` extracts only the authorized keys (`type`, `count`, plus normalized `name` or `id`) for each item. ```python def cmd_entities_list(backend: Backend, entity_type: str, *, output: str) -> None: """List entities of a given type.""" from mem0_cli.state import is_agent_mode, set_current_command set_current_command("entity list") if is_agent_mode(): output = "agent" valid_types = {"users", "agents", "apps", "runs"} if entity_type not in valid_types: print_error( err_console, f"Invalid entity type: {entity_type}. Use: {', '.join(valid_types)}" ) raise typer.Exit(1) _start = _time.perf_counter() with timed_status(err_console, f"Fetching {entity_type}...") as _ts: try: results = backend.entities(entity_type) except Exception as e: print_error(err_console, str(e), hint="This feature may require the mem0 Platform.") raise typer.Exit(1) from None _elapsed = _time.perf_counter() - _start if output == "agent": format_agent_envelope( console, command="entity list", data=results, count=len(results), duration_ms=int(_elapsed * 1000), ) return … def format_agent_envelope( console: Console, *, command: str, data: Any, duration_ms: int | None = None, scope: dict | None = None, count: int | None = None, ) -> None: """Output structured JSON envelope for agent/programmatic use (--json/--agent mode).""" envelope: dict[str, Any] = { "status": "success", "command": command, } if duration_ms is not None: envelope["duration_ms"] = duration_ms if scope: filtered = {k: v for k, v in scope.items() if v} if filtered: envelope["scope"] = filtered if count is not None: envelope["count"] = count envelope["data"] = sanitize_agent_data(command, data) # Surface the unclaimed-Agent-Mode notice (if any) in the envelope so an # agent reading the JSON output sees it without inspecting HTTP headers. from mem0_cli.state import take_notice notice = take_notice() if notice: envelope["mem0_notice"] = notice console.print_json(json.dumps(envelope, default=str)) … def sanitize_agent_data(command: str, data: Any) -> Any: """Project API response data to minimal relevant fields for agent consumption.""" def pick(obj: dict, keys: list) -> dict: return {k: obj[k] for k in keys if k in obj} if data is None: return data if command == "add": items = data if isinstance(data, list) else [data] result = [] for item in items: ``` Sources: [cli/python/src/mem0_cli/commands/entities.py:27-58](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/entities.py#L27-L58), [cli/python/src/mem0_cli/output.py:245-301](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py#L245-L301), [cli/python/src/mem0_cli/output.py:329-361](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py#L329-L361) ```mermaid sequenceDiagram participant entities.py as entities.py participant output.py as output.py entities.py->>output.py: format_agent_envelope() output.py->>output.py: sanitize_agent_data() output.py->>output.py: pick() ``` Sources: [cli/python/src/mem0_cli/commands/entities.py:27-58](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/entities.py#L27-L58), [cli/python/src/mem0_cli/output.py:245-361](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py#L245-L361) > [!WARNING] > If an unclaimed Agent Mode account triggers a platform notice during execution, both `format_json_envelope` and `format_agent_envelope` retrieve it via `take_notice()` and inject it directly into the `mem0_notice` JSON key. Consuming agents must inspect this field to discover authentication states without parsing HTTP headers. Sources: [cli/python/src/mem0_cli/output.py:233-242](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py#L233-L242), [cli/python/src/mem0_cli/output.py:353-360](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py#L353-L360) ## Related - [Node.js CLI Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/node-js-cli-architecture) - [Agent Account Provisioning](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/agent-account-provisioning) --- ## Technical docs: GET List Entities URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/entities/list-entities ## Responses ## Try It --- ## Technical docs: Node.js CLI Architecture URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/node-js-cli-architecture
Relevant source files The following files were used as context for generating this wiki page: - [integrations/openclaw/cli/commands.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts) - [cli/node/src/commands/agent-mode.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-mode.ts) - [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts) - [cli/node/src/commands/init.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/init.ts) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/hermes-plugin-mem0/_backend.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py) - [cli/node/src/backend/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/index.ts) - [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts) - [cli/node/src/commands/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/memory.ts) - [cli/python/src/mem0_cli/app.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py) - [cli/node/src/commands/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/config.ts) - [cli/node/src/backend/base.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/base.ts) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [cli/node/src/state.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/state.ts) - [scripts/oss-to-platform-migrate.sh](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh) - [cli/node/tsconfig.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/tsconfig.json) - [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts) - [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts) - [cli/node/src/branding.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/branding.ts)
## Overview The Node.js CLI architecture provides the primary command-line interface for interacting with Mem0 memory layers, offering robust command routing, configuration persistence, and platform communication tailored for both human operators and autonomous agents. Built around a Commander-based command dispatcher, the CLI maps subcommands to core memory operations while managing option normalization and execution flow. Sources: [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L10-L26), [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L149-L173) State management and disk-backed configuration handle setting resolution and persistent storage across user sessions. Through an abstract backend layer and platform client implementation, the CLI connects seamlessly with SaaS endpoints, ensuring secure communication and robust error handling. Sources: [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L11-L18), [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts#L25-L39) ## Command Dispatch and Argument Parsing ### Entry Point and Program Initialization The CLI executable initializes as a standard Node.js script using the Commander library to configure the root program [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L1-L26). The root `Command` instance defines global application properties, sets up positional option parsing via `.enablePositionalOptions()`, and configures help formatting [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L149-L173). Positional option configuration ensures that flags supplied after a subcommand name correctly target that subcommand rather than being intercepted at the global level [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L154-L158). Global options registered on the root program include version inspection, output formatting overrides, and agent mode flags [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L159-L168). | Option Flag | Description | | :--- | :--- | | `--version` | Show CLI version and exit | | `--json` | Output results as JSON for programmatic use | | `--agent` | Output as JSON (alias for `--json`, or agent bootstrap on `init`) | | `--help` | Show help message and exit | Sources: [cli/node/src/index.ts:159-171](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L159-L171) > [!WARNING] > When using the `--agent` flag with the `init` command, it serves as the unattended Agent Mode bootstrap selector rather than an output format modifier, overriding normal interactive email workflows. Sources: [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L165-L168), [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L219-L223) ### Telemetry Hooks and State Management Execution flow across all subcommands is intercepted by a `preAction` hook attached to the root program [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L176-L204). This hook resolves the active subcommand path—combining parent and child names such as `event.list` or falling back to standalone commands—and stores it in the transient execution state [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L178-L187), [cli/node/src/state.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/state.ts#L20-L26). Stashing the active command name ensures that JSON error envelopes include accurate command attribution [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L184-L187). ```typescript program.hook("preAction", (_thisCommand, actionCommand) => { try { const commandName = actionCommand.name(); const parentName = actionCommand.parent?.name(); const fullCommand = parentName && parentName !== "mem0" ? `${parentName}.${commandName}` : commandName; // Stash the active command name in shared state so the JSON // error envelope (printError) can report which command failed // instead of an empty `"command": ""` field. setCurrentCommand(fullCommand); // init fires its own telemetry from runInit with full M1-M6 props // (mode/agent_caller/signup_source/claimed_agent_mode); skip the // auto-fire here so we don't double-count. if (fullCommand === "init") return; const isAgent = !!(program.opts().json || program.opts().agent); captureEvent( `cli.${fullCommand}`, { command: fullCommand, is_agent: isAgent, }, _validatedUserEmail, ); } catch { /* silently swallow */ } }); ``` Sources: [cli/node/src/index.ts:176-204](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L176-L204) > [!NOTE] > The `init` command skips automatic telemetry emission inside the pre-action hook because `runInit` dispatches its own telemetry payload containing comprehensive mode, caller, and signup attributes [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L189-L192). Sources: [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L189-L192) ### Option Normalization and Entity Resolution Subcommands process user input by validating authentication credentials and normalizing filtering options [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L34-L89). Helper routines like `checkAgentMode()` inspect root options to determine if output formatting should default to agent JSON structures [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L102-L107), [cli/node/src/state.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/state.ts#L12-L18). Furthermore, entity scoping relies on `resolveIds()`, which inspects explicit CLI options (`userId`, `agentId`, `appId`, `runId`). If any explicit identifier is supplied, default configuration values are omitted to prevent unwanted cross-filtering; otherwise, configured default identifiers are applied [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L110-L145). ```typescript * Resolve entity IDs: CLI flag > config default > undefined. * * If any explicit ID is provided, only use explicit IDs (don't mix * in defaults for other entity types which would over-filter). * If no explicit IDs, fall back to all configured defaults. */ function resolveIds( config: Mem0Config, opts: { userId?: string; agentId?: string; appId?: string; runId?: string; }, ): { userId?: string; agentId?: string; appId?: string; runId?: string } { const hasExplicit = !!( opts.userId || opts.agentId || opts.appId || opts.runId ); if (hasExplicit) { return { userId: opts.userId || undefined, agentId: opts.agentId || undefined, appId: opts.appId || undefined, runId: opts.runId || undefined, }; } return { userId: config.defaults.userId || undefined, agentId: config.defaults.agentId || undefined, appId: config.defaults.appId || undefined, runId: config.defaults.runId || undefined, }; } ``` Sources: [cli/node/src/index.ts:110-145](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts#L110-L145) ## Configuration Persistence and State Management ### Configuration Precedence and Storage Mechanics The configuration subsystem manages disk-backed settings stored in `~/.mem0/config.json` with strict file permissions (`0o700` for the parent directory and `0o600` for the configuration file) [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L15-L16), [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L85-L86), [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L163-L164). Values are resolved according to a defined precedence order, where highest priority settings override lower levels [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L4-L9). | Precedence Order | Source Layer | Description | | :--- | :--- | :--- | | 1 (Highest) | CLI Flags | Direct execution flags such as `--api-key` and `--base-url` [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L5) | | 2 | Environment Variables | Runtime overrides like `MEM0_API_KEY`, `MEM0_BASE_URL`, and scoping keys [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L6), [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L121-L130) | | 3 | Configuration File | Persistent storage parsed from JSON on disk [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L7), [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L93-L95) | | 4 (Lowest) | Defaults | Built-in fallback constants including `DEFAULT_BASE_URL` (`https://api.mem0.ai`) and empty default strings [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L8), [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L17), [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L57-L83) | Sources: [cli/node/src/config.ts:4-132](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L4-L132) > [!WARNING] > When `saveConfig` persists settings to disk, it executes an idempotent ecosystem synchronization step (`syncApiKey`) to propagate the platform API key into agent integration environments and shell configuration files, wrapping failures in a try-catch block so that disk writes remain completely unblocked [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L166-L179). Sources: [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L166-L179) ### Key Mapping and Mutation Commands Configuration paths support dotted string lookups and short-form aliases via a static `KEY_MAP` structure [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L187-L204). Functions such as `getNestedValue` and `setNestedValue` translate these keys into internal typed properties across `platform`, `defaults`, `telemetry`, and `agentRush` sections [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L49-L55), [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L206-L231). The `cmdConfigSet` routine validates the requested key, mutates the active configuration object with type-aware casting for booleans and numbers, and invokes `saveConfig` alongside success or agent envelope reporting [cli/node/src/commands/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/config.ts#L92-L108), [cli/node/src/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L223-L230). ```typescript export function cmdConfigGet(key: string): void { setCurrentCommand("config get"); const config = loadConfig(); const value = getNestedValue(config, key); if (value === undefined) { printError(`Unknown config key: ${key}`); } else { // Redact secrets const displayValue = key.includes("api_key") || key.split(".").pop() === "key" ? redactKey(String(value)) : String(value); if (isAgentMode()) { formatAgentEnvelope({ command: "config get", data: { key, value: displayValue }, }); } else { console.log(displayValue); } } } export function cmdConfigSet(key: string, value: string): void { setCurrentCommand("config set"); const config = loadConfig(); if (setNestedValue(config, key, value)) { saveConfig(config); const display = key.includes("key") ? redactKey(value) : value; if (isAgentMode()) { formatAgentEnvelope({ command: "config set", data: { key, value: display }, }); } else { printSuccess(`${key} = ${display}`); } } else { printError(`Unknown config key: ${key}`); } } ``` Sources: [cli/node/src/commands/config.ts:68-109](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/config.ts#L68-L109), [cli/node/src/config.ts:187-232](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/config.ts#L187-L232) ### Transient Execution State In parallel with disk persistence, transient runtime state is maintained across command lifecycles [cli/node/src/state.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/state.ts#L1-L53). The state module tracks whether agent mode is active (`_agentMode`), records the currently executing command identifier (`_currentCommand`), and stashes backend notices such as unclaimed agent mode reminders via `captureNotice` and `takeNotice` [cli/node/src/state.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/state.ts#L8-L42). Furthermore, `stdinIsPiped` inspects standard input via `fs.fstatSync(0)` to detect active FIFO pipes or file redirections, automatically bypassing pipe checks whenever agent mode is enabled [cli/node/src/state.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/state.ts#L44-L53). ```typescript let _agentMode = false; let _currentCommand = ""; let _pendingNotice = ""; export function isAgentMode(): boolean { return _agentMode; } export function setAgentMode(val: boolean): void { _agentMode = val; } export function getCurrentCommand(): string { return _currentCommand; } export function setCurrentCommand(name: string): void { _currentCommand = name; } /** * Stash a Mem0 backend notice (Agent Mode unclaimed reminder) for end-of- * command surfacing. Called from the platform backend after each response so * the notice prints once per command regardless of how many sub-requests * fired. Last-write-wins is fine — the message text is identical. */ export function captureNotice(notice: string | null | undefined): void { if (notice) _pendingNotice = notice; } export function takeNotice(): string { const msg = _pendingNotice; _pendingNotice = ""; return msg; } ``` Sources: [cli/node/src/state.ts:8-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/state.ts#L8-L42) ## Backend Abstraction and Platform Client ### Pluggable Interface and Instantiation Factory The Node.js CLI defines memory backend interactions through the TypeScript `Backend` interface, establishing a standard contract for all CRUD operations, searches, entity management, and event listing [cli/node/src/backend/base.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/base.ts#L75-L116). The `getBackend` factory function accepts a global `Mem0Config` object and instantiates and returns the active SaaS platform client via `PlatformBackend` initialized with `config.platform` [cli/node/src/backend/base.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/base.ts#L141-L143), [cli/node/src/backend/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/index.ts#L5). | Error Class | Status Code / Condition | Default Error Message | | :--- | :--- | :--- | | `AuthError` | `401` Unauthorized | `Authentication failed. Your API key may be invalid or expired.` | | `NotFoundError` | `404` Not Found | `Resource not found: ${path}` | | `APIError` | `400` Bad Request | `Bad request to ${path}: ${detail}` | ```typescript export class AuthError extends Error { constructor( message = "Authentication failed. Your API key may be invalid or expired.", ) { super(message); this.name = "AuthError"; } } export class NotFoundError extends Error { constructor(path: string) { super(`Resource not found: ${path}`); this.name = "NotFoundError"; } } export class APIError extends Error { constructor(path: string, detail: string) { super(`Bad request to ${path}: ${detail}`); this.name = "APIError"; } } ``` Sources: [cli/node/src/backend/base.ts:118-139](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/base.ts#L118-L139) > [!NOTE] > The `PlatformBackend` class explicitly normalizes configured base URLs by stripping trailing slashes with `config.baseUrl.replace(/\/+$/, "")` to guarantee uniform path resolution when constructing REST requests [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts#L29-L30). Sources: [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts#L29-L30) ### SaaS REST API Communication The `PlatformBackend` class communicates directly with the Mem0 SaaS platform (`api.mem0.ai`) [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts#L2-L25). Every HTTP request builds standard diagnostic and authentication headers using the active API key and CLI version metadata [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts#L31-L39). ```typescript this.headers = { Authorization: `Token ${config.apiKey}`, "Content-Type": "application/json", "X-Mem0-Source": "CLI", "X-Mem0-Client": `mem0-cli-node/${CLI_VERSION}`, "X-Mem0-Client-Language": "node", "X-Mem0-Client-Version": CLI_VERSION, }; ``` Sources: [cli/node/src/backend/platform.ts:31-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts#L31-L39) The internal `_request` method handles execution parameters, query string serialization via `URLSearchParams`, and request timeouts configured at 30 seconds using `AbortSignal.timeout(30_000)` [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts#L41-L61). It dynamically injects `"X-Mem0-Caller-Type"` as `"agent"` or `"user"` depending on the active execution state checked by `isAgentMode()` [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts#L52-L55), [cli/node/src/state.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/state.ts#L8-L15). > [!WARNING] > When API responses include a `mem0_notice` payload property or an `X-Mem0-Notice-Message` header, the client automatically intercepts and stashes the message via `captureNotice` for end-of-command user surfacing, stripping the property from dictionaries and arrays so downstream consumers never encounter duplicate notices [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts#L102-L130). Sources: [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts#L102-L130) ## Memory Operations and Lifecycle Workflows ### Execution Flow and Validation Memory operations in the Node.js CLI are initiated via commands like `cmdSearch`, `cmdList`, `cmdGet`, and `cmdUpdate` which invoke the corresponding methods on the active `Backend` implementation [cli/node/src/commands/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/memory.ts#L231-L508). Before dispatching requests, handlers enforce validation rules on inputs and CLI options, writing errors to stderr and terminating execution with status code `1` if constraints fail. | Command Option / Parameter | Validation Rule | Error Message / Action | | :--- | :--- | :--- | | `query` (`cmdSearch`) | Must be truthy | `No query provided. Pass a query argument or pipe via stdin.` (Exit `1`) | | `filterJson` (`cmdSearch`) | Must be valid JSON | `Invalid JSON in --filter.` (Exit `1`) | | `topK` (`cmdSearch`) | Must be `>= 1` | `--top-k must be >= 1.` (Exit `1`) | | `threshold` (`cmdSearch`) | Must be between `0.0` and `1.0` | `--threshold must be between 0.0 and 1.0.` (Exit `1`) | | `pageSize` / `page` (`cmdList`) | Must be `>= 1` | `--page-size must be >= 1.` or `--page must be >= 1.` (Exit `1`) | | `metadata` (`cmdUpdate`) | Must be valid JSON | `Invalid JSON in --metadata.` (Exit `1`) | ```typescript setCurrentCommand("search"); if (!query) { printError("No query provided. Pass a query argument or pipe via stdin."); process.exit(1); } let filters: Record | undefined; if (opts.filterJson) { try { filters = JSON.parse(opts.filterJson); } catch { printError("Invalid JSON in --filter."); process.exit(1); } } const fieldList = opts.fields ? opts.fields.split(",").map((f) => f.trim()) : undefined; if (opts.topK < 1) { printError("--top-k must be >= 1."); process.exit(1); } if (opts.threshold < 0 || opts.threshold > 1) { printError("--threshold must be between 0.0 and 1.0."); process.exit(1); } ``` Sources: [cli/node/src/commands/memory.ts:251-278](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/memory.ts#L251-L278) > [!CAUTION] > Both search and update workflows parse stringified JSON flags (`--filter` and `--metadata`) using raw `JSON.parse` blocks wrapped in `try/catch` statements [cli/node/src/commands/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/memory.ts#L258-L265), [cli/node/src/commands/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/memory.ts#L491-L498). Malformed payload strings immediately trigger `process.exit(1)` after printing an error, bypassing any partial batch operations. Sources: [cli/node/src/commands/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/memory.ts#L258-L265), [cli/node/src/commands/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/memory.ts#L491-L498) ### Lifecycle Handlers and Asynchronous Timing Execution progress for remote CRUD and search requests is measured via `performance.now()` coupled with the `timedStatus` asynchronous status wrapper [cli/node/src/commands/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/memory.ts#L280-L305). The `Backend` interface defines methods for these lifecycle actions, accepting dedicated options interfaces for filtering, pagination, and structural modifications [cli/node/src/backend/base.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/base.ts#L75-L116). ```typescript export interface Backend { add( content?: string, messages?: Record[], opts?: AddOptions, ): Promise>; search( query: string, opts?: SearchOptions, ): Promise[]>; get(memoryId: string): Promise>; listMemories(opts?: ListOptions): Promise[]>; update( memoryId: string, content?: string, metadata?: Record, opts?: UpdateOptions, ): Promise>; delete( memoryId?: string, opts?: DeleteOptions, ): Promise>; } ``` Sources: [cli/node/src/backend/base.ts:76-101](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/base.ts#L76-L101) > [!NOTE] > When updating a memory entity via `cmdUpdate`, expiration parameters are validated against date formatting rules via `_validateExpires(opts.expires)` before `backend.update` is dispatched with optional content strings, metadata objects, timestamps, and expiration dates [cli/node/src/commands/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/memory.ts#L500-L508). Sources: [cli/node/src/commands/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/memory.ts#L500-L508) ## Output Formatting and Terminal Presentation ### Terminal Rendering and Multi-Mode Presentations The output subsystem supports multiple display modalities tailored for interactive terminal sessions, structured data extraction, and autonomous agent pipelines. It implements specialized formatters for textual lists, tabular displays via `cli-table3`, isolated modal frames using `boxen`, raw JSON serialization, and structured JSON envelopes. Symbol selection adapts dynamically between rich Unicode characters and plain text based on standard TTY availability and the presence of the `NO_COLOR` environment variable [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts#L5-L107), [cli/node/src/branding.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/branding.ts#L40-L43). | Formatter Function | Target Output Mode | Primary Dependencies & Features | | :--- | :--- | :--- | | `formatMemoriesText` | Human-readable text stream | Iterates record collections, formats ISO timestamps via `formatDate`, and appends metadata strings separated by centered dots. Sources: [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts#L22-L54) | | `formatMemoriesTable` | Fixed-width console table | Uses `cli-table3` with word-wrapping enabled, dynamic headers supporting optional score columns, and truncated content strings. Sources: [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts#L56-L103) | | `formatSingleMemory` | Single-record boxed view | Renders content inside a magenta-bordered box via `boxen` with explicit padding and property labels. Sources: [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts#L109-L151) | | `formatAddResult` | CRUD execution feedback | Maps status events (`ADD`, `UPDATE`, `DELETE`, `NOOP`, `PENDING`) to colored status icons and padded labels. Sources: [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts#L153-L228) | | `formatJsonEnvelope` | Standard JSON response wrapper | Packages data payloads alongside execution metadata, execution duration, scope filters, and account notices. Sources: [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts#L230-L258) | ```typescript export function formatMemoriesTable( memories: Record[], opts: { showScore?: boolean } = {}, ): void { const head = opts.showScore ? [ accent("ID"), accent("Score"), accent("Memory"), accent("Category"), accent("Created"), ] : [accent("ID"), accent("Memory"), accent("Category"), accent("Created")]; const colWidths = opts.showScore ? [38, 8, 40, 16, 14] : [38, 40, 16, 14]; const table = new Table({ head, colWidths, wordWrap: true, style: { head: [], border: [] }, }); ``` Sources: [cli/node/src/output.ts:56-75](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts#L56-L75) > [!NOTE] > When rendering add results containing asynchronous `PENDING` event statuses, `formatAddResult` deduplicates incoming entries sharing the exact `event_id` to prevent repetitive queue notifications in batch ingestion workflows. Sources: [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts#L173-L181) ### Branding Elements and Agent Data Sanitization Branding components provide visual consistency across terminal banners, status messages, and error hints. The ASCII banner renderer computes horizontal padding dynamically against the longest content line and appends a branded footer displaying the current SDK version [cli/node/src/branding.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/branding.ts#L45-L77). For programmatic consumption, `sanitizeAgentData` strips extraneous properties from backend response objects based on command names before wrapping them in `formatAgentEnvelope` [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts#L271-L390). ```typescript switch (command) { case "add": { const items = Array.isArray(data) ? data : [data]; return items.map((item) => { const r = item as Record; if (r.status === "PENDING") return pick(r, ["status", "event_id"]); return pick(r, ["id", "memory", "event"]); }); } case "search": return (data as Record[]).map((r) => pick(r, [ "id", "memory", "score", "created_at", "categories", "expiration_date", ]), ); case "list": return (data as Record[]).map((r) => pick(r, [ "id", "memory", "created_at", "categories", "expiration_date", ]), ); case "get": { const r = data as Record; return pick(r, [ "id", "memory", "created_at", "updated_at", "categories", "metadata", "expiration_date", ]); } case "update": { const r = data as Record; return pick(r, ["id", "memory", "expiration_date"]); } case "delete": case "delete-all": case "entity delete": return data; ``` Sources: [cli/node/src/output.ts:274-323](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts#L274-L323) > [!WARNING] > Both `formatJsonEnvelope` and `formatAgentEnvelope` automatically inspect transient state notices via `takeNotice()` and inject a `mem0_notice` property into the JSON output when operating on unclaimed Agent Mode accounts, allowing consuming agents to detect provisioning requirements without checking HTTP headers. Sources: [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts#L249-L255), [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts#L384-L388) ## Related - [Python CLI Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/python-cli-architecture) - [Agent Account Provisioning](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/agent-account-provisioning) --- ## Technical docs: DELETE Delete Entity URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/entities/delete-entity ## Parameters ## Responses ## Try It --- ## Technical docs: Agent Account Provisioning URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/agent-account-provisioning
Relevant source files The following files were used as context for generating this wiki page: - [cli/python/src/mem0_cli/commands/init_cmd.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/init_cmd.py) - [cli/node/src/commands/init.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/init.ts) - [integrations/openclaw/cli/commands.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts) - [cli/node/src/commands/agent-mode.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-mode.ts) - [cli/python/src/mem0_cli/commands/agent_mode_cmd.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_mode_cmd.py) - [integrations/mem0-agent-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py) - [cli/python/src/mem0_cli/app.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/app.py) - [cli/python/src/mem0_cli/commands/identify_cmd.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/identify_cmd.py) - [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts) - [cli/node/src/commands/identify.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/identify.ts) - [cli/python/src/mem0_cli/commands/whoami_cmd.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/whoami_cmd.py) - [integrations/mem0-agent-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/telemetry.py) - [integrations/agent-plugin-core/python/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/telemetry.py) - [cli/python/src/mem0_cli/commands/agent_rush_cmd.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_rush_cmd.py) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [cli/node/src/commands/agent-rush.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-rush.ts) - [cli/node/src/agent-detect.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/agent-detect.ts) - [cli/node/src/commands/whoami.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/whoami.ts) - [cli/python/src/mem0_cli/agent_detect.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/agent_detect.py) - [cli/node/src/state.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/state.ts) - [integrations/openclaw/isolation.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/isolation.ts) - [cli/python/src/mem0_cli/output.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py) - [cli/python/src/mem0_cli/state.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/state.py)
## Overview Agent account provisioning provides a zero-friction mechanism for autonomous AI agents to bootstrap platform credentials, establish ownership identities, and interact with remote memory services without manual browser-based authentication. By evaluating environment heuristics and state flags, the command-line interface automatically detects autonomous execution contexts and initiates backend provisioning to mint shadow API keys and slug-derived user identifiers. This workflow addresses the friction of programmatic onboarding, enabling unattended initialization while maintaining secure pathways for human operators to claim and upgrade provisioned accounts to permanent email-bound identities via one-time passwords. Once configured, agents can self-identify, manage metadata schemas, and execute specialized remote workloads such as `[[AGENTRUSH]]` operations under fully provisioned credentials. Sources: [cli/python/src/mem0_cli/commands/init_cmd.py:214-239](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/init_cmd.py#L214-L239), [cli/node/src/commands/agent-mode.ts:38-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-mode.ts#L38-L55), [cli/python/src/mem0_cli/commands/agent_mode_cmd.py:52-70](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_mode_cmd.py#L52-L70), [cli/python/src/mem0_cli/agent_detect.py:1-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/agent_detect.py#L1-L13) ## Agent Context Detection ### Environment Heuristics and Curated Runtimes The command-line interface determines whether it is running within an autonomous agent context by inspecting environment heuristics, state flags, and specific runtime indicators. During initialization via `mem0 init`, the detection logic checks if an explicit agent flag is active, evaluates global agent mode state flags, or queries runtime environment variables for recognized AI agent frameworks. The detection module scans the environment for curated environment variables mapped to known agent runtimes. This check serves as a context trigger only; it enables zero-friction auto-bootstrap UX but does not automatically populate canonical agent identity fields on platform records. | Agent Runtime | Associated Environment Variables | | :--- | :--- | | `claude-code` | `CLAUDECODE`, `CLAUDE_CODE` | | `cursor` | `CURSOR_AGENT`, `CURSOR_SESSION_ID` | | `codex` | `CODEX_CLI`, `OPENAI_CODEX` | | `cline` | `CLINE_AGENT`, `CLINE` | | `continue` | `CONTINUE_AGENT`, `CONTINUE_SESSION` | | `aider` | `AIDER_SESSION` | | `goose` | `GOOSE_AGENT` | | `windsurf` | `WINDSURF_AGENT` | Sources: [cli/node/src/agent-detect.ts:14-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/agent-detect.ts#L14-L31), [cli/python/src/mem0_cli/agent_detect.py:19-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/agent_detect.py#L19-L36) > [!NOTE] > Environment variable detection returns a canonical short name or `null`. Unrecognized environment variables fall through to `null`, treating the caller as a non-agent unless overridden by explicit CLI flags. Sources: [cli/python/src/mem0_cli/agent_detect.py:9-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/agent_detect.py#L9-L12) ### State Flags and Resolution Rules The composite agent context check (`_agent_ctx`) combines three distinct Boolean conditions evaluated sequentially during initialization: ```python _agent_ctx = agent or _global_agent_mode() or (detect_agent_caller() is not None) ``` Sources: [cli/python/src/mem0_cli/commands/init_cmd.py:266-266](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/init_cmd.py#L266-L266) State management utilities track whether the root option handler has enabled agent mode globally, while individual commands access or mutate this state. > [!WARNING] > When running in agent mode, standard pipe detection via `stdinIsPiped()` forces a return value of `false` to prevent interactive stream blocking. Sources: [cli/node/src/state.ts:44-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/state.ts#L44-L46) ## Autonomous Backend Bootstrapping ### Request Dispatch and Platform Envelopes When autonomous agent context detection is triggered during initialization, the command-line interface executes a unattended HTTP POST request to the remote platform endpoint `/api/v1/auth/agent_mode/`. Both the Python and Node.js client implementations transmit custom client language and source headers alongside optional payload fields such as the analytics source tag and the self-declared `agent_caller` identity. ```typescript const SOURCE_HEADERS = { "X-Mem0-Source": "cli", "X-Mem0-Client-Language": "node", } as const; export interface BootstrapEnvelope { api_key: string; default_user_id: string; org_id: string; project_id: string; mcp_url?: string; smoke_test_url?: string; claim_command?: string; mem0_notice?: string; } ``` Sources: [cli/node/src/commands/agent-mode.ts:11-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-mode.ts#L11-L25), [cli/python/src/mem0_cli/commands/agent_mode_cmd.py:26-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_mode_cmd.py#L26-L29) The backend responds with a JSON bootstrap envelope containing the provisioned API credentials, organizational scopes, and default user identifiers. The client validation logic enforces strict type checks to ensure that required fields such as `api_key` and `default_user_id` are populated strings before persisting the configuration locally. | Envelope Field | Type | Description | | :--- | :--- | :--- | | `api_key` | `string` | Provisioned shadow API authentication token | | `default_user_id` | `string` | Slug-derived user identifier for memory scoping | | `org_id` | `string` | Associated platform organization identifier | | `project_id` | `string` | Associated platform project identifier | | `mcp_url` | `string` (optional) | Remote Model Context Protocol endpoint | | `smoke_test_url` | `string` (optional) | Automated verification endpoint | | `claim_command` | `string` (optional) | Terminal command to bind account to an email | | `mem0_notice` | `string` (optional) | Unclaimed account reminder message | Sources: [cli/node/src/commands/agent-mode.ts:16-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-mode.ts#L16-L25), [cli/python/src/mem0_cli/commands/agent_mode_cmd.py:32-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_mode_cmd.py#L32-L50) > [!CAUTION] > If the backend response omits required fields or returns a null API key, the client aborts immediately with a non-zero exit code to prevent persisting malformed or undefined credentials. Sources: [cli/node/src/commands/agent-mode.ts:116-124](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-mode.ts#L116-L124), [cli/python/src/mem0_cli/commands/agent_mode_cmd.py:32-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_mode_cmd.py#L32-L50) ### Error Handling and Rate Limiting The bootstrap workflow intercepts specific HTTP status codes to handle network interruptions, service availability, and rate-limiting constraints without leaking raw backend tracebacks. | Status Code | Trigger Condition | Handled Message / Action | | :--- | :--- | :--- | | `429` | Request frequency limit exceeded | Rate-limited message; exits with status `1` | | `503` | Platform maintenance or feature toggle | Temporary disablement notice; exits with status `1` | | `403` | Django REST Framework `PermissionDenied` | Daily signup limit reached (5/day per IP); exits with status `1` | | Other `!ok` | General API error or invalid payload | Extracts `error` or `detail` field from JSON body; exits with status `1` | Sources: [cli/node/src/commands/agent-mode.ts:82-113](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-mode.ts#L82-L113), [cli/python/src/mem0_cli/commands/agent_mode_cmd.py:89-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_mode_cmd.py#L89-L112) > [!WARNING] > The backend rate limiter raises a generic `PermissionDenied` exception translated by Django REST Framework into an HTTP 403 status code with an opaque message. The CLI inspects this response for permission strings and normalizes it into a daily network signup quota warning. Sources: [cli/node/src/commands/agent-mode.ts:101-110](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-mode.ts#L101-L110), [cli/python/src/mem0_cli/commands/agent_mode_cmd.py:102-110](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_mode_cmd.py#L102-L110) ## Interactive Claim and Upgrade Flow ### OTP Verification and Upgrade Sequence The claim workflow allows human operators to bind an agent-provisioned shadow account to a permanent email identity without requiring a browser session or polling loops. When an existing configuration contains an active agent mode API key and a user passes the `--email` flag to the initialization command, the CLI dispatches a device claim flow using a one-time passcode. ```python if email and CONFIG_FILE.exists(): existing = load_config() if existing.platform.agent_mode and existing.platform.api_key: email = email.strip().lower() _validate_email(email) print_info(console, f"Claiming Agent Mode account to {email}...") claim_via_otp(existing, email=email, code=code) _fire_init("email", claimed=True) return ``` Sources: [cli/python/src/mem0_cli/commands/init_cmd.py:252-260](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/init_cmd.py#L252-L260) The upgrade process executes in two explicit phases across both the Python and Node.js implementations: 1. **Passcode Generation Request**: Unless a verification code is provided directly via the `--code` flag, the CLI transmits a `POST` request to `/api/v1/auth/email_code/` containing the target email address. If the terminal is running in an interactive TTY session, it prompts the operator for the received code; non-interactive environments without a supplied code terminate immediately with a usage hint. 2. **Atomic Verification and Claim**: The client sends a `POST` request to `/api/v1/auth/email_code/verify/` carrying the email, the trimmed verification code, and the raw agent mode API key under the parameter `agent_mode_api_key`. The backend processes this inline to execute an upgrade-in-place transaction. ```typescript // Step 1: request OTP (unless --code was supplied) if (!code) { const sendResp = await fetch(`${baseUrl}/api/v1/auth/email_code/`, { method: "POST", headers: { ...SOURCE_HEADERS, "Content-Type": "application/json" }, body: JSON.stringify({ email }), signal: AbortSignal.timeout(30_000), }); if (sendResp.status === 429) { printError("Too many attempts. Try again in a few minutes."); process.exit(1); } if (!sendResp.ok) { let detail: string = sendResp.statusText; try { const errBody = (await sendResp.json()) as { error?: string }; if (errBody.error) detail = errBody.error; } catch { /* leave as statusText */ } printError(`Failed to send code: ${detail}`); process.exit(1); } printSuccess(`Verification code sent to ${email}. Check your inbox.`); if (!process.stdin.isTTY) { printError( "No --code provided and terminal is non-interactive.", `Re-run: mem0 init --email ${email} --code `, ); process.exit(1); } console.log(); code = await promptLine(` ${brand("Verification Code")}`); if (!code) { printError("Code is required."); process.exit(1); } } // Step 2: verify + claim atomically const verifyResp = await fetch(`${baseUrl}/api/v1/auth/email_code/verify/`, { method: "POST", headers: { ...SOURCE_HEADERS, "Content-Type": "application/json" }, body: JSON.stringify({ email, code: code.trim(), agent_mode_api_key: rawKey, }), ``` Sources: [cli/node/src/commands/agent-mode.ts:180-230](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-mode.ts#L180-L230), [cli/python/src/mem0_cli/commands/agent_mode_cmd.py:165-208](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_mode_cmd.py#L165-L208) > [!NOTE] > During the upgrade-in-place transaction, the underlying shadow API key value remains completely unchanged. The backend updates account ownership associations while preserving existing authentication tokens. Sources: [cli/python/src/mem0_cli/commands/agent_mode_cmd.py:143-153](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_mode_cmd.py#L143-L153) ### State Mutations and Error Handling Upon receiving a successful verification response confirming `claimed: true`, the local configuration file is mutated to reflect the permanent identity state. The `platform.agent_mode` flag is disabled, `platform.claimed_at` is stamped with the verification timestamp (or current UTC time), `platform.user_email` is populated with the verified address, and `platform.created_via` switches from `agent_mode` to `email`. ```python claim_body = verify.json() if not claim_body.get("claimed"): print_error(err_console, f"Unexpected verify response: {claim_body}") raise typer.Exit(1) config.platform.agent_mode = False config.platform.claimed_at = claim_body.get("claimed_at") or _utcnow_iso() config.platform.user_email = email config.platform.created_via = "email" save_config(config) print_success(console, f"Agent claimed to {email}. Your API key is unchanged.") ``` Sources: [cli/python/src/mem0_cli/commands/agent_mode_cmd.py:225-235](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_mode_cmd.py#L225-L235) | Response Error Code | Trigger Condition | Handled Action / Guidance | | :--- | :--- | :--- | | `email_already_claimed` | The target email is already bound to another Mem0 account | Prints error details alongside a tip instructing the operator to sign in at `app.mem0.ai`; exits with status `1` | | Other `!ok` status | General validation failure, expired OTP, or network rejection | Extracts the error message or response text and terminates with status `1` | Sources: [cli/node/src/commands/agent-mode.ts:234-260](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-mode.ts#L234-L260), [cli/python/src/mem0_cli/commands/agent_mode_cmd.py:210-224](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_mode_cmd.py#L210-L224) > [!WARNING] > If a verification request fails because the email address is already registered to a separate account (`email_already_claimed`), the CLI prevents accidental credential overwrites and guides the user toward standard web authentication. Sources: [cli/node/src/commands/agent-mode.ts:250-265](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-mode.ts#L250-L265), [cli/python/src/mem0_cli/commands/agent_mode_cmd.py:219-223](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_mode_cmd.py#L219-L223) ## Agent Ownership Identification ### Identification Commands and Schema Mutations When an agent-mode initialization occurs without supplying an explicit `--agent-caller` argument, the platform backend initializes the row with `agent_caller=NULL`. To resolve this, active agents execute `mem0 identify ` (`run_identify` in Python and TypeScript implementations) to issue a schema mutation against the platform backend. ```python Used when `mem0 init --agent` ran without --agent-caller, so the backend saved agent_caller=NULL. The agent re-runs `mem0 identify ` to PATCH its own row with its real identity. Idempotent — running it again just overwrites. """ ``` Sources: [cli/python/src/mem0_cli/commands/identify_cmd.py:3-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/identify_cmd.py#L3-L7) The CLI validates that an active API key exists and that `agent_mode` is enabled in the local configuration before transmitting payloads. Requests target the endpoint `/api/v1/auth/agent_mode/caller/` via HTTP `PATCH` with headers specifying `X-Mem0-Source: cli`, `X-Mem0-Client-Language` (`python` or `node`), and a bearer token authentication header. ```python _SOURCE_HEADERS = { "X-Mem0-Source": "cli", "X-Mem0-Client-Language": "python", } def run_identify(name: str) -> None: """PATCH the active agent-mode key's agent_caller field.""" config = load_config() if not config.platform.api_key: print_error( err_console, "No API key configured. Run `mem0 init --agent` first.", ) raise typer.Exit(1) if not config.platform.agent_mode: print_error( err_console, "This command only works on unclaimed agent-mode keys.", ) raise typer.Exit(1) name = (name or "").strip() if not name: print_error(err_console, "Agent name is required.") raise typer.Exit(1) base_url = (config.platform.base_url or "https://api.mem0.ai").rstrip("/") try: with httpx.Client(timeout=30.0) as client: resp = client.patch( f"{base_url}/api/v1/auth/agent_mode/caller/", headers={ **_SOURCE_HEADERS, "Authorization": f"Token {config.platform.api_key}", "Content-Type": "application/json", }, json={"agent_caller": name}, ) ``` Sources: [cli/python/src/mem0_cli/commands/identify_cmd.py:21-59](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/identify_cmd.py#L21-L59), [cli/node/src/commands/identify.ts:12-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/identify.ts#L12-L50) > [!NOTE] > The identify command is idempotent; running it multiple times overwrites the existing row value. If the response returns a canonicalized `agent_caller` field, the local configuration updates to match the server-side normalization. Sources: [cli/python/src/mem0_cli/commands/identify_cmd.py:5-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/identify_cmd.py#L5-L6), [cli/python/src/mem0_cli/commands/identify_cmd.py:72-74](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/identify_cmd.py#L72-L74) ### AGENTRUSH Identifier Resolution To inspect the provisioned agent's `default_user_id` without network traversal, agents execute `mem0 whoami` (`run_whoami` / `cmdWhoami`). This command reads directly from local configuration storage to output the assigned AGENTRUSH session identifier and directs operators to view their row at `https://mem0.ai/agentrush`. ```python """mem0 whoami — print the active agent's default_user_id (AGENTRUSH identifier).""" from __future__ import annotations import typer from rich.console import Console from mem0_cli.branding import BRAND_COLOR, print_error, print_info from mem0_cli.config import load_config console = Console() err_console = Console(stderr=True) def run_whoami() -> None: config = load_config() session_id = config.platform.default_user_id if config.platform else None if not session_id: print_error( err_console, "No default_user_id found. Run `mem0 init --agent` first.", ) raise typer.Exit(1) console.print(f"Your AGENTRUSH identifier: [{BRAND_COLOR}]{session_id}[/{BRAND_COLOR}]") print_info(console, "Find your row at https://mem0.ai/agentrush") ``` Sources: [cli/python/src/mem0_cli/commands/whoami_cmd.py:1-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/whoami_cmd.py#L1-L25), [cli/node/src/commands/whoami.ts:1-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/whoami.ts#L1-L18) > [!WARNING] > If `default_user_id` is absent from local settings, `mem0 whoami` halts execution and instructs the operator to execute `mem0 init --agent` first. Sources: [cli/python/src/mem0_cli/commands/whoami_cmd.py:18-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/whoami_cmd.py#L18-L23), [cli/node/src/commands/whoami.ts:11-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/whoami.ts#L11-L15) ## Provisioned Agent Runtime Capabilities ### AGENTRUSH Execution and API Routing Provisioned agents interact with the platform using dedicated game commands wrapped by `mem0 agent-rush add` and `mem0 agent-rush search`. These commands issue `POST` requests to hardcoded endpoints `/v1/agent-rush/memories/` and `/v1/agent-rush/memories/search/` using base URLs resolved from local configuration files. ```python """mem0 agent-rush — AGENTRUSH game commands. Wraps the platform's /v1/agent-rush/{memories/, memories/search/} endpoints. Hardcoded routing; no flags needed. """ ``` Sources: [cli/python/src/mem0_cli/commands/agent_rush_cmd.py:1-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_rush_cmd.py#L1-L5), [cli/node/src/commands/agent-rush.ts:1-4](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-rush.ts#L1-L4) All runtime requests carry specialized source headers including `X-Mem0-Source: cli`, `X-Mem0-Client-Language` (`python` or `node`), `X-Mem0-Mode: agent-rush`, and an authorization header containing the agent's API token. ```python _SOURCE_HEADERS = { "X-Mem0-Source": "cli", "X-Mem0-Client-Language": "python", "X-Mem0-Mode": "agent-rush", } ``` Sources: [cli/python/src/mem0_cli/commands/agent_rush_cmd.py:29-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_rush_cmd.py#L29-L33), [cli/node/src/commands/agent-rush.ts:50-57](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-rush.ts#L50-L57) ### Verification and Error Handling When errors occur during runtime execution, the CLI parses response codes from platform payloads and maps them to descriptive user hints. | Error Code | Descriptive Hint Text | | :--- | :--- | | `agentrush_search_first` | Run 3 'mem0 agent-rush search' commands before adding. | | `agentrush_search_quota` | You've used your 3 lifetime searches. | | `agentrush_add_quota` | You've used your 3 lifetime adds. | | `agentrush_not_agent_mode` | Re-run 'mem0 init --agent' to bootstrap an agent-mode key. | | `agentrush_length` | Memory text must be 50-1000 characters. | | `agentrush_no_urls` | URLs are not allowed. | | `agentrush_blocklist` | Content contains a blocked term. | | `agentrush_global_quota` | Event-wide cap reached. Try again later. | | `agentrush_not_provisioned` | AGENTRUSH is not provisioned in this environment. | ```python _ERROR_HINTS = { "agentrush_search_first": "Run 3 'mem0 agent-rush search' commands before adding.", "agentrush_search_quota": "You've used your 3 lifetime searches.", "agentrush_add_quota": "You've used your 3 lifetime adds.", "agentrush_not_agent_mode": "Re-run 'mem0 init --agent' to bootstrap an agent-mode key.", "agentrush_length": "Memory text must be 50-1000 characters.", "agentrush_no_urls": "URLs are not allowed.", "agentrush_blocklist": "Content contains a blocked term.", "agentrush_global_quota": "Event-wide cap reached. Try again later.", "agentrush_not_provisioned": "AGENTRUSH is not provisioned in this environment.", } ``` Sources: [cli/python/src/mem0_cli/commands/agent_rush_cmd.py:35-45](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_rush_cmd.py#L35-L45), [cli/node/src/commands/agent-rush.ts:18-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-rush.ts#L18-L31) > [!WARNING] > AGENTRUSH memories are fully public and visible to other participants. In interactive TTY environments, submission blocks until the operator explicitly acknowledges the PII warning and persists the timestamp under `acknowledged_at`. In non-interactive agent runs lacking a TTY, the CLI prints the warning to standard error and proceeds without blocking. Sources: [cli/python/src/mem0_cli/commands/agent_rush_cmd.py:86-115](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_rush_cmd.py#L86-L115), [cli/node/src/commands/agent-rush.ts:100-119](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-rush.ts#L100-L119) ## Related - [Python CLI Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/python-cli-architecture) - [Node.js CLI Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/node-js-cli-architecture) --- ## Technical docs: GET List Requests URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/api/requests/list-requests ## Parameters ## Responses ## Try It --- ## Technical docs: Plugin Synchronization URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/plugin-synchronization
Relevant source files The following files were used as context for generating this wiki page: - [integrations/openclaw/cli/commands.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts) - [integrations/agent-plugin-core/python/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py) - [integrations/antigravity-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/hook_runner.py) - [integrations/agent-plugin-core/conformance/run.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py) - [integrations/kimi-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/hook_runner.py) - [integrations/claude-code-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py) - [integrations/codex-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/hook_runner.py) - [integrations/cursor-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py) - [integrations/mem0-agent-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py) - [integrations/agent-plugin-core/python/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py) - [integrations/claude-code-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_cli.py) - [integrations/codex-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_cli.py) - [integrations/antigravity-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_cli.py) - [cli/python/src/mem0_cli/plugin_sync.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py) - [integrations/kimi-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_cli.py) - [integrations/cursor-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_cli.py) - [cli/node/src/plugin-sync.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts) - [integrations/pi-agent-plugin/src/entry.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/entry.ts) - [integrations/codex-plugin/.mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/.mcp.json) - [integrations/claude-code-plugin/.mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/.mcp.json) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [integrations/mem0-agent-plugin/mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/mcp.json) - [integrations/hermes-plugin-mem0/_setup.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py) - [integrations/pi-agent-plugin/src/memory/tools.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts) - [integrations/antigravity-plugin/mcp_config.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/mcp_config.json) - [integrations/mem0-agent-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py) - [integrations/antigravity-plugin/hooks.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks.json) - [integrations/antigravity-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py) - [integrations/codex-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py) - [integrations/agent-plugin-core/python/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py)
## Overview Plugin synchronization bridges canonical CLI authentication state with external ecosystem touchpoints [cli/python/src/mem0_cli/plugin_sync.py:1-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L1-L25), [cli/node/src/plugin-sync.ts:1-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L1-L22). By propagating active Mem0 API keys into runtime configuration files and shell environment export declarations, the synchronization subsystem prevents authentication mismatches during agent-mode bootstraps [cli/python/src/mem0_cli/plugin_sync.py:1-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L1-L15), [cli/node/src/plugin-sync.ts:1-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L1-L15). Sources: [cli/python/src/mem0_cli/plugin_sync.py:1-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L1-L25), [cli/node/src/plugin-sync.ts:1-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L1-L22) The architecture enforces strict update constraints by modifying existing target entries without creating unrequested files, preserving document formatting and surrounding keys, and performing atomic file writes via temporary staging paths and renames [cli/python/src/mem0_cli/plugin_sync.py:10-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L10-L15), [cli/node/src/plugin-sync.ts:10-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L10-L15). It targets touchpoints such as Claude Code settings and shell resource configuration profiles [cli/python/src/mem0_cli/plugin_sync.py:17-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L17-L20), [cli/node/src/plugin-sync.ts:16-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L16-L19). Sources: [cli/python/src/mem0_cli/plugin_sync.py:10-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L10-L20), [cli/node/src/plugin-sync.ts:10-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L10-L19) ## CLI Synchronization Mechanism ### Propagation Targets and Rules The Python and Node.js command-line interfaces implement identical credential synchronization logic to propagate active Mem0 API keys into external development environments [cli/python/src/mem0_cli/plugin_sync.py:41-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L41-L55), [cli/node/src/plugin-sync.ts:40-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L40-L50). When `sync_api_key` or `syncApiKey` executes with a valid string, it targets the Claude Code configuration file and three standard POSIX shell resource profiles [cli/python/src/mem0_cli/plugin_sync.py:37-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L37-L38), [cli/node/src/plugin-sync.ts:28-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L28-L33). ```python _CLAUDE_SETTINGS = Path.home() / ".claude" / "settings.json" _SHELL_RCS = [Path.home() / ".zshrc", Path.home() / ".bashrc", Path.home() / ".bash_profile"] ``` Sources: [cli/python/src/mem0_cli/plugin_sync.py:37-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L37-L39) ```typescript const CLAUDE_SETTINGS = path.join(os.homedir(), ".claude", "settings.json"); const SHELL_RCS = [ path.join(os.homedir(), ".zshrc"), path.join(os.homedir(), ".bashrc"), path.join(os.homedir(), ".bash_profile"), ]; ``` Sources: [cli/node/src/plugin-sync.ts:28-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L28-L33) | Target Path | Config Key / Pattern | Scope & Behavior | | --- | --- | --- | | `~/.claude/settings.json` | `env.MEM0_API_KEY` | Updated only if parent `env` object and key already exist [cli/python/src/mem0_cli/plugin_sync.py:67-70](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L67-L70), [cli/node/src/plugin-sync.ts:66-69](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L66-L69) | | `~/.zshrc` | `export MEM0_API_KEY="..."` | Replaces existing assignment value using regular expression matching [cli/python/src/mem0_cli/plugin_sync.py:81-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L81-L84), [cli/node/src/plugin-sync.ts:37-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L37-L38) | | `~/.bashrc` | `export MEM0_API_KEY="..."` | Replaces existing assignment value using regular expression matching [cli/python/src/mem0_cli/plugin_sync.py:81-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L81-L84), [cli/node/src/plugin-sync.ts:37-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L37-L38) | | `~/.bash_profile` | `export MEM0_API_KEY="..."` | Replaces existing assignment value using regular expression matching [cli/python/src/mem0_cli/plugin_sync.py:81-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L81-L84), [cli/node/src/plugin-sync.ts:37-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L37-L38) | Sources: [cli/python/src/mem0_cli/plugin_sync.py:37-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L37-L84), [cli/node/src/plugin-sync.ts:28-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L28-L38) > [!IMPORTANT] > The synchronization engine strictly enforces a non-creation policy for target keys. If `MEM0_API_KEY` is missing from `settings.json` or no `export MEM0_API_KEY` declaration exists in a shell resource file, the synchronizer skips that target without writing new keys [cli/python/src/mem0_cli/plugin_sync.py:68-70](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L68-L70), [cli/node/src/plugin-sync.ts:67-69](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L67-L69), [cli/python/src/mem0_cli/plugin_sync.py:96-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L96-L97), [cli/node/src/plugin-sync.ts:87](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L87). Sources: [cli/python/src/mem0_cli/plugin_sync.py:68-70](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L68-L70), [cli/node/src/plugin-sync.ts:67-69](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L67-L69), [cli/python/src/mem0_cli/plugin_sync.py:96-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L96-L97), [cli/node/src/plugin-sync.ts:87](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L87) ### Parsing Patterns and Atomic Writes Shell RC file matching relies on an anchored regular expression pattern that captures export prefixes, optional surrounding quotes, credential values, and trailing spaces while sparing newline characters [cli/python/src/mem0_cli/plugin_sync.py:81-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L81-L84), [cli/node/src/plugin-sync.ts:37-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L37-L38). ```python _RC_LINE = re.compile( r'^([ \t]*export[ \t]+MEM0_API_KEY[ \t]*=[ \t]*)(["\']?)([^"\'\n]*)(["\']?)[ \t]*$', re.MULTILINE, ) ``` Sources: [cli/python/src/mem0_cli/plugin_sync.py:81-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L81-L84) ```typescript const RC_LINE_RE = /^([ \t]*export[ \t]+MEM0_API_KEY[ \t]*=[ \t]*)(["']?)([^"'\n]*)(["']?)[ \t]*$/m; ``` Sources: [cli/node/src/plugin-sync.ts:37-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L37-L38) > [!TIP] > Both implementations use explicit horizontal whitespace character classes `[ \t]*` rather than generic whitespace selectors `\s*` in regex definitions. This ensures that trailing newlines at the end of shell configuration files remain intact when `MEM0_API_KEY` resides on the final line [cli/python/src/mem0_cli/plugin_sync.py:79-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L79-L80), [cli/node/src/plugin-sync.ts:35-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L35-L36). Sources: [cli/python/src/mem0_cli/plugin_sync.py:79-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L79-L80), [cli/node/src/plugin-sync.ts:35-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L35-L36) File persistence utilizes an atomic write procedure that writes content to a temporary staging file in the target directory, copies POSIX file access permissions via bitwise masking against `0o777`, and replaces the destination file atomically via OS system calls [cli/python/src/mem0_cli/plugin_sync.py:105-118](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L105-L118), [cli/node/src/plugin-sync.ts:97-120](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L97-L120). ```python def _atomic_write_text(path: Path, content: str) -> None: """Write content to path atomically (temp + rename).""" dirname = path.parent fd, tmp_path = tempfile.mkstemp(prefix=f".{path.name}.", suffix=".tmp", dir=dirname) try: with os.fdopen(fd, "w", encoding="utf-8") as f: f.write(content) # Preserve mode if the original existed. if path.exists(): os.chmod(tmp_path, path.stat().st_mode & 0o777) os.replace(tmp_path, path) ``` Sources: [cli/python/src/mem0_cli/plugin_sync.py:105-115](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/plugin_sync.py#L105-L115) ```typescript function atomicWriteText(filePath: string, content: string): void { const dir = path.dirname(filePath); const tmp = path.join(dir, `.${path.basename(filePath)}.${process.pid}.tmp`); try { fs.writeFileSync(tmp, content, "utf-8"); // Preserve permissions if original existed. if (fs.existsSync(filePath)) { try { const mode = fs.statSync(filePath).mode & 0o777; fs.chmodSync(tmp, mode); } catch { /* best-effort */ } } fs.renameSync(tmp, filePath); } catch (err) { ``` Sources: [cli/node/src/plugin-sync.ts:97-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/plugin-sync.ts#L97-L112) ## IDE MCP Tool Manifests ### Manifest Structure Across Targets Standardized Model Context Protocol server declarations across IDE integration targets configure the execution parameters for the `mem0` memory service tool. Each manifest registers a server entry point executing via Python, pointing to the local `core/mcp_server.py` module relative to the plugin root. ```json { "mcpServers": { "mem0": { "command": "python3", "args": ["${CLAUDE_PLUGIN_ROOT}/core/mcp_server.py"], "env": { "MEM0_CODE_DATA_DIR": "${CLAUDE_PLUGIN_DATA}" } } } ``` Sources: [integrations/claude-code-plugin/.mcp.json:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/.mcp.json#L1-L10) ```json { "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", "mcpServers": { "mem0": { "type": "stdio", "command": "python3", "args": [ "${PLUGIN_ROOT}/core/mcp_server.py" ] } } ``` Sources: [integrations/mem0-agent-plugin/mcp.json:1-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/mcp.json#L1-L11) ```json { "mcpServers": { "mem0": { "command": "python3", "args": ["./core/mcp_server.py"] } } ``` Sources: [integrations/antigravity-plugin/mcp_config.json:1-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/mcp_config.json#L1-L7) Target configurations differ in their schema references, environment variable scoping, and path variables used for runtime resolution. | Target Plugin | Schema Reference | Command | Arguments | Environment Variables | | --- | --- | --- | --- | --- | | [Claude Code Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin) | None | `python3` | `["${CLAUDE_PLUGIN_ROOT}/core/mcp_server.py"]` | `MEM0_CODE_DATA_DIR: "${CLAUDE_PLUGIN_DATA}"` | | Mem0 Agent Plugin | `https://agent-plugins.org/schemas/1.0.0/mcp.schema.json` | `python3` | `["${PLUGIN_ROOT}/core/mcp_server.py"]` | None | | Antigravity Plugin | None | `python3` | `["./core/mcp_server.py"]` | None | Sources: [integrations/claude-code-plugin/.mcp.json:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/.mcp.json#L1-L10), [integrations/mem0-agent-plugin/mcp.json:1-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/mcp.json#L1-L11), [integrations/antigravity-plugin/mcp_config.json:1-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/mcp_config.json#L1-L7) > [!NOTE] > The `mem0-agent-plugin` explicitly declares a JSON schema validator URI `https://agent-plugins.org/schemas/1.0.0/mcp.schema.json` and a transport type property (`"type": "stdio"`), whereas Claude Code and Antigravity omit these explicit schema and transport declarations [integrations/mem0-agent-plugin/mcp.json:2-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/mcp.json#L2-L5), [integrations/claude-code-plugin/.mcp.json:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/.mcp.json#L1-L10), [integrations/antigravity-plugin/mcp_config.json:1-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/mcp_config.json#L1-L7). Sources: [integrations/mem0-agent-plugin/mcp.json:2-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/mcp.json#L2-L5), [integrations/claude-code-plugin/.mcp.json:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/.mcp.json#L1-L10), [integrations/antigravity-plugin/mcp_config.json:1-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/mcp_config.json#L1-L7) ## Standalone MCP Server Implementation ### JSON-RPC Server Loop and Request Dispatch The standalone Model Context Protocol server entry point is implemented in Python under `integrations/mem0-agent-plugin/core/mcp_server.py` and `integrations/antigravity-plugin/core/mcp_server.py`. It operates via standard input (`sys.stdin`) line reading, executing a JSON-RPC message processing loop that parses line-delimited payloads and flushes serialized responses to standard output (`sys.stdout`) with newline termination. ```python def main() -> int: for raw_line in sys.stdin: try: message = json.loads(raw_line) response = handle_request(message) except json.JSONDecodeError: response = { "jsonrpc": "2.0", "id": None, "error": {"code": -32700, "message": "Parse error"}, } except Exception: response = { "jsonrpc": "2.0", "id": None, "error": {"code": -32603, "message": "Internal error"}, } if response is not None: sys.stdout.write(json.dumps(response, separators=(",", ":")) + "\n") sys.stdout.flush() telemetry.spawn_flush() return 0 ``` Sources: [integrations/mem0-agent-plugin/core/mcp_server.py:215-238](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py#L215-L238), [integrations/antigravity-plugin/core/mcp_server.py:215-238](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py#L215-L238) The request router handles standard MCP JSON-RPC protocol methods. When an `initialize` request arrives, it echoes back the requested protocol version falling back to `2024-11-05`, declares tool capabilities, and returns server metadata comprising name `mem0` and the current `PLUGIN_VERSION`. ```python def handle_request(message: Any) -> dict[str, Any] | None: if not isinstance(message, dict): return None request_id = message.get("id") method = message.get("method") if method == "notifications/initialized": return None if method == "initialize": requested = (message.get("params") or {}).get("protocolVersion") return { "jsonrpc": "2.0", "id": request_id, "result": { "protocolVersion": requested or PROTOCOL_VERSION, "capabilities": {"tools": {"listChanged": False}}, "serverInfo": {"name": "mem0", "version": PLUGIN_VERSION}, }, } if method == "ping": return {"jsonrpc": "2.0", "id": request_id, "result": {}} ``` Sources: [integrations/mem0-agent-plugin/core/mcp_server.py:152-172](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py#L152-L172), [integrations/antigravity-plugin/core/mcp_server.py:152-172](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py#L152-L172) > [!WARNING] > Incoming JSON-RPC lines failing JSON deserialization return a `-32700` parse error, while unhandled internal exceptions return a `-32603` internal error code with `id: None`. Requests carrying unrecognized methods return a `-32601` method not found error [integrations/mem0-agent-plugin/core/mcp_server.py:206-232](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py#L206-L232), [integrations/antigravity-plugin/core/mcp_server.py:206-232](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py#L206-L232). Sources: [integrations/mem0-agent-plugin/core/mcp_server.py:206-232](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py#L206-L232), [integrations/antigravity-plugin/core/mcp_server.py:206-232](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py#L206-L232) ### Memory Retrieval Tool and Argument Validation The server exposes a single retrieval tool named `search_memories`. The tool definition includes input validation schemas, descriptive text, and execution annotations. ```python PROTOCOL_VERSION = "2024-11-05" TOOL_NAME = "search_memories" TOOL_DESCRIPTION = ( "Search memories from earlier work in this repository. Use it before " "repeating investigation or when earlier decisions, fixes, commands, or " "results may help." ) TOOL_SCHEMA = { "type": "object", "properties": { "query": { "type": "string", "minLength": 1, "maxLength": 2000, "description": "A direct question about earlier work in this repository.", }, "top_k": { "type": "integer", "minimum": 1, "maximum": 20, "description": "Maximum memories to return. Uses Mem0's configured default when omitted.", }, "category": { "type": "string", "enum": list(CODING_MEMORY_CATEGORY_NAMES), "description": "Optional memory category. Omit to search every category.", }, "scope": { "type": "string", "enum": list(SEARCH_SCOPES), "description": ( "Which memories to search. 'repo' (default) is the whole repository's " "shared memory plus your own preferences, 'dir' narrows the shared " "part to the current directory, 'mine' is your preferences alone." ), }, "run_id": { "type": "string", "minLength": 1, "description": ( "Optional coding-agent session ID. With any scope, restricts results to memories " "saved in that session. Omit to recall memories across sessions." ), }, }, "required": ["query"], "additionalProperties": False, } ``` Sources: [integrations/mem0-agent-plugin/core/mcp_server.py:21-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py#L21-L68), [integrations/antigravity-plugin/core/mcp_server.py:21-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py#L21-L68) | Property | Type | Constraints / Enum | Purpose | | --- | --- | --- | --- | | `query` | string | minLength: 1, maxLength: 2000, required | Direct question about earlier work in the repository | | `top_k` | integer | minimum: 1, maximum: 20, optional | Maximum number of memories to return | | `category` | string | `CODING_MEMORY_CATEGORY_NAMES`, optional | Optional memory category filter | | `scope` | string | `SEARCH_SCOPES`, optional | Search boundary (`repo`, `dir`, or `mine`) | | `run_id` | string | minLength: 1, optional | Restricts results to a specific coding-agent session ID | Sources: [integrations/mem0-agent-plugin/core/mcp_server.py:28-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py#L28-L68), [integrations/antigravity-plugin/core/mcp_server.py:28-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py#L28-L68) The `tools/list` method announces `search_memories` with annotations configured with `readOnlyHint: True`, `idempotentHint: True`, and `openWorldHint: True`. Argument validation enforces strict type boundaries via `_validate_arguments`, raising `ToolInputError` if unknown arguments are present, strings exceed character limits, or integer bounds are violated. ```python def _validate_arguments( arguments: Any, ) -> tuple[str, int | None, str | None, str | None, str | None]: if not isinstance(arguments, dict): raise ToolInputError("Search arguments must be an object.") unknown = set(arguments) - {"query", "top_k", "category", "scope", "run_id"} if unknown: raise ToolInputError(f"Unknown search argument: {sorted(unknown)[0]}") query = arguments.get("query") if not isinstance(query, str) or not query.strip(): raise ToolInputError("query must be a non-empty string.") query = query.strip() if len(query) > 2000: raise ToolInputError("query must be at most 2,000 characters.") top_k = arguments.get("top_k") if top_k is not None and ( isinstance(top_k, bool) or not isinstance(top_k, int) or not 1 <= top_k <= 20 ): raise ToolInputError("top_k must be an integer from 1 to 20.") category = arguments.get("category") if category is not None and category not in CODING_MEMORY_CATEGORY_NAMES: raise ToolInputError("category must be one of Mem0's supported categories.") scope = arguments.get("scope") if scope is not None and scope not in SEARCH_SCOPES: raise ToolInputError(f"scope must be one of {list(SEARCH_SCOPES)}.") run_id = arguments.get("run_id") if run_id is not None: if not isinstance(run_id, str) or not run_id.strip(): raise ToolInputError("run_id must be a non-empty string.") run_id = run_id.strip() return query, top_k, category, scope, run_id ``` Sources: [integrations/mem0-agent-plugin/core/mcp_server.py:75-113](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py#L75-L113), [integrations/antigravity-plugin/core/mcp_server.py:75-113](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py#L75-L113) ## IDE Hook Event Dispatching ### Hook Orchestration and Input Processing Host IDE hooks are orchestrated through shared runner scripts that intercept environment events and route them into Mem0 background pipelines. Hook input is read directly from standard input as a JSON dictionary, defaulting to an empty structure on parse or I/O errors. ```python def read_hook_input() -> dict: try: value = json.load(sys.stdin) return value if isinstance(value, dict) else {} except (json.JSONDecodeError, OSError): return {} ``` Sources: [integrations/antigravity-plugin/core/hook_runner.py:44-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/hook_runner.py#L44-L50), [integrations/kimi-plugin/core/hook_runner.py:44-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/hook_runner.py#L44-L50), [integrations/claude-code-plugin/core/hook_runner.py:44-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py#L44-L50), [integrations/codex-plugin/core/hook_runner.py:44-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/hook_runner.py#L44-L50), [integrations/cursor-plugin/core/hook_runner.py:44-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L44-L50) > [!NOTE] > Tool use response hooks evaluate tool exit codes and error flags to detect failed executions. If a tool response indicates a failure via `exit_code != 0`, `isError: true`, or `success: false`, the runner injects error telemetry into the payload before recording [integrations/codex-plugin/hooks/adapter.py:42-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L42-L55). Sources: [integrations/codex-plugin/hooks/adapter.py:42-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L42-L55) ### Configuration and Handoff Mechanics Plugin hooks configure their respective harness environments and execute asynchronous background flush workers to persist capture data without blocking the IDE event loop. ```python configure_harness("codex", data_dir_name="codex-plugin", source_tag="codex_plugin") telemetry.init(harness="codex", source_tag="CODEX_PLUGIN") ``` Sources: [integrations/codex-plugin/hooks/adapter.py:23-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L23-L24) | Constant Name | Value / Default | Meaning / Constraint | | --- | --- | --- | | `STALE_RUNNING_SECONDS` | `300` | Threshold in seconds before a running handoff process is considered stale and reset | | `PENDING_EXPIRY_SECONDS` | `604800` (7 days) | Maximum age for pending handoff files before deletion | | `PENDING_LAUNCH_LIMIT` | `5` | Maximum number of pending handoff processes launched concurrently | | `DEFAULT_IDLE_FLUSH_SECONDS` | `300` | Default idle duration before triggering a flush | Sources: [integrations/antigravity-plugin/core/hook_runner.py:36-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/hook_runner.py#L36-L39), [integrations/kimi-plugin/core/hook_runner.py:36-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/hook_runner.py#L36-L39), [integrations/claude-code-plugin/core/hook_runner.py:36-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py#L36-L39), [integrations/codex-plugin/core/hook_runner.py:36-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/hook_runner.py#L36-L39), [integrations/cursor-plugin/core/hook_runner.py:36-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L36-L39) > [!WARNING] > Handoff payloads are hashed using SHA-256 over the working directory, session ID, and reason, truncated to 24 hex characters, to prevent duplicate worker invocations for concurrent identical events [integrations/antigravity-plugin/core/hook_runner.py:170-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/hook_runner.py#L170-L191), [integrations/kimi-plugin/core/hook_runner.py:170-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/hook_runner.py#L170-L191), [integrations/claude-code-plugin/core/hook_runner.py:170-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py#L170-L191), [integrations/codex-plugin/core/hook_runner.py:170-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/hook_runner.py#L170-L191), [integrations/cursor-plugin/core/hook_runner.py:170-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L170-L191). Sources: [integrations/antigravity-plugin/core/hook_runner.py:170-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/hook_runner.py#L170-L191), [integrations/kimi-plugin/core/hook_runner.py:170-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/hook_runner.py#L170-L191), [integrations/claude-code-plugin/core/hook_runner.py:170-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py#L170-L191), [integrations/codex-plugin/core/hook_runner.py:170-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/hook_runner.py#L170-L191), [integrations/cursor-plugin/core/hook_runner.py:170-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L170-L191) ## In-Plugin Diagnostic Utilities Plugin distributions bundle a command-line interface script (`memory_cli.py`) that exposes diagnostic checks, telemetry controls, and repository memory management directly within agent environments and IDE extensions [integrations/mem0-agent-plugin/core/memory_cli.py:1-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L1-L20). The script configures harness environments via `--harness`, overrides local storage paths through `--plugin-data-dir`, and dispatches structured subcommands parsed with `argparse` [integrations/mem0-agent-plugin/core/memory_cli.py:61-87](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L61-L87). | Subcommand | Arguments | Action Performed | | --- | --- | --- | | `status` | `--json` | Prints operational state, repository identity, local data paths, API key presence, and last operation metrics | | `doctor` | `--json` | Runs system diagnostics and integrity checks against the working environment | | `pause` | None | Disables local memory saving and searching by setting the `paused` setting to `true` | | `resume` | None | Re-enables local memory operations by setting the `paused` setting to `false` | | `forget` | `--remote`, `--yes`, `--include-project-memory` | Purges local repository memory scopes and optionally removes remote repository data from Mem0 | Sources: [integrations/mem0-agent-plugin/core/memory_cli.py:67-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L67-L80), [integrations/mem0-agent-plugin/core/memory_cli.py:92-146](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L92-L146)] > [!WARNING] > The `forget` subcommand enforces a safety check requiring the `--yes` flag; attempting to execute deletion without `--yes` aborts with exit code `2` to prevent accidental loss of user or project memory scopes [integrations/mem0-agent-plugin/core/memory_cli.py:124-129](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L124-L129). Sources: [integrations/mem0-agent-plugin/core/memory_cli.py:124-129](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L124-L129)] The CLI records control telemetry events for every invoked command before executing the corresponding handler [integrations/mem0-agent-plugin/core/memory_cli.py:91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L91). Upon completion, the underlying `EvidenceStore` is safely closed within a `finally` block, and background telemetry flushes are spawned to transmit pending diagnostic records [integrations/mem0-agent-plugin/core/memory_cli.py:147-150](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L147-L150). Sources: [integrations/mem0-agent-plugin/core/memory_cli.py:91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L91), [integrations/mem0-agent-plugin/core/memory_cli.py:147-150](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L147-L150)] ## Related - [Python Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core) - [TypeScript Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/typescript-agent-core) --- ## Technical docs: Python Agent Core URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core
Relevant source files The following files were used as context for generating this wiki page: - [integrations/agent-plugin-core/python/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py) - [integrations/cursor-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py) - [integrations/codex-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/hook_runner.py) - [integrations/antigravity-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/hook_runner.py) - [integrations/kimi-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/hook_runner.py) - [integrations/claude-code-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/agent-plugin-core/python/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py) - [integrations/antigravity-plugin/core/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py) - [integrations/cursor-plugin/core/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py) - [integrations/agent-plugin-core/python/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py) - [integrations/kimi-plugin/core/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py) - [integrations/mem0-agent-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py) - [integrations/claude-code-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py) - [integrations/codex-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/mcp_server.py) - [integrations/agent-plugin-core/python/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/telemetry.py) - [integrations/cursor-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/mcp_server.py) - [integrations/antigravity-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py) - [integrations/kimi-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/mcp_server.py) - [integrations/mem0-agent-plugin/mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/mcp.json) - [integrations/codex-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py) - [integrations/claude-code-plugin/.mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/.mcp.json) - [integrations/codex-plugin/.mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/.mcp.json) - [integrations/agent-plugin-core/python/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py)
## Overview The Python Agent Core provides the shared runtime foundation for Mem0 coding agent plugins, managing local evidence persistence, hook orchestration, and background synchronization. It solves the challenge of capturing developer interactions across distinct agent harnesses by maintaining an SQLite-backed evidence store for session events and subagent turns. Design decisions prioritize local-first reliability through detached worker handoffs, robust error recovery, and strict data scoping. Sources: [integrations/agent-plugin-core/python/hook_runner.py:1-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py#L1-L34), [integrations/agent-plugin-core/python/memory_core.py:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L1-L8), [integrations/agent-plugin-core/python/flush_worker.py:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L1-L6) ## Evidence Store Architecture and Session State ### Storage Model and Session Synchronization The underlying SQLite-backed storage model captures session events, subagent turns, and prompt evidence locally before synchronization. When agents compact or conclude sessions, the system evaluates the state via `flush_session()`, verifying API keys, determining repository contexts through `store.repo_for_session()`, and preparing pending packets via `store.prepare_flush()` to synchronize records with the platform. Sources: [integrations/agent-plugin-core/python/memory_core.py:1962-1978](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L1962-L1978), [integrations/mem0-agent-plugin/core/memory_core.py:1962-1978](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1962-L1978), [integrations/antigravity-plugin/core/memory_core.py:1962-1978](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L1962-L1978), [integrations/cursor-plugin/core/memory_core.py:1962-1978](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L1962-L1978), [integrations/codex-plugin/core/memory_core.py:1962-1978](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L1962-L1978) > [!NOTE] > Extraction jobs are durable server-side. Transient polling failures during `_wait_for_event()` do not discard jobs that may still complete normally. Sources: [integrations/agent-plugin-core/python/memory_core.py:1925-1929](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L1925-L1929), [integrations/mem0-agent-plugin/core/memory_core.py:1925-1929](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1925-L1929), [integrations/antigravity-plugin/core/memory_core.py:1925-1929](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L1925-L1929), [integrations/cursor-plugin/core/memory_core.py:1925-1929](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L1925-L1929), [integrations/codex-plugin/core/memory_core.py:1925-1929](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L1925-L1929) ### Public API Surface and Event Polling The public functions manage background heartbeats and remote event tracking. The function `touch_handoff_heartbeat()` updates the worker's handoff file timestamp using `MEM0_CODE_HANDOFF_PATH` to prevent recovery mechanisms from prematurely relaunching active workers. Meanwhile, `_wait_for_event()` polls server-side extraction tasks using configurable parameters. | Parameter / Variable | Environment Variable | Default Value | Purpose | | :--- | :--- | :--- | :--- | | Extraction Wait Timeout | `MEM0_CODE_EXTRACTION_WAIT_SECONDS` | `"120"` | Maximum seconds to wait for remote event extraction to complete. | | Event Poll Interval | `MEM0_CODE_EVENT_POLL_SECONDS` | `"1"` (minimum `0.1`) | Delay interval between successive event status polling checks. | | Worker Handoff Path | `MEM0_CODE_HANDOFF_PATH` | `""` (disabled) | Filesystem path for the worker handoff heartbeat file. | | Platform API URL | `MEM0_API_URL` | `DEFAULT_API_URL` | Base URL endpoint for Mem0 platform synchronization requests. | Sources: [integrations/agent-plugin-core/python/memory_core.py:1893-1910](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L1893-L1910), [integrations/mem0-agent-plugin/core/memory_core.py:1893-1910](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1893-L1910), [integrations/antigravity-plugin/core/memory_core.py:1893-1910](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L1893-L1910), [integrations/cursor-plugin/core/memory_core.py:1893-1910](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L1893-L1910), [integrations/codex-plugin/core/memory_core.py:1893-1910](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L1893-L1910) ## Hook Runner Orchestration and Injection ### Input Handling and First-Prompt Context Injection The hook runner reads JSON payloads from standard input via `read_hook_input()`, safely parsing dictionary structures while falling back to empty dictionaries on decoding or operating system errors. ```python def read_hook_input() -> dict: try: value = json.load(sys.stdin) return value if isinstance(value, dict) else {} except (json.JSONDecodeError, OSError): return {} ``` Sources: [integrations/agent-plugin-core/python/hook_runner.py:44-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py#L44-L50) When a user submits a prompt, `first_prompt_memory_output()` determines whether the interaction represents the session's initial prompt via `record_user_prompt()`. If it is the first prompt, the function checks environment constraints before executing a memory search. ```python def first_prompt_memory_output(store: EvidenceStore, hook_input: dict) -> dict: """Search once before the agent handles the first prompt in a session.""" repo, session_id, prompt, is_first_prompt = record_user_prompt(store, hook_input) if not is_first_prompt: return {} try: minimum_query_chars = int(os.environ.get("MEM0_CODE_MIN_QUERY_CHARS", "20")) except ValueError: minimum_query_chars = 20 if len(prompt.strip()) < max(minimum_query_chars, 1): return {} result = search_memories( store, repo, session_id, bounded(prompt, 6000), top_k=5, operation="first-prompt-search", timeout=2, ) ``` Sources: [integrations/agent-plugin-core/python/hook_runner.py:62-76](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py#L62-L76) > [!NOTE] > The search query character length must meet or exceed the minimum threshold defined by `MEM0_CODE_MIN_QUERY_CHARS` (defaulting to 20 characters) and is bounded to a maximum of 6000 characters before querying. Sources: [integrations/agent-plugin-core/python/hook_runner.py:67-74](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py#L67-L74) If matching memories are retrieved, they are formatted into additional context and logged via telemetry under the `context_injected` event trigger. The hook runner then returns a dictionary containing `hookSpecificOutput` with the populated `additionalContext`. ```python if not result.memories: return {} context = format_context( result.memories, "Mem0 found these relevant memories from earlier work in this repository:", ) telemetry.record( "context_injected", repo=repo, session_id=session_id, trigger="first-prompt", memory_count=len(result.memories), context_chars=len(context), prompt_chars=len(prompt), ) return { "hookSpecificOutput": { "hookEventName": "UserPromptSubmit", "additionalContext": context, }, } ``` Sources: [integrations/agent-plugin-core/python/hook_runner.py:77-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py#L77-L94) ### Event Recording and Tool Error Adaptation Agent turns and tool executions are captured through specialized record handlers. The function `default_record_stop()` logs the assistant response without transcript parsing by retrieving the session repository via `store.repo_for_session()`, redacting sensitive substrings from `last_assistant_message`, and recording the clean text. ```python def default_record_stop(store: EvidenceStore, hook_input: dict): """Record the assistant's response without transcript parsing.""" session_id = _session_id(hook_input) repo = store.repo_for_session(session_id, hook_input.get("cwd")) message = redact(hook_input.get("last_assistant_message", "")).strip() if message: store.record_assistant_response(repo, session_id, message) return repo, session_id ``` Sources: [integrations/agent-plugin-core/python/hook_runner.py:52-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py#L52-L60) Specific integrations like the Codex plugin adapter augment default behavior by handling subagent lifecycle events and normalizing tool execution responses. Specifically, `_post_tool()` inspects tool response objects for numeric exit codes or boolean flags (`isError`, `success`) to reliably flag tool failures before passing payloads to `record_tool()`. ```python response = payload.get("tool_response") failed = None if isinstance(response, dict): exit_code = response.get("exit_code") if isinstance(exit_code, int) and not isinstance(exit_code, bool): failed = exit_code != 0 elif isinstance(response.get("isError"), bool): failed = response["isError"] elif isinstance(response.get("success"), bool): failed = not response["success"] if failed: payload = {**payload, "error": response} record_tool(store, payload, failed=failed) ``` Sources: [integrations/codex-plugin/hooks/adapter.py:43-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L43-L55) > [!WARNING] > Boolean evaluation order for tool responses checks non-boolean integers first (`exit_code != 0`), followed by `isError`, and finally `success`. Ensure tool response schemas populate these keys accurately to prevent misclassified execution states. Sources: [integrations/codex-plugin/hooks/adapter.py:45-52](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L45-L52) ## Detached Background Flush Worker Lifecycle ### Subprocess Handoff and Payload Initialization When print-mode host processes or exit hooks face termination constraints (such as Claude Code canceling `SessionEnd` hooks as parent streams close), hooks persist their input payloads and launch `flush_worker.py` as a detached background process in a new session. ```python def main() -> int: if len(sys.argv) != 2: return 2 handoff_path = Path(sys.argv[1]) os.environ["MEM0_CODE_HANDOFF_PATH"] = str(handoff_path) harness = os.environ.get("MEM0_PLUGIN_HARNESS") if harness: source_tag = os.environ.get("MEM0_PLUGIN_SOURCE_TAG", "") configure_harness( harness, env_prefix=os.environ.get("MEM0_PLUGIN_ENV_PREFIX", ""), data_dir_name=os.environ.get("MEM0_PLUGIN_DATA_DIR_NAME", ""), source_tag=source_tag, ) telemetry.init(harness=harness, source_tag=source_tag.upper()) completed = False ``` Sources: [integrations/agent-plugin-core/python/flush_worker.py:25-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L25-L40), [integrations/antigravity-plugin/core/flush_worker.py:25-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py#L25-L40), [integrations/cursor-plugin/core/flush_worker.py:25-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py#L25-L40), [integrations/kimi-plugin/core/flush_worker.py:25-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py#L25-L40) The worker expects exactly one command-line argument: the file path to the handoff JSON payload (`sys.argv[1]`). It assigns this path to the environment variable `MEM0_CODE_HANDOFF_PATH`. If `MEM0_PLUGIN_HARNESS` is configured in the environment, the worker initializes the plugin harness configuration and sets up telemetry using `telemetry.init()`. ```python try: payload = json.loads(handoff_path.read_text(encoding="utf-8")) delay = float(payload.get("delay_seconds") or 0) if delay > 0: payload.pop("delay_seconds", None) temporary = handoff_path.with_suffix(f".{os.getpid()}.tmp") try: temporary.write_text(json.dumps(payload), encoding="utf-8") temporary.replace(handoff_path) finally: temporary.unlink(missing_ok=True) time.sleep(delay) if not handoff_path.exists(): return 0 ``` Sources: [integrations/agent-plugin-core/python/flush_worker.py:41-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L41-L54), [integrations/antigravity-plugin/core/flush_worker.py:41-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py#L41-L54), [integrations/cursor-plugin/core/flush_worker.py:41-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py#L41-L54), [integrations/kimi-plugin/core/flush_worker.py:41-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py#L41-L54) After loading the payload, the worker inspects `delay_seconds`. If a positive delay is specified, the worker pops the delay key, writes the modified payload to a temporary file (`..tmp`), atomically replaces the original handoff file, and sleeps for the requested duration. ```python hook_input = payload.get("hook_input") or {} reason = str(payload.get("reason") or "checkpoint") wait_for_inflight = bool(payload.get("wait_for_inflight")) store = EvidenceStore() try: if wait_for_inflight: session_id = str( hook_input.get("session_id") or "unknown-session" ) repo = store.repo_for_session(session_id, hook_input.get("cwd")) deadline = time.monotonic() + float( os.environ.get("MEM0_CODE_EXTRACTION_WAIT_SECONDS", "120") ) while ( store.has_inflight_flush(repo.identity, session_id) and time.monotonic() < deadline ): touch_handoff_heartbeat() time.sleep(0.25) ``` Sources: [integrations/agent-plugin-core/python/flush_worker.py:55-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L55-L73), [integrations/antigravity-plugin/core/flush_worker.py:55-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py#L55-L73), [integrations/cursor-plugin/core/flush_worker.py:55-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py#L55-L73), [integrations/kimi-plugin/core/flush_worker.py:55-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py#L55-L73) ### Heartbeat Tracking and Inflight Checkpointing If the payload configures `wait_for_inflight` as true, the worker monitors existing database activity before executing the final checkpoint. It retrieves the active repository context for the session and polls `store.has_inflight_flush()` until active flushes clear or a timeout is reached. ```python deadline = time.monotonic() + float( os.environ.get("MEM0_CODE_EXTRACTION_WAIT_SECONDS", "120") ) while ( store.has_inflight_flush(repo.identity, session_id) and time.monotonic() < deadline ): touch_handoff_heartbeat() time.sleep(0.25) ``` Sources: [integrations/agent-plugin-core/python/flush_worker.py:65-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L65-L73), [integrations/antigravity-plugin/core/flush_worker.py:65-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py#L65-L73), [integrations/cursor-plugin/core/flush_worker.py:65-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py#L65-L73), [integrations/kimi-plugin/core/flush_worker.py:65-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py#L65-L73) > [!NOTE] > The extraction wait timeout defaults to 120 seconds, configurable via the `MEM0_CODE_EXTRACTION_WAIT_SECONDS` environment variable. During each 0.25-second poll iteration, the worker invokes `touch_handoff_heartbeat()`. Sources: [integrations/agent-plugin-core/python/flush_worker.py:66-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L66-L73), [integrations/agent-plugin-core/python/memory_core.py:1893-1901](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L1893-L1901) The helper `touch_handoff_heartbeat()` updates the modification timestamp of the handoff file via `os.utime(path)`, signaling to external supervisor processes that the background worker remains active. ```python def touch_handoff_heartbeat() -> None: """Mark the worker's handoff file alive so recovery does not relaunch it.""" path = os.environ.get("MEM0_CODE_HANDOFF_PATH", "") if not path: return try: os.utime(path) except OSError: pass ``` Sources: [integrations/agent-plugin-core/python/memory_core.py:1893-1901](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L1893-L1901) ### Status Evaluation and Recovery Cleanup Once inflight checks conclude, the worker invokes `checkpoint_session(store, hook_input, reason)` to process and transmit the session evidence packet to the platform backend. ```python # Hooks capture the conversation before handoff; the worker only flushes it. result = checkpoint_session(store, hook_input, reason) print(json.dumps(result, sort_keys=True), flush=True) completed = result.get("status") in { "semantic-succeeded", "explicitly-stored", "nothing-to-flush", } finally: store.close() return 0 ``` Sources: [integrations/agent-plugin-core/python/flush_worker.py:74-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L74-L84), [integrations/antigravity-plugin/core/flush_worker.py:74-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py#L74-L84), [integrations/cursor-plugin/core/flush_worker.py:74-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py#L74-L84), [integrations/kimi-plugin/core/flush_worker.py:74-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py#L74-L84) The completion status determines how the handoff file is cleaned up in the `finally` block. Successful status codes validate completion, unlinking the handoff file. If incomplete and the file uses a `.running` suffix, it is renamed back to `.json` for recovery. ```python finally: telemetry.flush() if completed: try: handoff_path.unlink() except OSError: pass elif handoff_path.suffix == ".running": try: handoff_path.replace(handoff_path.with_suffix(".json")) except OSError: pass ``` Sources: [integrations/agent-plugin-core/python/flush_worker.py:85-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L85-L97), [integrations/antigravity-plugin/core/flush_worker.py:85-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py#L85-L97), [integrations/cursor-plugin/core/flush_worker.py:85-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py#L85-L97), [integrations/kimi-plugin/core/flush_worker.py:85-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py#L85-L97) | Status Code | Action | Outcome | | :--- | :--- | :--- | | `semantic-succeeded` | Complete execution | Handoff file is unlinked | | `explicitly-stored` | Complete execution | Handoff file is unlinked | | `nothing-to-flush` | Complete execution | Handoff file is unlinked | | Other / Error | Incomplete execution | `.running` suffix renamed to `.json` | Sources: [integrations/agent-plugin-core/python/flush_worker.py:77-81](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L77-L81), [integrations/agent-plugin-core/python/flush_worker.py:87-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L87-L96) ## MCP Server Memory Search Tool ### JSON-RPC Protocol Handling and Request Routing The Model Context Protocol (MCP) server runs over standard input and output streams, processing newline-delimited JSON-RPC 2.0 messages via `main()`. Incoming payloads are parsed and routed through `handle_request()`, which supports methods including `initialize`, `ping`, `tools/list`, and `tools/call`. Initialization responds with protocol version `2024-11-05`, tool capabilities, and server metadata containing name `mem0` and `PLUGIN_VERSION`. ```python def handle_request(message: Any) -> dict[str, Any] | None: if not isinstance(message, dict): return None request_id = message.get("id") method = message.get("method") if method == "notifications/initialized": return None if method == "initialize": requested = (message.get("params") or {}).get("protocolVersion") return { "jsonrpc": "2.0", "id": request_id, "result": { "protocolVersion": requested or PROTOCOL_VERSION, "capabilities": {"tools": {"listChanged": False}}, "serverInfo": {"name": "mem0", "version": PLUGIN_VERSION}, }, } if method == "ping": return {"jsonrpc": "2.0", "id": request_id, "result": {}} if method == "tools/list": return { "jsonrpc": "2.0", "id": request_id, "result": { "tools": [ { "name": TOOL_NAME, "description": TOOL_DESCRIPTION, "inputSchema": TOOL_SCHEMA, "annotations": { "readOnlyHint": True, "idempotentHint": True, "openWorldHint": True, }, } ] }, } ``` Sources: [integrations/agent-plugin-core/python/mcp_server.py:152-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L152-L191) > [!NOTE] > Unhandled methods return a JSON-RPC error with code `-32601` ("Method not found"), while parse errors yield code `-32700` and internal exceptions return code `-32603`. Notifications such as `notifications/initialized` return `None` and produce no output. Sources: [integrations/agent-plugin-core/python/mcp_server.py:158-159](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L158-L159), [integrations/agent-plugin-core/python/mcp_server.py:206-231](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L206-L231) ### Tool Schema and Argument Validation When a client invokes `tools/call` for `search_memories`, arguments are validated by `_validate_arguments()` against a strict input schema. The tool accepts a required `query` string alongside optional parameters `top_k`, `category`, `scope`, and `run_id`. | Parameter | Type | Validation Rules | Description | | :--- | :--- | :--- | :--- | | `query` | string | Non-empty, max 2000 chars | Direct question about earlier work in the repository | | `top_k` | integer | Integer between 1 and 20 (inclusive) | Maximum memories to return | | `category` | string | Must be in `CODING_MEMORY_CATEGORY_NAMES` | Optional memory category filter | | `scope` | string | Must be in `SEARCH_SCOPES` | Search boundary (`repo`, `dir`, or `mine`) | | `run_id` | string | Non-empty string when provided | Coding-agent session ID restriction | Sources: [integrations/agent-plugin-core/python/mcp_server.py:28-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L28-L68), [integrations/agent-plugin-core/python/mcp_server.py:75-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L75-L112) > [!WARNING] > Passing boolean values for integer parameters like `top_k` or supplying unknown keys outside the permitted set triggers a `ToolInputError`, which is caught and returned to the client as an MCP tool error response with `isError: true`. Sources: [integrations/agent-plugin-core/python/mcp_server.py:78-83](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L78-L83), [integrations/agent-plugin-core/python/mcp_server.py:93-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L93-L96), [integrations/agent-plugin-core/python/mcp_server.py:201-202](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L201-L202) ### Workspace Resolution and Execution The helper `_workspace_cwd(params)` inspects request metadata for `x-codex-turn-metadata` workspaces to determine the active working directory. If absent, `call_search_memories()` falls back to `CLAUDE_PROJECT_DIR` or the current working directory via `resolve_repo()`, then executes `search_memories()` with operation `mcp-search`. ```python def call_search_memories(arguments: Any, cwd: str | None = None) -> str: query, top_k, category, scope, run_id = _validate_arguments(arguments) repo = resolve_repo(cwd or os.environ.get("CLAUDE_PROJECT_DIR") or os.getcwd()) result = search_memories( None, repo, None, query, top_k=top_k, category=category, scope=scope, run_id=run_id, operation="mcp-search", ) return format_search_result(result) def _workspace_cwd(params: dict[str, Any]) -> str | None: meta = params.get("_meta") if not isinstance(meta, dict): return None metadata = meta.get("x-codex-turn-metadata") if not isinstance(metadata, dict): return None workspaces = metadata.get("workspaces") or {} if isinstance(workspaces, dict): return next((path for path in workspaces if isinstance(path, str) and path), None) return None ``` Sources: [integrations/agent-plugin-core/python/mcp_server.py:115-142](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L115-L142) ## Telemetry Spooling and Identity Management ### Spooling Architecture and Non-Blocking Recording Telemetry events record session details locally without blocking tool execution or touching the network during short-lived agent turns. The `record` function appends a single JSON line to a local spool file within a strict 3 to 6-second hook execution budget. > [!NOTE] > Telemetry never transmits sensitive content such as prompts, memory text, queries, file paths, repository names, or raw API keys. Recorded payloads are restricted to event names, durations, coarse outcomes, counts, and identifiers hashed with a random per-installation salt. Sources: [integrations/agent-plugin-core/python/telemetry.py:8-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/telemetry.py#L8-L17) ### Installation State and Salt Management The installation salt is generated once and memoized using an atomic file creation mechanism (`O_CREAT |O_EXCL`) located at `telemetry-salt` within the data directory. This design isolates salt generation from identity dictionaries, preventing concurrent processes from overwriting repository hashes or splitting user identification during rapid tool invocations. Telemetry can be completely disabled across any process by setting the environment variable `MEM0_TELEMETRY` to `false`, `0`, `no`, or `off`. Sources: [integrations/agent-plugin-core/python/telemetry.py:115-149](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/telemetry.py#L115-L149) ### Configuration Constants and Claim Parameters The telemetry module defines several operational constants governing spool limits, batch sizes, timeout boundaries, and claim retry mechanics for background delivery workers. | Constant Name | Value | Purpose | | :--- | :--- | :--- | | `SPOOL_LIMIT_BYTES` | `262144` (256 KB) | Maximum size threshold for the local telemetry spool file | | `BATCH_SIZE` | `100` | Maximum number of events sent in a single PostHog batch request | | `SEND_TIMEOUT` | `5` | Maximum network timeout duration in seconds for HTTP requests | | `CLAIM_STALE_SECONDS` | `120` | Time window before an unacknowledged claim is considered stale | | `CLAIM_EXPIRY_SECONDS` | `604800` (7 days) | Total expiration period for parked claims | | `MAX_CLAIM_ATTEMPTS` | `3` | Maximum retry attempts before a batch claim is permanently discarded | | `MAX_PARKED_PER_RUN` | `3` | Bound on parked claims drained per flush run after the live spool | | `RETRY_COOLDOWN_SECONDS` | `60` | Cooldown period added per prior attempt before a released claim is reclaimable | Sources: [integrations/agent-plugin-core/python/telemetry.py:98-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/telemetry.py#L98-L112) ## Command-Line Diagnostics and User Controls ### Status Verification and Health Diagnostics The CLI tooling exposed by `memory_cli.py` provides administrative commands for inspecting plugin state, verifying environment configuration, and running diagnostic checks. Invoking `status` retrieves summary metrics including pause state, repository identity, local data path, API key configuration status, total recorded session details, memory updates, returned memories, and subagent runs. When executed with `--json`, these properties are serialized directly as a JSON object, while default execution formats human-readable metrics through `_print_status`. The `doctor` command executes diagnostic validations against the current working directory, printing individual check outcomes as `PASS` or `FAIL` with detailed error descriptions, returning an exit code of `0` on success or `1` if any check fails. ```python def _print_status(value: dict) -> None: last = value.get("last_operation") or {} print(f"Mem0: {'paused' if value['paused'] else 'active'}") print(f"Repository: {value['repo_id']}") print(f"Local data: {value['data_dir']}") print(f"API key: {'configured' if value['api_key_configured'] else 'missing'}") print( "Saved on this computer: " f"{value['events']} session details, {value['flushes']} memory updates" ) print( f"Used in this repository: {value['retrievals']} memories returned, " f"{value['subagent_runs']} subagent runs" ) if last: item_label = "" if last["operation"] in {"flush", "flush-retry"}: item_label = f", {last['item_count']} memories" operation = ( "memory update" if last["operation"] in {"flush", "flush-retry"} else last["operation"].replace("-", " ") ) print( f"Last {operation}: " f"{'succeeded' if last['success'] else 'failed'} " f"({last['duration_ms']:.1f} ms{item_label})" ) subagent = value.get("last_subagent") or {} if subagent: state = "finished" if subagent.get("stopped_at") else "started" print( "Last subagent: " f"{state}, received {subagent['context_chars']} characters of memory, " f"agent {subagent['agent_id']}" ) ``` Sources: [integrations/agent-plugin-core/python/memory_cli.py:23-59](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py#L23-L59), [integrations/agent-plugin-core/python/memory_cli.py:67-72](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py#L67-L72), [integrations/agent-plugin-core/python/memory_cli.py:92-116](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py#L92-L116) ### Configuration Management and Repository Forgetting Users can toggle memory persistence dynamically using the `pause` and `resume` commands, which update the `paused` setting in the evidence store to `true` or `false` respectively. Repository data removal is handled by the `forget` command, which requires explicit confirmation via the `--yes` flag to prevent accidental data destruction. If `--yes` is omitted, the CLI aborts with exit code `2` and displays a warning message. When confirmed, `forget` invokes `forget_remote_repo()` if `--include-project-memory` and `--remote` are specified, alongside `store.forget_local_repo()` to clear local SQLite evidence for the repository identity. > [!CAUTION] > The `forget` command permanently destroys local session evidence and, when `--remote` is supplied, deletes scoped repository and user memories from the Mem0 platform. This operation requires the `--yes` safety flag and cannot be undone. Sources: [integrations/agent-plugin-core/python/memory_cli.py:76-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py#L76-L80), [integrations/agent-plugin-core/python/memory_cli.py:117-146](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py#L117-L146) ## Related - [TypeScript Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/typescript-agent-core) - [Claude Code Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin) - [IDE Plugins Family](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/ide-plugins-family) --- ## Technical docs: TypeScript Agent Core URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/typescript-agent-core
Relevant source files The following files were used as context for generating this wiki page: - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/agent-plugin-core/typescript/src/lifecycle.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts) - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [integrations/agent-plugin-core/python/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py) - [integrations/kimi-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/hook_runner.py) - [integrations/antigravity-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/hook_runner.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/deepseek-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [mem0-ts/src/community/src/integrations/langchain/mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts) - [integrations/openclaw/recall.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts) - [integrations/pi-agent-plugin/src/memory/tools.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts) - [integrations/openclaw/isolation.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/isolation.ts) - [mem0-ts/src/oss/src/prompts/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/prompts/index.ts) - [mem0-ts/src/oss/src/llms/langchain.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/langchain.ts) - [integrations/agent-plugin-core/typescript/src/formatting.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/formatting.ts) - [integrations/agent-plugin-core/typescript/src/identity.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/identity.ts) - [integrations/agent-plugin-core/typescript/src/prompts.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/prompts.ts) - [integrations/agent-plugin-core/typescript/src/scoping.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/scoping.ts)
## Overview The TypeScript Agent Core provides foundational infrastructure and utilities for integrating Mem0 long-term memory into developer coding assistants and agent runtimes. It standardizes state management, prompt generation, scoping, and data formatting across host environments. Sources: [integrations/agent-plugin-core/typescript/src/lifecycle.ts:86-121](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L86-L121), [integrations/agent-plugin-core/typescript/src/formatting.ts:1-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/formatting.ts#L1-L54), [integrations/agent-plugin-core/typescript/src/scoping.ts:1-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/scoping.ts#L1-L50) ## Lifecycle hook execution ### MemoryLifecycle Class and Session Management The `MemoryLifecycle` class implements a shared lifecycle policy where host adapters translate native events into standard memory operations. It manages internal state tracking through a private `#seenMemoryIds` set and an `#options` configuration object initialized via `MemoryLifecycleOptions`. ```typescript class MemoryLifecycle { readonly #seenMemoryIds = new Set(); readonly #options: MemoryLifecycleOptions; constructor(options: MemoryLifecycleOptions = {}) { this.#options = options; } beginSession(): void { this.#seenMemoryIds.clear(); } ... ``` Sources: [integrations/agent-plugin-core/typescript/src/lifecycle.ts:86-98](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L86-L98) The `beginSession()` method clears the `#seenMemoryIds` collection to reset duplicate suppression for a new conversation turn or session. `prepareConversation()` and `prepareUserText()` process raw messages and user inputs by extracting text blocks and sanitizing sensitive tokens. | Option Key | Type | Default | Purpose | | :--- | :--- | :--- | :--- | | `recallHeading` | `string` | `RECALL_HEADING` | Custom heading injected above retrieved memories | | `maxContextChars` | `number` | `4_000` (`DEFAULT_MAX_CONTEXT_CHARS`) | Maximum character budget for injected memory context | | `recallTimeoutMs` | `number` | `2_000` | Timeout threshold for asynchronous memory search operations | Sources: [integrations/agent-plugin-core/typescript/src/lifecycle.ts:5-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L5-L6), [integrations/agent-plugin-core/typescript/src/lifecycle.ts:80-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L80-L84), [integrations/agent-plugin-core/typescript/src/lifecycle.ts:99-121](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L99-L121) > [!NOTE] > The `recall` method delegates directly to `buildRecallContext`, passing along the instance's configured heading, character limits, seen memory identifiers, and timeout parameters. Sources: [integrations/agent-plugin-core/typescript/src/lifecycle.ts:109-120](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L109-L120) ### Context Retrieval and Secret Redundancy The `buildRecallContext` function orchestrates asynchronous memory retrieval, timeout enforcement, duplicate filtering, and character budgeting. If `enabled` is false or the bounded query evaluates to empty, it returns an empty string immediately. ```typescript export async function buildRecallContext( prompt: string, enabled: boolean, search: (query: string) => Promise<{ results?: unknown[] }>, options: RecallOptions = {}, ): Promise { if (!enabled) return ""; const query = boundedText(prompt, MAX_RECALL_QUERY_CHARS); if (!query) return ""; ... ``` Sources: [integrations/agent-plugin-core/typescript/src/lifecycle.ts:129-138](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L129-L138) Search execution is wrapped in a `Promise.race` call against a timeout promise defaulting to `2,000` milliseconds. Retrieved results are filtered against `seenIds` to omit previously injected memories, and formatted entries are accumulated until `maxChars` (`DEFAULT_MAX_CONTEXT_CHARS`) is reached. ```typescript const SECRET_PATTERNS: Array<[RegExp, string]> = [ [/(authorization\s*[:=]\s*(?:bearer|token)\s+)[^\s"']+/gi, "$1[REDACTED]"], [ /((?:api[_-]?key|secret[_-]?access[_-]?key|session[_-]?token)\s*[:=]\s*)[^\s"']+/gi, "$1[REDACTED]", ], [ /((?:access[_-]?token|refresh[_-]?token|password|credential)\s*[:=]\s*)[^\s&"']+/gi, "$1[REDACTED]", ], [/\b(?:sk|m0|mem0_sk|psk)-[A-Za-z0-9_-]{12,}\b/g, "[REDACTED]"], [/\b(?:ASIA|AKIA)[A-Z0-9]{12,}\b/g, "[REDACTED]"], [/\b(?:ghp_|github_pat_|xox[baprs]-)[A-Za-z0-9_-]{12,}\b/g, "[REDACTED]"], [/-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?-----END [^-]*PRIVATE KEY-----/g, "[REDACTED]"], ]; ``` Sources: [integrations/agent-plugin-core/typescript/src/lifecycle.ts:8-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L8-L22) > [!WARNING] > `redactSecrets` applies these regular expression patterns to all conversation extracts, user text inputs, and memory compaction outputs to prevent accidental leakage of authorization headers, API keys, private keys, and session tokens into model prompts. Sources: [integrations/agent-plugin-core/typescript/src/lifecycle.ts:24-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L24-L31), [integrations/agent-plugin-core/typescript/src/lifecycle.ts:67-67](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L67-L67), [integrations/agent-plugin-core/typescript/src/lifecycle.ts:105-107](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L105-L107), [integrations/agent-plugin-core/typescript/src/lifecycle.ts:160-162](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L160-L162) ## Session scoping resolution ### Scope Resolution and Normalization Memory isolation across execution contexts is governed by the `Scope` type, supporting `"project"`, `"session"`, and `"global"` values. The `normalizeScope` utility enforces this constraint by validating incoming unknown inputs and defaulting unrecognised values to `"project"`. ```typescript export type Scope = "project" | "session" | "global"; export function normalizeScope(value: unknown): Scope { return value === "session" || value === "global" ? value : "project"; } ``` Sources: [integrations/agent-plugin-core/typescript/src/scoping.ts:1-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/scoping.ts#L1-L11) When resolving tool execution scopes via `resolveToolScope`, if a requested scope is absent, the function falls back to the configured default scope. > [!CAUTION] > If a user requests `"global"` scope when the configured scope is not `"global"`, `resolveToolScope` throws an error demanding that the global scope be selected explicitly in plugin settings or via the `/mem0-scope` command first. ```typescript export function resolveToolScope(requested: Scope | undefined, configured: Scope): Scope { const scope = requested ?? configured; if (scope === "global" && configured !== "global") { throw new Error("Select global scope in the plugin settings or /mem0-scope command first."); } return scope; } ``` Sources: [integrations/agent-plugin-core/typescript/src/scoping.ts:13-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/scoping.ts#L13-L19) ### Context Validation and Filter Mapping The `ScopeContext` interface requires specific identification properties depending on the active hierarchy level. Validation rules inspect context properties to ensure required identifiers are present and non-empty. | Scope | Required Context Keys | Validation Behavior | | :--- | :--- | :--- | | `"global"` | `userId` | Validates `userId` is present, trimmed, and not wildcarded (`*`) | | `"project"` | `userId`, `appId` | Validates both `userId` and `appId` | | `"session"` | `userId`, `appId`, `runId` | Validates `userId`, `appId`, and `runId` | Sources: [integrations/agent-plugin-core/typescript/src/scoping.ts:3-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/scoping.ts#L3-L7), [integrations/agent-plugin-core/typescript/src/scoping.ts:21-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/scoping.ts#L21-L30) > [!WARNING] > `validateContext` throws an `Error` prefixed with `"Invalid memory scope"` if any evaluated key is empty, undefined, whitespace-only, or composed entirely of asterisks (`*+`). ```typescript function validateContext(scope: Scope, context: ScopeContext): void { const keys: (keyof ScopeContext)[] = ["userId"]; if (scope !== "global") keys.push("appId"); if (scope === "session") keys.push("runId"); for (const key of keys) { if (!context[key]?.trim() || /^\*+$/.test(context[key].trim())) { throw new Error(`Invalid memory scope ${key}`); } } } ``` Sources: [integrations/agent-plugin-core/typescript/src/scoping.ts:21-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/scoping.ts#L21-L30) Search filters and add parameters translate validated scopes into concrete key-value dictionaries. The `scopeSearchFilters` function maps scopes to database-query filters, whereas `scopeAddParams` formats payload parameters for memory persistence operations. ```typescript export function scopeSearchFilters(scope: Scope, context: ScopeContext): Record { validateContext(scope, context); if (scope === "session") { return { user_id: context.userId, app_id: context.appId, run_id: context.runId }; } return scope === "global" ? { user_id: context.userId } : { user_id: context.userId, app_id: context.appId }; } ``` Sources: [integrations/agent-plugin-core/typescript/src/scoping.ts:32-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/scoping.ts#L32-L40) ```typescript export function scopeAddParams(scope: Scope, context: ScopeContext): Record { validateContext(scope, context); if (scope === "session") { return { userId: context.userId, appId: context.appId, runId: context.runId }; } return scope === "global" ? { userId: context.userId } : { userId: context.userId, appId: context.appId }; } ``` Sources: [integrations/agent-plugin-core/typescript/src/scoping.ts:42-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/scoping.ts#L42-L50) ## Identity detection and entity parameters ### Entity Parameters Structure and Normalization The `EntityParams` interface defines the core attribution properties used across memory search and persistence operations. It supports three optional identifiers: `userId`, `agentId`, and `runId`. ```typescript export interface EntityParams { userId?: string; agentId?: string; runId?: string; } ``` Sources: [integrations/agent-plugin-core/typescript/src/identity.ts:1-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/identity.ts#L1-L5) An internal helper function `clean` trims string inputs, translating empty or whitespace-only strings into `undefined`. ```typescript const clean = (value: string | undefined): string | undefined => value?.trim() || undefined; ``` Sources: [integrations/agent-plugin-core/typescript/src/identity.ts:7-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/identity.ts#L7-L7) ### Filter Mapping and Parameter Construction Entity parameters are translated into database search filters and memory addition payload parameters via dedicated helper functions. Both `entitySearchFilters` and `entityAddParams` accept an `EntityParams` object and a fallback `defaultUserId`. ```typescript export function entitySearchFilters( params: EntityParams, defaultUserId: string, ): Record { const filters: Record = { user_id: clean(params.userId) ?? defaultUserId }; const agentId = clean(params.agentId); const runId = clean(params.runId); if (agentId) filters.agent_id = agentId; if (runId) filters.run_id = runId; return filters; } ``` Sources: [integrations/agent-plugin-core/typescript/src/identity.ts:9-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/identity.ts#L9-L19) ```typescript export function entityAddParams(params: EntityParams, defaultUserId: string): Record { const values: Record = { userId: clean(params.userId) ?? defaultUserId }; const agentId = clean(params.agentId); const runId = clean(params.runId); if (agentId) values.agentId = agentId; if (runId) values.runId = runId; return values; } ``` Sources: [integrations/agent-plugin-core/typescript/src/identity.ts:21-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/identity.ts#L21-L28) | Function | Output Key Format | Fallback Behavior | Optional Keys Included | | :--- | :--- | :--- | :--- | | `entitySearchFilters` | `user_id`, `agent_id`, `run_id` | `clean(params.userId) ?? defaultUserId` | `agent_id`, `run_id` | | `entityAddParams` | `userId`, `agentId`, `runId` | `clean(params.userId) ?? defaultUserId` | `agentId`, `runId` | Sources: [integrations/agent-plugin-core/typescript/src/identity.ts:9-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/identity.ts#L9-L28) ### Remote Git URL Parsing The `parseProjectFromRemote` function extracts project identifiers from Git remote URL strings using a regular expression that matches organization and repository segments while stripping optional `.git` suffixes and trailing slashes. ```typescript export function parseProjectFromRemote(remote: string): string | null { const match = remote.trim().match(/[:/]([^/:]+)\/([^/:]+?)(?:\.git)?\/?$/); return match ? `${match[1]}-${match[2]}` : null; } ``` Sources: [integrations/agent-plugin-core/typescript/src/identity.ts:30-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/identity.ts#L30-L33) > [!NOTE] > `parseProjectFromRemote` expects standard SSH or HTTPS remote strings (such as `git@github.com:owner/repo.git` or `https://github.com/owner/repo`), returning a hyphenated `${owner}-${repo}` string or `null` if the pattern fails to match. Sources: [integrations/agent-plugin-core/typescript/src/identity.ts:30-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/identity.ts#L30-L33) ## Prompt synthesis and guidance constants ### Repository and User-Scoped Prompt Constants Standardizing prompt directives, search timing triggers, and query descriptions enables consistent LLM tool selection across repository-scoped and general user contexts. The `prompts.ts` module exports static string constants that define search triggers, tool descriptions, query descriptions, and injection headers. The prompt constants are partitioned into repository-scoped variants and general user-scoped variants. The `SEARCH_WHEN` trigger specifies execution timing for search actions. ```typescript export const SEARCH_WHEN = "before repeating investigation or when earlier decisions, fixes, commands, or results may help"; ``` Sources: [integrations/agent-plugin-core/typescript/src/prompts.ts:1-2](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/prompts.ts#L1-L2) Repository-scoped constants bind tool behavior to the current repository scope, whereas user-scoped constants omit repository restrictions. | Constant Name | Value | Scope | Sources | | :--- | :--- | :--- | :--- | | `SEARCH_WHEN` | `"before repeating investigation or when earlier decisions, fixes, commands, or results may help"` | Shared Trigger | [integrations/agent-plugin-core/typescript/src/prompts.ts:1-2](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/prompts.ts#L1-L2) | | `SEARCH_TOOL_DESCRIPTION` | `"Search memories from earlier work in this repository. Use it " + SEARCH_WHEN + "."` | Repository | [integrations/agent-plugin-core/typescript/src/prompts.ts:4-4](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/prompts.ts#L4-L4) | | `SEARCH_QUERY_DESCRIPTION` | `"A direct question about earlier work in this repository."` | Repository | [integrations/agent-plugin-core/typescript/src/prompts.ts:5-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/prompts.ts#L5-L5) | | `RECALL_HEADING` | `"Mem0 found these relevant memories from earlier work in this repository:"` | Repository | [integrations/agent-plugin-core/typescript/src/prompts.ts:6-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/prompts.ts#L6-L6) | | `USER_SEARCH_TOOL_DESCRIPTION` | `"Search memories from earlier work. Use it " + SEARCH_WHEN + "."` | General User | [integrations/agent-plugin-core/typescript/src/prompts.ts:8-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/prompts.ts#L8-L8) | | `USER_SEARCH_QUERY_DESCRIPTION` | `"A direct question about earlier work."` | General User | [integrations/agent-plugin-core/typescript/src/prompts.ts:9-9](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/prompts.ts#L9-L9) | | `USER_RECALL_HEADING` | `"Mem0 found these relevant memories from earlier work:"` | General User | [integrations/agent-plugin-core/typescript/src/prompts.ts:10-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/prompts.ts#L10-L10) | Sources: [integrations/agent-plugin-core/typescript/src/prompts.ts:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/prompts.ts#L1-L10) ## Memory formatting interfaces ### Memory Data Structures and Output Formatting The [Typescript Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/typescript-core/typescript-core) defines data structures and formatting utilities to transform retrieved records into compact injection formats for agent contexts. The core data contract is represented by the `MemoryLike` interface, which models individual stored memory records with identifiers, text payloads, categorical labels, and creation timestamps. ```typescript export interface MemoryLike { id: string; memory?: string; categories?: string[]; createdAt?: Date | string; } ``` Sources: [integrations/agent-plugin-core/typescript/src/formatting.ts:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/formatting.ts#L1-L6) To prevent context bloat during model injection and CLI reporting, output limits enforce boundaries on serialized memory text. Maximum output constraints restrict total lines, character counts, and byte sizes. ```typescript export const MAX_OUTPUT_LINES = 200; export const MAX_OUTPUT_CHARS = 50_000; export const MAX_OUTPUT_BYTES = MAX_OUTPUT_CHARS; ``` Sources: [integrations/agent-plugin-core/typescript/src/formatting.ts:8-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/formatting.ts#L8-L10) | Constant / Interface | Type / Value | Purpose | Sources | | :--- | :--- | :--- | :--- | | `MemoryLike` | Interface | Models memory records (`id`, `memory`, `categories`, `createdAt`) | [integrations/agent-plugin-core/typescript/src/formatting.ts:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/formatting.ts#L1-L6) | | `MAX_OUTPUT_LINES` | `200` | Limits total rendered lines for memory output | [integrations/agent-plugin-core/typescript/src/formatting.ts:8-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/formatting.ts#L8-L8) | | `MAX_OUTPUT_CHARS` | `50_000` | Limits total character length for memory output | [integrations/agent-plugin-core/typescript/src/formatting.ts:9-9](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/formatting.ts#L9-L9) | | `MAX_OUTPUT_BYTES` | `MAX_OUTPUT_CHARS` | Limits total byte size for memory output | [integrations/agent-plugin-core/typescript/src/formatting.ts:10-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/formatting.ts#L10-L10) | Sources: [integrations/agent-plugin-core/typescript/src/formatting.ts:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/formatting.ts#L1-L10) ### Transformation and Truncation Functions Retrieved records are formatted into readable strings using utility functions that calculate relative age, apply category badges, and structure lists. The `formatAge` function computes time intervals in minutes, hours, or days from a timestamp. ```typescript export function formatAge(date: Date | string): string { const minutes = Math.floor((Date.now() - new Date(date).getTime()) / 60_000); if (minutes < 60) return `${minutes}m ago`; const hours = Math.floor(minutes / 60); return hours < 24 ? `${hours}h ago` : `${Math.floor(hours / 24)}d ago`; } ``` Sources: [integrations/agent-plugin-core/typescript/src/formatting.ts:12-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/formatting.ts#L12-L17) The `formatMemoryCompact` function builds a single-line representation containing the primary category, memory text, age suffix, and memory identifier tag. ```typescript export function formatMemoryCompact(memory: MemoryLike): string { const category = memory.categories?.[0] ?? "uncategorized"; const age = memory.createdAt ? ` (${formatAge(memory.createdAt)})` : ""; return `[${category}] ${memory.memory ?? "(empty)"}${age} [mem0:${memory.id}]`; } ``` Sources: [integrations/agent-plugin-core/typescript/src/formatting.ts:19-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/formatting.ts#L19-L23) > [!NOTE] > When multiple memory records are returned, `formatMemoryList` maps over the array to generate a numbered list, falling back to a default string if no records exist. Sources: [integrations/agent-plugin-core/typescript/src/formatting.ts:25-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/formatting.ts#L25-L29) The execution walkthrough begins when the `apply` function is invoked in the DeepSeek plugin. It delegates to `stateFor` to retrieve or initialize the session state, which invokes `createMemoryLifecycle` to instantiate a new `MemoryLifecycle` instance for managing conversation preparation and memory recall. ```mermaid sequenceDiagram participant apply as apply() participant stateFor as stateFor() participant createMemoryLifecycle as createMemoryLifecycle() participant MemoryLifecycle as MemoryLifecycle apply->>stateFor: Resolve active session state stateFor->>createMemoryLifecycle: Initialize lifecycle instance if missing createMemoryLifecycle->>MemoryLifecycle: Instantiate MemoryLifecycle class MemoryLifecycle-->>apply: Return configured lifecycle policy ``` Sources: [integrations/deepseek-plugin/src/index.ts:96-121](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L96-L121), [integrations/agent-plugin-core/typescript/src/lifecycle.ts:87-127](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts#L87-L127) ## Related - [Python Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core) - [OpenCode Integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/opencode-integration) - [OpenClaw Integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/openclaw-integration) --- ## Technical docs: Claude Code Plugin URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin
Relevant source files The following files were used as context for generating this wiki page: - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/claude-code-plugin/.mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/.mcp.json) - [integrations/claude-code-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py) - [integrations/claude-code-plugin/adapters/claude/transcript.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/transcript.py) - [integrations/codex-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/hook_runner.py) - [integrations/antigravity-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/hook_runner.py) - [integrations/agent-plugin-core/python/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py) - [integrations/kimi-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/hook_runner.py) - [integrations/cursor-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py) - [integrations/claude-code-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/claude-code-plugin/adapters/claude/hook.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/hook.py) - [integrations/codex-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/mcp_server.py) - [integrations/antigravity-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/mcp_server.py) - [integrations/claude-code-plugin/core/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/flush_worker.py) - [integrations/antigravity-plugin/core/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/cursor-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/mcp_server.py) - [integrations/kimi-plugin/core/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py) - [integrations/cursor-plugin/core/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py) - [integrations/codex-plugin/core/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/flush_worker.py) - [integrations/agent-plugin-core/python/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py) - [integrations/mem0-agent-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py) - [integrations/agent-plugin-core/python/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py) - [integrations/antigravity-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks/adapter.py)
## Overview The Claude Code plugin integrates Mem0 memory management into the Claude Code developer agent via Model Context Protocol (MCP) server registration, transcript parsing, and host event hooks. It captures session conversations, extracts relevant coding insights, and injects persistent repository context into agent workflows. Sources: [integrations/claude-code-plugin/.mcp.json:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/.mcp.json#L1-L10), [integrations/claude-code-plugin/core/memory_core.py:2-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L2-L7), [integrations/claude-code-plugin/adapters/claude/transcript.py:1-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/transcript.py#L1-L20) ## MCP Server Configuration and Manifest ### Manifest Registration and Environment Configuration The Claude Code plugin configures its local Model Context Protocol (MCP) server through a declarative manifest file, `.mcp.json`, which defines the execution environment and runtime command. The manifest registers a single server named `mem0` that executes via `python3` targeting the plugin's core script at `${CLAUDE_PLUGIN_ROOT}/core/mcp_server.py`. It injects the runtime data directory through the environment variable `MEM0_CODE_DATA_DIR`, mapped from `${CLAUDE_PLUGIN_DATA}`. ```json { "mcpServers": { "mem0": { "command": "python3", "args": ["${CLAUDE_PLUGIN_ROOT}/core/mcp_server.py"], "env": { "MEM0_CODE_DATA_DIR": "${CLAUDE_PLUGIN_DATA}" } } } } ``` Sources: [integrations/claude-code-plugin/.mcp.json:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/.mcp.json#L1-L10) ### MCP Server Protocol and Tool Definition The local server implementation in `core/mcp_server.py` adheres to MCP protocol version `2024-11-05`, exposing the `search_memories` tool. The server handles JSON-RPC request methods including `initialize`, `ping`, `tools/list`, and `tools/call`, returning capability listings and execution responses. The exposed tool carries annotations declaring it as read-only, idempotent, and open-world. ```python PROTOCOL_VERSION = "2024-11-05" TOOL_NAME = "search_memories" TOOL_DESCRIPTION = ( "Search memories from earlier work in this repository. Use it before " "repeating investigation or when earlier decisions, fixes, commands, or " "results may help." ) ``` Sources: [integrations/claude-code-plugin/core/mcp_server.py:21-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L21-L27) ```python if method == "tools/list": return { "jsonrpc": "2.0", "id": request_id, "result": { "tools": [ { "name": TOOL_NAME, "description": TOOL_DESCRIPTION, "inputSchema": TOOL_SCHEMA, "annotations": { "readOnlyHint": True, "idempotentHint": True, "openWorldHint": True, }, } ] }, } ``` Sources: [integrations/claude-code-plugin/core/mcp_server.py:173-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L173-L191) The tool accepts five search parameters with explicit typing and validation rules enforced by `_validate_arguments`. | Parameter | Type | Validation Rules | Purpose | | :--- | :--- | :--- | :--- | | `query` | `string` | Non-empty, max 2000 chars, required | Direct question about earlier work in the repository | | `top_k` | `integer` | 1 to 20, non-boolean | Maximum memories to return | | `category` | `string` | Must be in `CODING_MEMORY_CATEGORY_NAMES` | Optional memory category filter | | `scope` | `string` | Must be in `SEARCH_SCOPES` | Memory search scope (`repo`, `dir`, `mine`) | | `run_id` | `string` | Non-empty string when provided | Optional coding-agent session ID restriction | Sources: [integrations/claude-code-plugin/core/mcp_server.py:28-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L28-L68) > [!WARNING] > Boolean inputs passed to `top_k` are explicitly rejected by `_validate_arguments` even though Python treats booleans as integers; input validation requires strict integer checks between 1 and 20 to prevent malformed query parameters. > Sources: [integrations/claude-code-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L93-L96) ## Transcript Parsing and Ingestion ### Conversation Stream Reading and Row Parsing The Claude Code transcript ingestion engine parses newline-delimited JSON records from conversation transcript files. The `_transcript_rows` function opens the transcript path in binary mode, seeks to an optional byte offset, and reads line by line. Each line is decoded as UTF-8 with error replacement, parsed via `json.loads`, and filtered to retain records containing a valid `uuid` field. ```python def _transcript_rows( path: str, offset: int = 0 ) -> tuple[list[dict[str, Any]], int, bool]: """Parse transcript rows from a byte offset, returning rows, end offset, and whether the offset was honored.""" if not path: return [], 0, False rows = [] try: resolved = Path(path).expanduser() if not 0 <= offset <= resolved.stat().st_size: offset = 0 end = offset with resolved.open("rb") as handle: handle.seek(offset) for line in handle: if not line.endswith(b"\n"): break end += len(line) try: row = json.loads(line.decode("utf-8", errors="replace")) except json.JSONDecodeError: continue if isinstance(row, dict) and row.get("uuid"): rows.append(row) except OSError: return [], offset, False return rows, end, offset > 0 ``` Sources: [integrations/claude-code-plugin/adapters/claude/transcript.py:39-66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/transcript.py#L39-L66) > [!WARNING] > If a trailing line in the transcript file does not end with a newline byte (`b"\n"`), it is immediately discarded by the parser loop to prevent ingestion of partial, atomically unwritten JSON writes. > Sources: [integrations/claude-code-plugin/adapters/claude/transcript.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/transcript.py#L53-L55) ### Active Chain Traversal and Message Extraction The parser constructs an active conversation chain by identifying the latest leaf record matching the target `session_id` while ignoring sidechain records where `isSidechain` is true. Starting from this leaf node, it traverses backward through parent records using `parentUuid` references, collecting nodes into a sequence and reversing them into chronological order. ```python def _active_transcript_chain( rows: list[dict[str, Any]], session_id: str ) -> list[dict[str, Any]]: """Follow the current Claude conversation branch from its latest record.""" by_uuid = {str(row["uuid"]): row for row in rows if row.get("uuid")} leaf = next( ( row for row in reversed(rows) if not row.get("isSidechain") and str(row.get("sessionId") or "") == session_id ), None, ) if leaf is None: return [] chain = [] seen = set() current = leaf while current is not None: uuid = str(current.get("uuid") or "") if not uuid or uuid in seen: break seen.add(uuid) chain.append(current) current = by_uuid.get(str(current.get("parentUuid") or "")) chain.reverse() return chain ``` Sources: [integrations/claude-code-plugin/adapters/claude/transcript.py:68-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/transcript.py#L68-L96) User prompts and subagent interactions undergo redaction and prefix filtering before memory extraction. Human prompt text filtering strips attachment markers and ignores specific system or command prefixes. | Ignored Prompt Prefix | Purpose | | :--- | :--- | | `` | Suppresses local shell execution warnings | | `` | Suppresses raw stdout captures from tool runs | | `` | Suppresses raw stderr captures from tool runs | | `` | Suppresses internal command execution markers | | `` | Suppresses injected system reminders | | `` | Suppresses asynchronous task notification banners | Sources: [integrations/claude-code-plugin/adapters/claude/transcript.py:112-120](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/transcript.py#L112-L120) ## Claude Hook Dispatching and Lifecycle ### Environment Setup and Syspath Bootstrap The Claude Code hook adapter initializes the execution environment by configuring Python's module search path (`sys.path`). It resolves the directory containing `hook_runner.py` by inspecting the script location and falling back between bundled core directories. ```python _here = Path(__file__).resolve() _bundled_core = _here.parents[2] / "core" _core_dir = _bundled_core if (_bundled_core / "memory_core.py").is_file() else _bundled_core / "python" sys.path.insert(0, str(_core_dir)) sys.path.insert(0, str(_here.parent)) ``` Sources: [integrations/claude-code-plugin/adapters/claude/hook.py:8-14](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/hook.py#L8-L14) Once the path is resolved, the adapter registers the harness with the memory core and telemetry subsystems using the identifier `claude-code`. ```python configure_harness("claude-code", data_dir_name="claude-code-plugin", source_tag="claude_code_plugin") telemetry.init(harness="claude-code", source_tag="CLAUDE_CODE_PLUGIN") ``` Sources: [integrations/claude-code-plugin/adapters/claude/hook.py:25-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/hook.py#L25-L26) ### Sidekick Subagent Actions The Claude hook adapter defines specialized handlers for sidekick subagent lifecycle events. The `_sidekick_start` handler records the start of a subagent with the agent type `mem0:sidekick` and injects any returned context back into the agent runtime via structured hook output. ```python def _sidekick_start(store, hook_input): context = record_subagent_start(store, {"agent_type": "mem0:sidekick", **hook_input}) if context: return { "hookSpecificOutput": { "hookEventName": "SubagentStart", "additionalContext": context, }, } ``` Sources: [integrations/claude-code-plugin/adapters/claude/hook.py:29-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/hook.py#L29-L38) Conversely, `_sidekick_stop` records the conclusion of the sidekick subagent session without returning specialized hook output. ```python def _sidekick_stop(store, hook_input): record_subagent_stop(store, {"agent_type": "mem0:sidekick", **hook_input}) ``` Sources: [integrations/claude-code-plugin/adapters/claude/hook.py:40-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/hook.py#L40-L41) ### Hook Entry Point Mapping When invoked as the main script, the adapter delegates execution to `hook_runner.entry_point`, passing custom action bindings and configuration parameters. | Parameter | Value | Purpose | | :--- | :--- | :--- | | `record_stop_fn` | `record_stop` | Transcript parsing function imported from `transcript.py` | | `extra_actions["post-tool-failure"]` | `lambda s, h: record_tool(s, h, failed=True)` | Records tool execution failures | | `extra_actions["sidekick-start"]` | `_sidekick_start` | Initializes sidekick subagent session | | `extra_actions["sidekick-stop"]` | `_sidekick_stop` | Concludes sidekick subagent session | | `data_dir_env` | `"MEM0_CODE_DATA_DIR"` | Environment variable override for data directory storage | | `automatic_flush_reasons` | `{"session-end", "pre-compact"}` | Triggers automatic asynchronous flush workers | Sources: [integrations/claude-code-plugin/adapters/claude/hook.py:44-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/hook.py#L44-L54) > [!NOTE] > Automatic checkpoint flushing is restricted to explicit lifecycle milestones (`session-end` and `pre-compact`) to prevent unnecessary background worker spawn overhead during incremental tool executions. > Sources: [integrations/claude-code-plugin/adapters/claude/hook.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/hook.py#L53-L53) ## Core Memory Management and State ### Harness Configuration and Constants The shared memory core initializes runtime metadata via `configure_harness()`, establishing default identifiers and environment variables across execution harnesses. ```python DEFAULT_API_URL = "https://api.mem0.ai" PLUGIN_VERSION = "0.3.3" _harness_name: str = "generic" _harness_env_prefix: str = "MEM0_PLUGIN" _harness_data_dir_name: str = "mem0-plugin" _harness_source_tag: str = "mem0_plugin" ``` Sources: [integrations/claude-code-plugin/core/memory_core.py:31-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L31-L38) ```python def configure_harness( name: str, env_prefix: str = "", data_dir_name: str = "", source_tag: str = "", ) -> None: global _harness_name, _harness_env_prefix, _harness_data_dir_name, _harness_source_tag _harness_name = name _harness_env_prefix = env_prefix or f"MEM0_{name.upper().replace('-', '_')}" _harness_data_dir_name = data_dir_name or f"{name}-plugin" _harness_source_tag = source_tag or f"{name.replace('-', '_')}_plugin" ``` Sources: [integrations/claude-code-plugin/core/memory_core.py:40-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L40-L51) Memory persistence and extraction operations adhere to explicit character, exchange, and retry limits defined at the module level. | Constant Name | Value | Purpose | | :--- | :--- | :--- | | `MAX_COMMAND_CHARS` | `2000` | Maximum character length for captured command strings | | `MAX_RESULT_CHARS` | `2500` | Maximum character length for command execution results | | `MAX_EPISODE_CHARS` | `12000` | Maximum character length for accumulated episode payloads | | `CHECKPOINT_EXCHANGES` | `5` | Exchange count threshold for checkpoint triggers | | `CHECKPOINT_MESSAGES` | `10` | Message count threshold for checkpoint triggers | | `CHECKPOINT_SOURCE_CHARS` | `40000` | Maximum source text size for checkpoint extraction | | `DEFAULT_MAX_CONTEXT_CHARS` | `4000` | Default character budget for injected memory context | | `MAX_EXTRACTION_INPUT_TOKENS` | `24000` | Token limit for LLM memory extraction inputs | | `MAX_FLUSH_ATTEMPTS` | `5` | Maximum retry attempts for asynchronous flush workers | | `FORGET_PAGE_SIZE` | `100` | Pagination size for memory deletion operations | | `FORGET_MAX_PAGES` | `50` | Maximum pagination depth for memory deletion | Sources: [integrations/claude-code-plugin/core/memory_core.py:62-72](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L62-L72) > [!NOTE] > The harness configuration exposes `harness_config()`, which returns a dictionary containing the active plugin name, environment prefix, data directory name, and source tracking tag used by downstream storage and telemetry components. > Sources: [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L53-L59) ### Memory Instructions and Categorization Project memory extraction relies on strict prompt instructions that guide the agent to save concise repository facts regarding behavior, implementation locations, constraints, and reasoning. ```python PROJECT_MEMORY_INSTRUCTIONS = """Save concise repository facts that will help with future coding work. A completed change should produce one memory explaining the resulting behavior, where it is implemented when useful, and any important constraints or reasoning. Exploration or accepted decisions may produce separate memories only when they are independently useful. Use the coding agent's final response for conclusions about current repository behavior. Do not save proposed or recommended changes unless the user accepted them or the coding agent completed them. Treat subagent responses as supporting repository evidence, not as decisions. Write about the repository, not the user, assistant, session, or task. Do not include test results, documentation updates, release notes, or temporary state. If nothing useful was established, return no memories.""" ``` Sources: [integrations/claude-code-plugin/core/memory_core.py:74-82](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L74-L82) Personal memory extraction uses distinct instructions focused on user preferences, written in the third person. ```python PERSONAL_MEMORY_INSTRUCTIONS = """Save concise facts about the user that will help in any repository: preferred tools, package managers, languages, coding style, review and communication preferences, and anything the user explicitly asked to be remembered about themselves. Write in the third person about the user, not about the repository, the assistant, the session, or the task. Do not save repository facts, project decisions, commands, or what was built. Never save that the user has no preferences or that nothing was learned. If nothing was learned about the user, return no memories.""" ``` Sources: [integrations/claude-code-plugin/core/memory_core.py:84-88](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L84-L88) Coding memories are classified into explicit structural categories to organize project knowledge. | Category Identifier | Description | | :--- | :--- | | `project_knowledge` | What the project is and how its code, APIs, data, files, and components work. | | `decisions_and_constraints` | Why an approach was chosen, what must remain true, and rules future work must follow. | | `workflows` | How to run, test, debug, deploy, configure, or otherwise work on the project. | | `problems_and_fixes` | Bugs, failures, known pitfalls, their causes, and how to fix or avoid them. | | `results` | Outcomes and measurements from tests, benchmarks, experiments, or investigations. | Sources: [integrations/claude-code-plugin/core/memory_core.py:90-121](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L90-L121) > [!WARNING] > `CODING_MEMORY_CATEGORY_NAMES` is programmatically derived as a tuple of keys from `CODING_MEMORY_CATEGORIES`. Modifying the dictionary structure directly impacts category validation downstream. > Sources: [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L122-L126) ### Sanitization and Security Redaction Command matching rules detect test and build invocations using regular expressions across multiple testing frameworks and build tools. ```python TEST_COMMAND_RE = re.compile( r"(?:^|\s)(?:pytest|py\.test|jest|vitest|go\s+test|cargo\s+test|" r"npm\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|yarn\s+test|" r"mvn\s+test|gradle\s+test|make\s+test)(?:\s|$)", re.IGNORECASE, ) BUILD_COMMAND_RE = re.compile( r"(?:^|\s)(?:npm|pnpm|yarn)\s+(?:run\s+)?build(?:\s|$)|" r"(?:^|\s)(?:cargo|go|mvn|gradle|make)\s+build(?:\s|$)", re.IGNORECASE, ) ``` Sources: [integrations/claude-code-plugin/core/memory_core.py:128-138](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L128-L138) To prevent credential leakage, the memory core scans strings and serialized payloads against a comprehensive list of secret patterns, replacing matched secrets with `[REDACTED]`. ```python SECRET_PATTERNS = [ re.compile(r"(?i)(authorization\s*[:=]\s*(?:bearer|token)\s+)[^\s\"']+"), re.compile( r"(?i)((?:api[_-]?key|secret[_-]?access[_-]?key|session[_-]?token)\s*[:=]\s*)[^\s\"']+" ), re.compile( r"(?i)((?:access[_-]?token|refresh[_-]?token|password|credential)" r"\s*[:=]\s*)[^\s&\"']+" ), re.compile(r"\b(?:sk|m0|mem0_sk|psk)-[A-Za-z0-9_\-]{12,}\b"), re.compile(r"\b(?:ASIA|AKIA)[A-Z0-9]{12,}\b"), re.compile(r"\b(?:ghp_|github_pat_|xox[baprs]-)[A-Za-z0-9_\-]{12,}\b"), re.compile( r"-----BEGIN [^-]*PRIVATE KEY-----.*?-----END [^-]*PRIVATE KEY-----", re.DOTALL, ), re.compile( r'(?i)("(?:api[_-]?key|password|secret(?:[_-]?access[_-]?key)?' r'|(?:access|refresh|session)[_-]?token|token|authorization|credential' r')"\s*:\s*")(?:\\.|[^"\\])*' ), ] ``` Sources: [integrations/claude-code-plugin/core/memory_core.py:140-161](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L140-L161) The utility function `redact()` processes arbitrary input values, serializing non-string types via JSON and applying all defined secret regular expression patterns sequentially. ```python def redact(value: Any) -> str: text = ( value if isinstance(value, str) else json.dumps(value, ensure_ascii=False, default=str) ) for pattern in SECRET_PATTERNS: if pattern.groups: text = pattern.sub(r"\1[REDACTED]", text) else: text = pattern.sub("[REDACTED]", text) return text ``` Sources: [integrations/claude-code-plugin/core/memory_core.py:168-179](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L168-L179) Furthermore, `bounded()` enforces strict character length constraints on redacted text, appending truncation notices when size limits are exceeded. ```python def bounded(value: Any, limit: int) -> str: text = redact(value).strip() if len(text) <= limit: return text return text[:limit] + f"\n...[truncated {len(text) - limit} chars]" ``` Sources: [integrations/claude-code-plugin/core/memory_core.py:182-187](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py#L182-L187) ## Asynchronous Remote Checkpoint Flushing ### Detached Worker Initialization and Handoff When session end hooks in print-mode processes risk being terminated prematurely, the hook runner persists its state locally and delegates processing to a detached background worker. The worker entry point expects a single argument representing the path to the handoff JSON payload. It configures the runtime environment and telemetry using environment variables such as `MEM0_PLUGIN_HARNESS`, `MEM0_PLUGIN_SOURCE_TAG`, and `MEM0_PLUGIN_ENV_PREFIX`. ```python def main() -> int: if len(sys.argv) != 2: return 2 handoff_path = Path(sys.argv[1]) os.environ["MEM0_CODE_HANDOFF_PATH"] = str(handoff_path) harness = os.environ.get("MEM0_PLUGIN_HARNESS") if harness: source_tag = os.environ.get("MEM0_PLUGIN_SOURCE_TAG", "") configure_harness( harness, env_prefix=os.environ.get("MEM0_PLUGIN_ENV_PREFIX", ""), data_dir_name=os.environ.get("MEM0_PLUGIN_DATA_DIR_NAME", ""), source_tag=source_tag, ) telemetry.init(harness=harness, source_tag=source_tag.upper()) ``` Sources: [integrations/claude-code-plugin/core/flush_worker.py:25-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/flush_worker.py#L25-L39) Payloads can specify a delay before execution occurs. If `delay_seconds` is greater than zero, the worker writes the configuration to a temporary process-specific file, replaces the original handoff path, and sleeps for the designated duration. ```python try: payload = json.loads(handoff_path.read_text(encoding="utf-8")) delay = float(payload.get("delay_seconds") or 0) if delay > 0: payload.pop("delay_seconds", None) temporary = handoff_path.with_suffix(f".{os.getpid()}.tmp") try: temporary.write_text(json.dumps(payload), encoding="utf-8") temporary.replace(handoff_path) finally: temporary.unlink(missing_ok=True) time.sleep(delay) if not handoff_path.exists(): return 0 ``` Sources: [integrations/claude-code-plugin/core/flush_worker.py:41-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/flush_worker.py#L41-L54) > [!NOTE] > If the handoff file is removed during the delay interval, the worker terminates immediately with a status code of `0`. > Sources: [integrations/claude-code-plugin/core/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/flush_worker.py#L52-L54) ### In-Flight Concurrency and Checkpoint Execution When `wait_for_inflight` is enabled in the payload, the worker tracks active session operations prior to executing the checkpoint session. It computes a deadline using `MEM0_CODE_EXTRACTION_WAIT_SECONDS` (defaulting to 120 seconds) and periodically touches the handoff heartbeat while waiting for active flushes to clear. ```python if wait_for_inflight: session_id = str( hook_input.get("session_id") or "unknown-session" ) repo = store.repo_for_session(session_id, hook_input.get("cwd")) deadline = time.monotonic() + float( os.environ.get("MEM0_CODE_EXTRACTION_WAIT_SECONDS", "120") ) while ( store.has_inflight_flush(repo.identity, session_id) and time.monotonic() < deadline ): touch_handoff_heartbeat() time.sleep(0.25) ``` Sources: [integrations/claude-code-plugin/core/flush_worker.py:60-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/flush_worker.py#L60-L73) Once conditions are satisfied, the worker invokes `checkpoint_session()` with the `EvidenceStore`, hook inputs, and reason string. Completion status is evaluated against a fixed set of recognized terminal states. ```python # Hooks capture the conversation before handoff; the worker only flushes it. result = checkpoint_session(store, hook_input, reason) print(json.dumps(result, sort_keys=True), flush=True) completed = result.get("status") in { "semantic-succeeded", "explicitly-stored", "nothing-to-flush", } ``` Sources: [integrations/claude-code-plugin/core/flush_worker.py:74-81](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/flush_worker.py#L74-L81) | Status Identifier | Meaning | | :--- | :--- | | `semantic-succeeded` | Memory extraction and remote persistence completed successfully. | | `explicitly-stored` | Checkpoint data was stored directly without additional semantic parsing. | | `nothing-to-flush` | No new conversation evidence accumulated since the previous checkpoint. | Sources: [integrations/claude-code-plugin/core/flush_worker.py:77-81](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/flush_worker.py#L77-L81) ### Cleanup and File State Management Upon completion of the flush cycle, telemetry data is flushed, and handoff files are managed based on success metrics. Successfully completed checkpoints unlink the handoff file, whereas interrupted running states revert their file suffix for future recovery. ```python finally: telemetry.flush() if completed: try: handoff_path.unlink() except OSError: pass elif handoff_path.suffix == ".running": try: handoff_path.replace(handoff_path.with_suffix(".json")) except OSError: pass ``` Sources: [integrations/claude-code-plugin/core/flush_worker.py:85-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/flush_worker.py#L85-L96) ## Related - [Python Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core) - [IDE Plugins Family](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/ide-plugins-family) --- ## Technical docs: IDE Plugins Family URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/ide-plugins-family
Relevant source files The following files were used as context for generating this wiki page: - [integrations/codex-plugin/hooks/hooks.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/hooks.json) - [integrations/antigravity-plugin/hooks.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks.json) - [integrations/cursor-plugin/mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/mcp.json) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/kimi-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py) - [integrations/cursor-plugin/hooks/hooks.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/hooks.json) - [integrations/cursor-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py) - [integrations/kimi-plugin/kimi.plugin.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/kimi.plugin.json) - [integrations/antigravity-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks/adapter.py) - [integrations/codex-plugin/.mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/.mcp.json) - [integrations/codex-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py) - [integrations/cursor-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/hook_runner.py) - [integrations/codex-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/hook_runner.py) - [integrations/claude-code-plugin/.mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/.mcp.json) - [integrations/antigravity-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/hook_runner.py) - [integrations/claude-code-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/claude-code-plugin/hooks/hooks.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/hooks/hooks.json) - [integrations/antigravity-plugin/core/_harness_id.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/_harness_id.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/antigravity-plugin/mcp_config.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/mcp_config.json) - [integrations/cursor-plugin/plugin-build.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/plugin-build.json) - [integrations/agent-plugin-core/python/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py) - [integrations/kimi-plugin/core/_harness_id.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/_harness_id.py) - [integrations/cursor-plugin/core/_harness_id.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/_harness_id.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/claude-code-plugin/adapters/claude/hook.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/hook.py)
## Overview The IDE plugins family integrates Mem0 memory persistence directly into developer coding environments [integrations/kimi-plugin/kimi.plugin.json:1-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/kimi.plugin.json#L1-L12), including [OpenCode Integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/opencode-integration) and the [Claude Code Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin). These integrations solve the fragmentation of context across sessions by capturing prompts, tool executions, and transcript streams, bridging editor hooks [integrations/cursor-plugin/hooks/adapter.py:2-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L2-L3) with the [Python Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core). Key design decisions include unified harness identifiers, normalized event adapters [integrations/kimi-plugin/hooks/adapter.py:85-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L85-L100), and Model Context Protocol (MCP) server declarations [integrations/cursor-plugin/mcp.json:2-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/mcp.json#L2-L17) to synchronize long-term memory across hosts. Sources: [integrations/kimi-plugin/kimi.plugin.json:1-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/kimi.plugin.json#L1-L12), [integrations/cursor-plugin/hooks/adapter.py:2-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L2-L3), [integrations/kimi-plugin/hooks/adapter.py:85-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L85-L100), [integrations/cursor-plugin/mcp.json:2-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/mcp.json#L2-L17) ## IDE MCP Server Manifest Specifications ### Cursor Environment Manifest The Cursor plugin configures the `mem0` Model Context Protocol server via `mcp.json`, declaring a `stdio` transport type executing `python3` with path resolution tied to `${CURSOR_PLUGIN_ROOT}`. Environment variables pass template parameters for API authentication and memory scoping directly to the underlying server process. ```json { "mcpServers": { "mem0": { "type": "stdio", "command": "python3", "args": [ "${CURSOR_PLUGIN_ROOT}/core/mcp_server.py" ], "env": { "PLUGIN_OPTION_API_KEY": "${api_key}", "PLUGIN_OPTION_USER_ID": "${user_id}", "PLUGIN_OPTION_TOP_K": "${top_k}", "PLUGIN_OPTION_MAX_CONTEXT_CHARS": "${max_context_chars}", "PLUGIN_OPTION_SEARCH_SCOPE": "${search_scope}" } } } } ``` Sources: [integrations/cursor-plugin/mcp.json:1-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/mcp.json#L1-L18) | Option Key | Value Template | Purpose | | :--- | :--- | :--- | | `PLUGIN_OPTION_API_KEY` | `${api_key}` | Authentication token for memory service requests | | `PLUGIN_OPTION_USER_ID` | `${user_id}` | User scope identifier for stored memories | | `PLUGIN_OPTION_TOP_K` | `${top_k}` | Result count limit for memory retrieval queries | | `PLUGIN_OPTION_MAX_CONTEXT_CHARS` | `${max_context_chars}` | Character threshold governing context injection size | | `PLUGIN_OPTION_SEARCH_SCOPE` | `${search_scope}` | Boundary filter applied during memory searches | Sources: [integrations/cursor-plugin/mcp.json:9-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/mcp.json#L9-L15) > [!NOTE] > Cursor relies on explicit `${CURSOR_PLUGIN_ROOT}` path interpolation within `args` to locate `mcp_server.py` relative to the installed plugin bundle directory. Sources: [integrations/cursor-plugin/mcp.json:5-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/mcp.json#L5-L8) ### Codex Environment Manifest The Codex plugin declares its MCP server configuration within `.mcp.json`, setting a working directory (`cwd`) of `.` alongside an explicit array of allowed environment variable names rather than interpolated option templates. ```json { "mcpServers": { "mem0": { "type": "stdio", "command": "python3", "args": ["./core/mcp_server.py"], "cwd": ".", "env_vars": [ "MEM0_API_KEY", "MEM0_API_URL", "MEM0_CODE_USER_ID", "MEM0_USER_ID", "MEM0_RESOLVED_USER_ID", "MEM0_PROJECT_ID", "MEM0_TELEMETRY" ] } } } ``` Sources: [integrations/codex-plugin/.mcp.json:1-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/.mcp.json#L1-L18) > [!IMPORTANT] > Codex uses the `env_vars` array specification to whitelist environment variables (`MEM0_API_KEY`, `MEM0_API_URL`, `MEM0_CODE_USER_ID`, `MEM0_USER_ID`, `MEM0_RESOLVED_USER_ID`, `MEM0_PROJECT_ID`, `MEM0_TELEMETRY`) to be passed through to the Python MCP server process. Sources: [integrations/codex-plugin/.mcp.json:8-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/.mcp.json#L8-L16) ### Antigravity Environment Manifest The Antigravity plugin uses a streamlined `mcp_config.json` manifest structure that omits explicit transport type declarations, environment maps, and working directories, launching `python3` with a relative script path. ```json { "mcpServers": { "mem0": { "command": "python3", "args": ["./core/mcp_server.py"] } } } ``` Sources: [integrations/antigravity-plugin/mcp_config.json:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/mcp_config.json#L1-L8) > [!WARNING] > Omitting `type: "stdio"` and environment block definitions means Antigravity relies entirely on process inheritance for runtime configuration and default transport bindings. Sources: [integrations/antigravity-plugin/mcp_config.json:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/mcp_config.json#L1-L8) ## Plugin Metadata and Host Declarations ### Kimi Plugin Definition The Kimi extension is defined by `kimi.plugin.json`, specifying metadata, package keywords, an Apache-2.0 license, and integration interfaces for Kimi Code. It registers a Model Context Protocol (MCP) server running `python3` with `./core/mcp_server.py`, alongside an array of event hooks with specific execution timeouts. ```json { "name": "mem0", "version": "0.3.3", "description": "Cross-session memory and token savings for coding agents.", "keywords": ["memory", "coding-agents", "continual-learning", "token-efficiency"], "author": { "name": "Mem0", "email": "support@mem0.ai" }, "homepage": "https://docs.mem0.ai/integrations/kimi", "license": "Apache-2.0", "interface": { "displayName": "Mem0", "shortDescription": "Cross-session memory for Kimi Code" }, "skills": "./skills/", "mcpServers": { "mem0": { "command": "python3", "args": ["./core/mcp_server.py"] } }, "hooks": [ { "event": "SessionStart", "command": "python3 ./hooks/adapter.py SessionStart", "timeout": 5 }, { "event": "UserPromptSubmit", "command": "python3 ./hooks/adapter.py UserPromptSubmit", "timeout": 6 }, { "event": "PostToolUse", "matcher": ".*", "command": "python3 ./hooks/adapter.py PostToolUse", "timeout": 3 }, { "event": "PostToolUseFailure", "matcher": ".*", "command": "python3 ./hooks/adapter.py PostToolUseFailure", "timeout": 3 }, { "event": "Stop", "command": "python3 ./hooks/adapter.py Stop", "timeout": 3 }, { "event": "PreCompact", "command": "python3 ./hooks/adapter.py PreCompact", "timeout": 5 }, { "event": "SessionEnd", "command": "python3 ./hooks/adapter.py SessionEnd", "timeout": 5 } ] } ``` Sources: [integrations/kimi-plugin/kimi.plugin.json:1-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/kimi.plugin.json#L1-L28) | Hook Event | Matcher | Command Template | Timeout (s) | | :--- | :--- | :--- | :--- | | `SessionStart` | — | `python3 ./hooks/adapter.py SessionStart` | 5 | | `UserPromptSubmit` | — | `python3 ./hooks/adapter.py UserPromptSubmit` | 6 | | `PostToolUse` | `.*` | `python3 ./hooks/adapter.py PostToolUse` | 3 | | `PostToolUseFailure` | `.*` | `python3 ./hooks/adapter.py PostToolUseFailure` | 3 | | `Stop` | — | `python3 ./hooks/adapter.py Stop` | 3 | | `PreCompact` | — | `python3 ./hooks/adapter.py PreCompact` | 5 | | `SessionEnd` | — | `python3 ./hooks/adapter.py SessionEnd` | 5 | Sources: [integrations/kimi-plugin/kimi.plugin.json:20-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/kimi.plugin.json#L20-L28) > [!NOTE] > Kimi hook registrations rely on direct script invocations via `python3 ./hooks/adapter.py` paired with capitalized event selectors. Sources: [integrations/kimi-plugin/kimi.plugin.json:20-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/kimi.plugin.json#L20-L28) ### Cursor Packaging and Native Build Configuration Cursor plugin builds are managed via `plugin-build.json`, which sets the package identifier to `mem0`, version `0.3.3`, and links to the Cursor documentation homepage. The `native` manifest maps the plugin root directory `${CURSOR_PLUGIN_ROOT}` to specific bundle assets required for distribution. ```json { "id": "mem0", "version": "0.3.3", "homepage": "https://docs.mem0.ai/integrations/cursor", "native": { "pluginRoot": "${CURSOR_PLUGIN_ROOT}", "files": { ".cursor-plugin/plugin.json": ".cursor-plugin/plugin.json", "hooks/hooks.json": "hooks/hooks.json", "hooks/adapter.py": "hooks/adapter.py", "mcp.json": "mcp.json" } } } ``` Sources: [integrations/cursor-plugin/plugin-build.json:1-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/plugin-build.json#L1-L13) | Bundled Asset Path | Target Destination | Purpose | | :--- | :--- | :--- | | `.cursor-plugin/plugin.json` | `.cursor-plugin/plugin.json` | Host extension registration metadata | | `hooks/hooks.json` | `hooks/hooks.json` | Lifecycle event hook mappings | | `hooks/adapter.py` | `hooks/adapter.py` | Python adapter translating host payloads | | `mcp.json` | `mcp.json` | Model Context Protocol server configuration | Sources: [integrations/cursor-plugin/plugin-build.json:7-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/plugin-build.json#L7-L12) > [!CAUTION] > The packaging manifest strictly requires all four bundled files under `native.files`; omitting any file path during compilation causes runtime initialization failures within the Cursor environment. Sources: [integrations/cursor-plugin/plugin-build.json:5-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/plugin-build.json#L5-L13) ## Host Hook Adapters and Normalization ### Payload Translation and Transcript Parsing Each IDE environment delivers lifecycle hooks and transcript files with distinct payloads. The host adapters translate these platform-specific structures into normalized arguments and key-value fields consumed by the shared Mem0 runtime. The Kimi plugin adapter maps native events such as `SessionStart`, `UserPromptSubmit`, `PostToolUse`, `PostToolUseFailure`, `Stop`, `PreCompact`, and `SessionEnd` to shared actions, normalizing field variations where `tool_output` or `error` populates `tool_response`, and `response` sets `last_assistant_message`. When processing `Stop` events, the adapter resolves the session directory from `KIMI_CODE_HOME` or `~/.kimi-code` using `session_index.jsonl` or directory scanning, locates the agent wire transcript at `agents/main/wire.jsonl`, parses assistant message loops, and injects the latest assistant response. ```python EVENTS = { "SessionStart": ["session-start"], "UserPromptSubmit": ["user-prompt"], "PostToolUse": ["post-tool"], "PostToolUseFailure": ["post-tool-failure"], "Stop": ["stop"], "PreCompact": ["flush", "--reason", "pre-compact"], "SessionEnd": ["flush", "--reason", "session-end"], } def _session_dir(session_id: str) -> Path | None: home = Path(os.environ.get("KIMI_CODE_HOME", Path.home() / ".kimi-code")).expanduser() found = None try: with (home / "session_index.jsonl").open(encoding="utf-8") as index: for line in index: try: entry = json.loads(line) except json.JSONDecodeError: continue if entry.get("sessionId") == session_id and isinstance(entry.get("sessionDir"), str): found = Path(entry["sessionDir"]) except OSError: pass if found: return found if not session_id or Path(session_id).name != session_id: return None return next((path for path in (home / "sessions").glob(f"*/{session_id}") if path.is_dir()), None) def _last_assistant_message(transcript: Path) -> str: message = "" step_id = None parts = [] try: lines = transcript.read_text(encoding="utf-8").splitlines() except OSError: return "" for line in lines: try: record = json.loads(line) except json.JSONDecodeError: continue if record.get("type") != "context.append_loop_event" or not isinstance(record.get("event"), dict): continue event = record["event"] if event.get("type") == "step.begin": step_id = event.get("uuid") parts = [] elif event.get("type") == "content.part" and event.get("stepUuid") == step_id: part = event.get("part") if isinstance(part, dict) and part.get("type") == "text" and isinstance(part.get("text"), str): parts.append(part["text"]) elif event.get("type") == "step.end" and event.get("uuid") == step_id: if event.get("finishReason") not in {"error", "interrupted"} and parts: message = "".join(parts) step_id = None parts = [] return message def normalize(payload: dict) -> dict: value = dict(payload) if "tool_output" in value: value.setdefault("tool_response", value["tool_output"]) if "error" in value: value.setdefault("tool_response", value["error"]) if "response" in value: value.setdefault("last_assistant_message", value["response"]) if value.get("hook_event_name") == "Stop": session = _session_dir(str(value.get("session_id") or "")) transcript = session / "agents" / "main" / "wire.jsonl" if session else None if transcript and transcript.is_file(): value.setdefault("transcript_path", str(transcript)) if message := _last_assistant_message(transcript): value.setdefault("last_assistant_message", message) return value ``` Sources: [integrations/kimi-plugin/hooks/adapter.py:22-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L22-L100) The Cursor adapter translates camelCase native events into kebab-case actions, standardizing session identification across `conversation_id` and `parent_conversation_id`, mapping `workspace_roots` to working directories (`cwd`), and normalizing `tool_output` or `error_message` into `tool_response`. ```python EVENTS = { "sessionStart": "session-start", "beforeSubmitPrompt": "user-prompt", "postToolUse": "post-tool", "postToolUseFailure": "post-tool-failure", "afterAgentResponse": "assistant-stop", "stop": "stop", "sessionEnd": "session-end", "preCompact": "pre-compact", } def normalize(payload: dict, event: str) -> dict: value = dict(payload) value.setdefault("session_id", value.get("conversation_id") or value.get("parent_conversation_id", "")) roots = value.get("workspace_roots") or [] if roots: value.setdefault("cwd", roots[0]) if "tool_output" in value: value.setdefault("tool_response", value["tool_output"]) if "error_message" in value: value.setdefault("tool_response", value["error_message"]) if "text" in value: value.setdefault("last_assistant_message", value["text"]) if "summary" in value: value.setdefault("last_assistant_message", value["summary"]) return {"action": EVENTS[event], "payload": value} ``` Sources: [integrations/cursor-plugin/hooks/adapter.py:25-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L25-L51) The Antigravity adapter reads structured step transcripts, filtering entries where `status` equals `DONE` and `type` is `USER_INPUT` or `PLANNER_RESPONSE`, extracting redacted content inside `` tags for prompts and plain text for assistant planner responses. ```python def _subagent_start(store, hook_input): context = record_subagent_start(store, hook_input) if context: return { "hookSpecificOutput": { "hookEventName": "SubagentStart", "additionalContext": context, } } def _subagent_stop(store, hook_input): record_subagent_stop(store, hook_input) def _post_tool(store, payload): response = payload.get("tool_response") failed = None if isinstance(response, dict): exit_code = response.get("exit_code") if isinstance(exit_code, int) and not isinstance(exit_code, bool): failed = exit_code != 0 elif isinstance(response.get("isError"), bool): failed = response["isError"] elif isinstance(response.get("success"), bool): failed = not response["success"] if failed: payload = {**payload, "error": response} record_tool(store, payload, failed=failed) ``` Sources: [integrations/antigravity-plugin/hooks/adapter.py:28-48](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks/adapter.py#L28-L48), [integrations/codex-plugin/hooks/adapter.py:27-56](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L27-L56) > [!NOTE] > The Codex adapter intercepts `post-tool` arguments, normalizing tool responses that return dictionaries with `exit_code`, `isError`, or `success` keys into explicit failure flags before passing payloads to `record_tool`. Sources: [integrations/codex-plugin/hooks/adapter.py:42-56](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L42-L56) ### Event Routing and Field Mapping Matrix | IDE Host | Native Event / Action | Normalized Target Action | Key Field Normalization | | :--- | :--- | :--- | :--- | | Kimi | `SessionStart` | `session-start` | Maps script argument directly | | Kimi | `UserPromptSubmit` | `user-prompt` | Extracts additional context for injection | | Kimi | `Stop` | `stop` | Resolves wire transcript for `last_assistant_message` | | Cursor | `sessionStart` | `session-start` | Sets `session_id` from conversation identifiers | | Cursor | `beforeSubmitPrompt` | `user-prompt` | Normalizes `workspace_roots[0]` to `cwd` | | Cursor | `afterAgentResponse` | `assistant-stop` | Maps `text` or `summary` to `last_assistant_message` | | Antigravity | `PreInvocation` | `session-start` / `user-prompt` | Parses `transcriptPath` for user prompt and assistant text | | Antigravity | `PostToolUse` | `post-tool` / `post-tool-failure` | Unpacks `toolCall` name and args | | Codex | `post-tool` | `codex-post-tool` | Evaluates response exit codes and error flags | Sources: [integrations/kimi-plugin/hooks/adapter.py:22-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L22-L100), [integrations/cursor-plugin/hooks/adapter.py:25-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L25-L51), [integrations/antigravity-plugin/hooks/adapter.py:78-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks/adapter.py#L78-L97), [integrations/codex-plugin/hooks/adapter.py:42-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L42-L60) > [!WARNING] > If an Antigravity payload lacks a valid working directory (`cwd`), the adapter bypasses runtime execution entirely, returning an empty JSON object or a default allow decision to prevent hook timeouts. Sources: [integrations/antigravity-plugin/hooks/adapter.py:126-131](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks/adapter.py#L126-L131) ## Shared Hook Orchestration Lifecycle ### Input Reading and Default Stops The hook runner reads incoming JSON hook payloads directly from standard input via `read_hook_input()`, safely guarding against decoding errors or missing streams. ```python def read_hook_input() -> dict: try: value = json.load(sys.stdin) return value if isinstance(value, dict) else {} except (json.JSONDecodeError, OSError): return {} ``` Sources: [integrations/cursor-plugin/core/hook_runner.py:44-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L44-L50) When recording agent stops without external transcript parsing, `default_record_stop()` extracts the session identifier, resolves the active repository, redacts the assistant response message, and records it to the evidence store. ```python def default_record_stop(store: EvidenceStore, hook_input: dict): """Record the assistant's response without transcript parsing.""" session_id = _session_id(hook_input) repo = store.repo_for_session(session_id, hook_input.get("cwd")) message = redact(hook_input.get("last_assistant_message", "")).strip() if message: store.record_assistant_response(repo, session_id, message) return repo, session_id ``` Sources: [integrations/cursor-plugin/core/hook_runner.py:52-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L52-L60) ### First-Prompt Memory Search Before an agent processes the initial user prompt in a session, `first_prompt_memory_output()` executes a conditional search over historical repository memories. ```python def first_prompt_memory_output(store: EvidenceStore, hook_input: dict) -> dict: """Search once before the agent handles the first prompt in a session.""" repo, session_id, prompt, is_first_prompt = record_user_prompt(store, hook_input) if not is_first_prompt: return {} try: minimum_query_chars = int(os.environ.get("MEM0_CODE_MIN_QUERY_CHARS", "20")) except ValueError: minimum_query_chars = 20 if len(prompt.strip()) < max(minimum_query_chars, 1): return {} result = search_memories( store, repo, session_id, bounded(prompt, 6000), top_k=5, operation="first-prompt-search", timeout=2, ) if not result.memories: return {} context = format_context( result.memories, "Mem0 found these relevant memories from earlier work in this repository:", ) telemetry.record( "context_injected", repo=repo, session_id=session_id, trigger="first-prompt", memory_count=len(result.memories), context_chars=len(context), prompt_chars=len(prompt), ) return { "hookSpecificOutput": { "hookEventName": "UserPromptSubmit", "additionalContext": context, }, } ``` Sources: [integrations/cursor-plugin/core/hook_runner.py:62-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L62-L94) > [!NOTE] > The first-prompt search strictly requires `is_first_prompt` to be true and enforces a minimum query length derived from `MEM0_CODE_MIN_QUERY_CHARS` (defaulting to 20 characters) before querying stored memories with a timeout of 2 seconds. Sources: [integrations/cursor-plugin/core/hook_runner.py:64-76](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L64-L76) ### Periodic Checkpoint Scheduling Periodic flushing and background process handoffs coordinate asynchronous state persistence during active sessions. ```python def schedule_periodic_checkpoint( store: EvidenceStore, hook_input: dict, repo, session_id: str, ) -> bool: if ( not automatic_flush_enabled() or not api_key() or not store.checkpoint_due(repo.identity, session_id) ): return False if store.prepare_flush(repo, session_id, "periodic") is None: return False hand_off_flush(hook_input, "periodic") return True ``` Sources: [integrations/cursor-plugin/core/hook_runner.py:199-211](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L199-L211) > [!WARNING] > Periodic checkpoint scheduling is conditional: it requires automatic flushing to be enabled via environment configuration, an active API key to be present, and a checkpoint to be explicitly due for the session identity. Sources: [integrations/cursor-plugin/core/hook_runner.py:202-206](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L202-L206) ### Lifecycle Configuration Constants | Constant Name | Default Value | Purpose | | :--- | :--- | :--- | | `STALE_RUNNING_SECONDS` | `300` | Threshold in seconds before a running handoff lock is considered stale | | `PENDING_EXPIRY_SECONDS` | `604800` (7 days) | Maximum retention age for pending handoff payload files | | `PENDING_LAUNCH_LIMIT` | `5` | Maximum number of recovered handoffs launched in a single batch | | `DEFAULT_IDLE_FLUSH_SECONDS` | `300` | Default idle duration required before triggering an automatic background flush | Sources: [integrations/cursor-plugin/core/hook_runner.py:36-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L36-L39) ## Host Identity and Bundle Generation ### Harness Identity Constants Generated harness identity modules establish host-specific constants distinguishing IDE environments within shared agent core distributions. Each target plugin maintains an auto-generated configuration mapping its harness identifier, source tag, and platform application vocabulary. | Plugin Integration | `HARNESS_ID` | `SOURCE_TAG` | `PLATFORM_SOURCE` | `PLATFORM_APPLICATION` | | :--- | :--- | :--- | :--- | :--- | | **Antigravity** | `"antigravity"` | `"ANTIGRAVITY_PLUGIN"` | `"MEM0_PLUGIN"` | `"antigravity"` | | **Kimi** | `"kimi"` | `"KIMI_PLUGIN"` | `"MEM0_PLUGIN"` | `"kimi"` | | **Cursor** | `"cursor"` | `"CURSOR_PLUGIN"` | `"MEM0_PLUGIN"` | `"cursor"` | Sources: [integrations/antigravity-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/_harness_id.py#L3-L11), [integrations/kimi-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/_harness_id.py#L3-L11), [integrations/cursor-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/_harness_id.py#L3-L11) > [!NOTE] > The platform-side vocabulary treats the entire plugin family as a single source (`MEM0_PLUGIN`), while `PLATFORM_APPLICATION` identifies the specific editor host context. If the host application is unknown or empty, `memory_core` omits the application header entirely rather than transmitting a placeholder value. Sources: [integrations/antigravity-plugin/core/_harness_id.py:6-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/_harness_id.py#L6-L11), [integrations/kimi-plugin/core/_harness_id.py:6-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/_harness_id.py#L6-L11), [integrations/cursor-plugin/core/_harness_id.py:6-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/_harness_id.py#L6-L11) ## Host Memory State and Ephemeral Storage ### API Key and Data Directory Resolution Host plugins manage configuration state and caching via environment variables and local files. The API key caching subsystem inspects multiple fallback locations to locate credentials, and clears cached token files when all configuration sources are exhausted. ```python """Drop the cached key file once every configured key source is gone.""" configured = ( os.environ.get("MEM0_API_KEY") or os.environ.get("PLUGIN_OPTION_API_KEY") or os.environ.get("CLAUDE_PLUGIN_OPTION_API_KEY") or os.environ.get("CLAUDE_PLUGIN_OPTION_MEM0_API_KEY") or "" ).strip() if configured: return False path = data_dir() / "api-key" if not path.exists(): return False try: path.unlink() except OSError: return False return True ``` Sources: [integrations/codex-plugin/core/memory_core.py:431-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L431-L448), [integrations/cursor-plugin/core/memory_core.py:431-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L431-L448), [integrations/antigravity-plugin/core/memory_core.py:431-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L431-L448), [integrations/kimi-plugin/core/memory_core.py:431-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L431-L448) > [!WARNING] > The credential removal utility `clear_stale_api_key_cache` verifies that every environment configuration vector (`MEM0_API_KEY`, `PLUGIN_OPTION_API_KEY`, `CLAUDE_PLUGIN_OPTION_API_KEY`, and `CLAUDE_PLUGIN_OPTION_MEM0_API_KEY`) is completely unset before unlinking the local cached `api-key` file. Sources: [integrations/codex-plugin/core/memory_core.py:432-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L432-L448), [integrations/cursor-plugin/core/memory_core.py:432-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L432-L448), [integrations/antigravity-plugin/core/memory_core.py:432-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L432-L448), [integrations/kimi-plugin/core/memory_core.py:432-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L432-L448) Storage paths and scoping parameters rely on environment overrides with explicit fallback precedence. | Option Function | Primary Variable | Fallback Variables | Default Value / Pathing | | :--- | :--- | :--- | :--- | | `data_dir()` | `MEM0_CODE_DATA_DIR` | `MEM0_PLUGIN_DATA_DIR`, `PLUGIN_DATA`, `CLAUDE_PLUGIN_DATA` | `~/.mem0/{_harness_data_dir_name}` | | `user_id()` | `PLUGIN_OPTION_USER_ID`, `MEM0_CODE_USER_ID` | `MEM0_USER_ID`, `MEM0_RESOLVED_USER_ID`, `USER`, `USERNAME` | `"default"` | Sources: [integrations/codex-plugin/core/memory_core.py:470-490](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L470-L490), [integrations/cursor-plugin/core/memory_core.py:470-490](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L470-L490), [integrations/antigravity-plugin/core/memory_core.py:470-490](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L470-L490), [integrations/kimi-plugin/core/memory_core.py:470-490](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L470-L490) ### SQLite Evidence Store and Checkpointing Local persistence relies on an SQLite-backed `EvidenceStore` configured with write-ahead logging and busy timeout protections. ```python class EvidenceStore: def __init__(self, path: Path | None = None): directory = data_dir() if path is None else path.parent directory.mkdir(parents=True, exist_ok=True) self.path = path or directory / "evidence.sqlite3" try: self._open() except sqlite3.DatabaseError: self._quarantine() self._open() def _open(self) -> None: self.conn = sqlite3.connect(self.path, timeout=10) self.conn.row_factory = sqlite3.Row try: self.conn.execute("PRAGMA journal_mode=WAL") self.conn.execute("PRAGMA busy_timeout=10000") self._migrate() except sqlite3.DatabaseError: self.conn.close() raise ``` Sources: [integrations/codex-plugin/core/memory_core.py:555-575](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L555-L575), [integrations/cursor-plugin/core/memory_core.py:555-575](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L555-L575), [integrations/antigravity-plugin/core/memory_core.py:555-575](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L555-L575), [integrations/kimi-plugin/core/memory_core.py:555-575](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L555-L575) > [!NOTE] > If a database error occurs during initialization, the `EvidenceStore` automatically quarantines the corrupted database file before attempting to recreate and reopen a clean SQLite store. Sources: [integrations/codex-plugin/core/memory_core.py:560-564](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L560-L564), [integrations/cursor-plugin/core/memory_core.py:560-564](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L560-L564), [integrations/antigravity-plugin/core/memory_core.py:560-564](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L560-L564), [integrations/kimi-plugin/core/memory_core.py:560-564](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L560-L564) Transcript events are processed to extract checkpoint text payloads through `_checkpoint_message`, which handles user prompts, assistant stops, and subagent or sidekick termination messages while applying redaction. ```python def _checkpoint_message(event: dict[str, Any]) -> str: kind = event.get("kind") payload = event.get("payload") or {} if kind == "user_prompt": return redact(payload.get("text", "")).strip() if kind == "assistant_stop": transcript_messages = payload.get("transcript_messages") or [] if isinstance(transcript_messages, list): text = "\n".join( str(message.get("content") or "") for message in transcript_messages if isinstance(message, dict) and message.get("content") ) if text: return text return redact(payload.get("text", "")).strip() if kind in {"subagent_stop", "sidekick_stop"}: return redact(payload.get("final_message", "")).strip() return "" ``` Sources: [integrations/codex-plugin/core/memory_core.py:509-527](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L509-L527), [integrations/cursor-plugin/core/memory_core.py:509-527](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L509-L527), [integrations/antigravity-plugin/core/memory_core.py:509-527](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L509-L527), [integrations/kimi-plugin/core/memory_core.py:509-527](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L509-L527) ## Related - [Python Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core) - [Claude Code Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin) --- ## Technical docs: OpenClaw Integration URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/openclaw-integration
Relevant source files The following files were used as context for generating this wiki page: - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [integrations/openclaw/cli/commands.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts) - [integrations/openclaw/tools/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/index.ts) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/openclaw/tools/memory-list.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-list.ts) - [integrations/pi-agent-plugin/src/memory/tools.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts) - [integrations/openclaw/tools/memory-delete.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-delete.ts) - [integrations/openclaw/tools/memory-get.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-get.ts) - [integrations/openclaw/tools/memory-update.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-update.ts) - [integrations/openclaw/openclaw.plugin.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/openclaw.plugin.json) - [integrations/openclaw/tools/memory-add.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-add.ts) - [integrations/claude-code-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_cli.py) - [integrations/openclaw/tools/memory-search.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-search.ts) - [integrations/openclaw/providers.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/providers.ts) - [integrations/openclaw/tools/memory-event-list.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-event-list.ts) - [integrations/openclaw/isolation.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/isolation.ts) - [integrations/openclaw/openclaw-plugin-sdk.d.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/openclaw-plugin-sdk.d.ts) - [integrations/deepseek-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts) - [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts) - [integrations/kimi-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_cli.py) - [integrations/codex-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_cli.py) - [integrations/antigravity-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_cli.py) - [integrations/openclaw/cli/config-file.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/config-file.ts) - [integrations/openclaw/cli/oss-wizard.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts) - [integrations/openclaw/recall.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts) - [integrations/openclaw/fs-safe.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/fs-safe.ts)
## Overview The OpenClaw Integration binds Mem0 memory management into the OpenClaw agent runtime, supporting both cloud-hosted Mem0 platform backends and self-hosted open-source deployments. It solves the problem of stateless agent turns by providing automated context injection and fact extraction alongside granular tool execution. Key design decisions include per-agent session namespacing to isolate state, token-budgeted and category-ranked recall engines, and robust CLI authentication helpers that persist credentials safely within local configuration files. Sources: [integrations/openclaw/openclaw.plugin.json:4-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/openclaw.plugin.json#L4-L5), [integrations/openclaw/index.ts:90-105](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L90-L105), [integrations/openclaw/recall.ts:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts#L1-L10) ## Plugin Lifecycle and Hook Registration The `memoryPlugin` entry point orchestrates plugin bootstrapping, configuration validation, and backend initialization within the OpenClaw execution environment. Upon registration through `definePluginEntry`, the `register` method initializes telemetry flags and reads persistent authentication state from configuration artifacts. It validates settings using `mem0ConfigSchema` against `api.pluginConfig` combined with file-based credentials, determining whether the host runs in `cli-metadata` registration mode or standard execution. ```mermaid flowchart TD Start([register API]) --> Bootstrap[bootstrapTelemetryFlag] Bootstrap --> ReadAuth[readPluginAuth] ReadAuth --> ParseCfg[mem0ConfigSchema.parse] ParseCfg --> CheckMode{registrationMode === cli-metadata?} CheckMode -- Yes --> RegCLI[registerCliCommands only] CheckMode -- No --> CheckSetup{cfg.needsSetup?} CheckSetup -- Yes --> Warn[api.logger.warn missing API key] Warn --> RegCLIInit[registerCLICommands with nulls] RegCLIInit --> RegSvc[api.registerService waiting state] CheckSetup -- No --> Prov[createProvider cfg, api] Prov --> Life[createMemoryLifecycle beginSession] Life --> BackendChoice{cfg.mode === platform?} BackendChoice -- Yes --> Plat[PlatformBackend init] BackendChoice -- No --> OSS[providerToBackend adapter] Plat --> Tools[registerAllTools & hooks] OSS --> Tools Tools --> End([Ready]) ``` Sources: [integrations/openclaw/index.ts:90-184](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L90-L184) > [!WARNING] > If `cfg.needsSetup` evaluates to true due to a missing API key, full memory capabilities are disabled, logging a warning instructing operators to run `openclaw mem0 init`. Even in this unauthenticated state, the plugin registers CLI commands and a waiting service so the initialization flow remains accessible. Sources: [integrations/openclaw/index.ts:137-167](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L137-L167) When fully configured, `createProvider` instantiates either a `PlatformProvider` for cloud-hosted backends or an `OSSProvider` for self-hosted instances. The lifecycle manager begins a new session, and a `Backend` instance is selected: `PlatformBackend` is assigned when `cfg.mode === "platform"`, while `providerToBackend` wraps local providers for open-source mode. ```typescript const provider = createProvider(cfg, api); const lifecycle = createMemoryLifecycle(); lifecycle.beginSession(); // Create Backend instance — PlatformBackend for platform mode, providerToBackend adapter for OSS let backend: Backend; if (cfg.mode === "platform") { backend = new PlatformBackend({ apiKey: cfg.apiKey!, baseUrl: cfg.baseUrl ?? "https://api.mem0.ai", }); } else { backend = providerToBackend(provider, cfg.userId); } ``` Sources: [integrations/openclaw/index.ts:170-184](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L170-L184), [integrations/openclaw/providers.ts:530-584](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/providers.ts#L530-L584) ## Per-Agent Isolation and Scoping ### Session Trigger Filtering and Subagent Detection Multi-agent memory boundary enforcement relies on deriving structured user identifiers from session keys, ensuring distinct agent workspaces do not pollute shared storage namespaces. The isolation engine processes OpenClaw session keys to detect system automation triggers, identify ephemeral subagent runs, and extract specific agent identifiers. System-initiated execution loops such as cron jobs, heartbeats, automation pipelines, and schedules are filtered out using `isNonInteractiveTrigger` to prevent automated prompts from contaminating long-term user memory stores. This function checks explicit trigger names against the `SKIP_TRIGGERS` set and falls back to inspecting the session key pattern for `:cron:` or `:heartbeat:` tokens. ```typescript */ const SKIP_TRIGGERS = new Set(["cron", "heartbeat", "automation", "schedule"]); /** * Returns true if the session trigger is non-interactive and memory * hooks should be skipped entirely. * * Also detects cron-style session keys (e.g. "agent:main:cron:") * as a fallback when the trigger field is not set. */ export function isNonInteractiveTrigger( trigger: string | undefined, sessionKey: string | undefined, ): boolean { if (trigger && SKIP_TRIGGERS.has(trigger.toLowerCase())) return true; // Fallback: detect cron/heartbeat from the session key pattern if (sessionKey) { if (/:cron:/i.test(sessionKey) || /:heartbeat:/i.test(sessionKey)) return true; } return false; } ``` Sources: [integrations/openclaw/isolation.ts:16-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/isolation.ts#L16-L39) > [!WARNING] > Ephemeral subagent sessions identified by `isSubagentSession` through the `:subagent:` pattern evaluate to empty namespaces during memory recall and leave orphaned data after capture because their random UUIDs change with every spawn. Sources: [integrations/openclaw/isolation.ts:46-49](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/isolation.ts#L46-L49) ### User Identification Resolution and Session Parsing Session keys follow specific formats that determine how the agent extracts isolation boundaries. The `extractAgentId` function parses subagent UUIDs, named agent identifiers, and primary main sessions. | Session Key Pattern | Example Format | Extracted Agent ID Result | | :--- | :--- | :--- | | Main Agent Session | `agent:main:main` | `undefined` (falls back to base user ID) | | Subagent Session | `agent:main:subagent:d3b07384` | `subagent-d3b07384` | | Named Agent Session | `agent:researcher:main` | `researcher` | Sources: [integrations/openclaw/isolation.ts:53-77](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/isolation.ts#L53-L77) The resolution hierarchy implemented in `resolveUserId` enforces priority rules when determining the active namespace for memory reads and writes. Explicit parameter overrides take precedence over session context, which in turn overrides base configuration settings. ```mermaid flowchart TD Start([resolveUserId params]) --> CheckAgent{opts.agentId present?} CheckAgent -- Yes --> ResAgent[Return agentUserId base, opts.agentId] CheckAgent -- No --> CheckUser{opts.userId present?} CheckUser -- Yes --> ResUser[Return opts.userId] CheckUser -- No --> CheckSession{currentSessionId present?} CheckSession -- Yes --> ResEffective[Return effectiveUserId base, currentSessionId] CheckSession -- No --> ResBase[Return baseUserId] ``` Sources: [integrations/openclaw/isolation.ts:98-107](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/isolation.ts#L98-L107) ## Memory Tool Execution and CRUD ### Tool Registration and Execution Lifecycle The plugin exposes a comprehensive set of capabilities for managing stored memories through the OpenClaw tool registration framework. The `registerAllTools` function registers eight distinct tool handlers using the plugin API with `optional: false` settings, ensuring that memory management tools are always available during agent execution loops. ```typescript export function registerAllTools(deps: ToolDeps): void { const { api } = deps; const nonOptional = { optional: false }; api.registerTool(createMemorySearchTool(deps), nonOptional); api.registerTool(createMemoryAddTool(deps), nonOptional); api.registerTool(createMemoryGetTool(deps), nonOptional); api.registerTool(createMemoryListTool(deps), nonOptional); api.registerTool(createMemoryUpdateTool(deps), nonOptional); api.registerTool(createMemoryDeleteTool(deps), nonOptional); api.registerTool(createMemoryEventListTool(deps), nonOptional); api.registerTool(createMemoryEventStatusTool(deps), nonOptional); } ``` Sources: [integrations/openclaw/tools/index.ts:29-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/index.ts#L29-L41) | Tool Name | Creating Function | Description / Purpose | | :--- | :--- | :--- | | `memory_search` | `createMemorySearchTool` | Retrieves memories relevant to a query using semantic search with rank ordering. | | `memory_add` | `createMemoryAddTool` | Saves preferences, facts, decisions, rules, or technical notes to long-term memory. | | `memory_get` | `createMemoryGetTool` | Fetches a single memory by exact ID to inspect its full content and metadata. | | `memory_list` | `createMemoryListTool` | Lists all stored memories for auditing purposes without requiring a query string. | | `memory_update` | `createMemoryUpdateTool` | Modifies an existing memory's text in-place while preserving its ID and update history. | | `memory_delete` | `createMemoryDeleteTool` | Deletes memories via explicit ID, query search, or bulk deletion (`all: true`). | | `memory_event_list` | `createMemoryEventListTool` | Lists recent background processing events from the platform backend. | | `memory_event_status` | `createMemoryEventStatusTool` | Inspects status and telemetry of specific background memory processing events. | Sources: [integrations/openclaw/tools/index.ts:5-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/index.ts#L5-L12), [integrations/openclaw/tools/memory-list.ts:9-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-list.ts#L9-L11), [integrations/openclaw/tools/memory-add.ts:11-14](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-add.ts#L11-L14), [integrations/openclaw/tools/memory-get.ts:8-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-get.ts#L8-L10), [integrations/openclaw/tools/memory-update.ts:9-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-update.ts#L9-L11), [integrations/openclaw/tools/memory-delete.ts:9-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-delete.ts#L9-L11), [integrations/openclaw/tools/memory-event-list.ts:6-9](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-event-list.ts#L6-L9) ### Search, Retrieval, and Listing Handlers The `memory_search` tool accepts queries, result limits, scoping filters (`session`, `long-term`, `all`), categories, and advanced search metadata filters. When `scope` is set to `session`, it queries specifically against the current session's run ID; when set to `all`, it combines long-term results with session-scoped items while deduplicating records by memory ID. ```typescript if (scope === "session") { if (currentSessionId) { results = await provider.search(query, applyFilters(buildSearchOptions(uid, limit, undefined, currentSessionId))); } } else if (scope === "long-term") { results = await provider.search(query, applyFilters(buildSearchOptions(uid, limit))); } else { const longTerm = await provider.search(query, applyFilters(buildSearchOptions(uid, limit))); let session: MemoryItem[] = []; if (currentSessionId) { session = await provider.search(query, applyFilters(buildSearchOptions(uid, limit, undefined, currentSessionId))); } const seen = new Set(longTerm.map((r) => r.id)); results = [...longTerm, ...session.filter((r) => !seen.has(r.id))]; } ``` Sources: [integrations/openclaw/tools/memory-search.ts:49-63](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-search.ts#L49-L63) The `memory_get` tool calls `provider.get(memoryId)` directly and formats the output with creation and update timestamps, whereas `memory_list` returns an itemized audit listing without semantic scoring. ```typescript try { const memory = await provider.get(memoryId); deps.captureToolEvent("memory_get", { success: true, latency_ms: Date.now() - start }); return { content: [{ type: "text", text: `Memory ${memory.id}:\n${memory.memory}\n\nCreated: ${memory.created_at ?? "unknown"}\nUpdated: ${memory.updated_at ?? "unknown"}` }], details: { memory }, }; ``` Sources: [integrations/openclaw/tools/memory-get.ts:18-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-get.ts#L18-L24), [integrations/openclaw/tools/memory-list.ts:33-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-list.ts#L33-L43) > [!NOTE] > `memory_list` de-duplicates entries when `scope` is `"all"` by collecting long-term results first and filtering out any session-scoped items sharing an existing record ID. Sources: [integrations/openclaw/tools/memory-list.ts:37-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-list.ts#L37-L43) ### Storage, Mutation, and Deletion Safeguards The `memory_add` tool supports single-fact strings or arrays of facts via the `facts` parameter. Before storage, incoming content passes through `stripNoiseFromContent` and `isNoiseMessage` validation filters. ```typescript const allFacts = rawFacts .map((f) => stripNoiseFromContent(f)) .filter((f) => f.length > 0 && !isNoiseMessage(f)); if (allFacts.length === 0) { return { content: [{ type: "text", text: "All provided facts were filtered as noise. Nothing stored." }], details: { error: "all_noise" } }; } ``` Sources: [integrations/openclaw/tools/memory-add.ts:38-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-add.ts#L38-L44) Mutative tools implement strict safety guardrails. Both `memory_update` and `memory_delete` check `isSubagentSession(getCurrentSessionId())` upon execution and immediately reject requests made within ephemeral subagent sessions. ```typescript if (isSubagentSession(getCurrentSessionId())) { return { content: [{ type: "text", text: "Memory update is not available in subagent sessions." }], details: { error: "subagent_blocked" } }; } ``` Sources: [integrations/openclaw/tools/memory-update.ts:21-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-update.ts#L21-L23), [integrations/openclaw/tools/memory-delete.ts:29-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-delete.ts#L29-L31) > [!WARNING] > Bulk memory deletion (`memory_delete` with `all: true`) requires both the `all: true` flag and explicit `confirm: true` parameters to proceed; otherwise, execution aborts and returns an error details payload. Sources: [integrations/openclaw/tools/memory-delete.ts:58-61](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-delete.ts#L58-L61) ## Token-Budgeted Category-Ranked Recall Engine ### Query Sanitization and Vector Retrieval The recall engine processes raw prompt inputs by stripping untrusted sender metadata prefixes and bracketed tags via `sanitizeQuery` before executing search operations. It then queries the memory provider using `SearchOptions` configured with an over-fetched `top_k` value of `maxMemories * 2` and a custom score threshold. ```typescript export function sanitizeQuery(raw: string): string { let cleaned = raw.replace( /Sender\s*\(untrusted metadata\):\s*```json[\s\S]*?```\s*/gi, "", ); cleaned = cleaned.replace(/^\[.*?\]\s*/g, ""); cleaned = cleaned.trim(); return cleaned || raw; } // ============================================================================ // Public API // ============================================================================ /** * Perform token-budgeted, category-ranked recall. */ export async function recall( provider: Mem0Provider, query: string, userId: string, config: SkillsConfig = {}, sessionId?: string, ): Promise { const recallConfig = config.recall ?? {}; const tokenBudget = recallConfig.tokenBudget ?? DEFAULT_TOKEN_BUDGET; const maxMemories = recallConfig.maxMemories ?? DEFAULT_MAX_MEMORIES; const threshold = recallConfig.threshold ?? DEFAULT_THRESHOLD; const categoryOrder = recallConfig.categoryOrder ?? DEFAULT_CATEGORY_ORDER; const identityAlwaysInclude = recallConfig.identityAlwaysInclude !== false; // Build search options (v3.0.0: keyword_search, reranking, filter_memories removed) const searchOpts: SearchOptions = { user_id: userId, top_k: maxMemories * 2, // Over-fetch for ranking threshold, source: "OPENCLAW", }; ``` Sources: [integrations/openclaw/recall.ts:225-263](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts#L225-L263) When a `sessionId` is present, the engine performs a secondary search against session memories using `run_id` filtering, and subsequently deduplicates any items already retrieved in the long-term memory set. ```typescript // Search session memories if we have a session let sessionMemories: MemoryItem[] = []; if (sessionId) { try { sessionMemories = await provider.search(cleanQuery, { ...searchOpts, run_id: sessionId, top_k: 5, }); } catch { // Session search failure is non-critical } } // Deduplicate: session memories that are also in long-term const longTermIds = new Set(longTermMemories.map((m) => m.id)); const uniqueSession = sessionMemories.filter((m) => !longTermIds.has(m.id)); ``` Sources: [integrations/openclaw/recall.ts:279-296](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts#L279-L296) > [!NOTE] > `sanitizeQuery` removes framework noise like sender metadata blocks and timestamps, leaving conversational query rewriting explicitly to the agent layer via its skill protocol. Sources: [integrations/openclaw/recall.ts:219-224](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts#L219-L224) ### Category Ranking and Importance Defaults Retrieved memories are categorized by inspecting item metadata categories or `mem0` extraction categories, falling back to `"uncategorized"`. Memories are sorted using a multi-tiered comparator that prioritizes category configuration order, falls back to importance scores, and resolves ties using search relevance. ```typescript function getMemoryCategory(memory: MemoryItem): string { // Check metadata first (skill-stored memories have explicit category) if ( memory.metadata?.category && typeof memory.metadata.category === "string" ) { return memory.metadata.category; } // Check categories array (mem0-extracted memories) if (memory.categories?.length) { return memory.categories[0]; } return "uncategorized"; } function getMemoryImportance(memory: MemoryItem): number { if ( memory.metadata?.importance && typeof memory.metadata.importance === "number" ) { return memory.metadata.importance; } // Default importance by category const cat = getMemoryCategory(memory); const defaults: Record = { identity: 0.95, configuration: 0.95, rule: 0.9, preference: 0.85, decision: 0.8, technical: 0.8, relationship: 0.75, project: 0.75, operational: 0.6, }; return defaults[cat] ?? 0.5; } ``` Sources: [integrations/openclaw/recall.ts:57-93](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts#L57-L93) ```typescript function rankMemories( memories: MemoryItem[], categoryOrder: string[], ): MemoryItem[] { const orderMap = new Map(categoryOrder.map((cat, i) => [cat, i])); return [...memories].sort((a, b) => { const catA = getMemoryCategory(a); const catB = getMemoryCategory(b); const orderA = orderMap.get(catA) ?? 999; const orderB = orderMap.get(catB) ?? 999; // Primary sort: category priority if (orderA !== orderB) return orderA - orderB; // Secondary sort: importance (higher first) const impA = getMemoryImportance(a); const impB = getMemoryImportance(b); if (impA !== impB) return impB - impA; // Tertiary sort: search relevance score return (b.score ?? 0) - (a.score ?? 0); }); } ``` Sources: [integrations/openclaw/recall.ts:107-130](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts#L107-L130) | Category Name | Default Importance | Category Priority Index | | :--- | :--- | :--- | | `identity` | `0.95` | `0` (Highest) | | `configuration` | `0.95` | `1` | | `rule` | `0.9` | `2` | | `preference` | `0.85` | `3` | | `decision` | `0.8` | `4` | | `technical` | `0.8` | `5` | | `relationship` | `0.75` | `6` | | `project` | `0.75` | `7` | | `operational` | `0.6` | `8` | Sources: [integrations/openclaw/recall.ts:25-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts#L25-L35), [integrations/openclaw/recall.ts:81-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts#L81-L91) ### Token Budgeting and Formatting The budgeting step iterates through ranked memories to select items under a configured character-derived token estimation (`CHARS_PER_TOKEN = 4`), respecting the `maxMemories` limit and token constraints. ```typescript // Rough token estimate: ~4 chars per token for English text const CHARS_PER_TOKEN = 4; ``` Sources: [integrations/openclaw/recall.ts:37-38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts#L37-L38) ```typescript function budgetMemories( rankedMemories: MemoryItem[], tokenBudget: number, maxMemories: number, identityAlwaysInclude: boolean, ): MemoryItem[] { const selected: MemoryItem[] = []; let usedTokens = 0; for (const memory of rankedMemories) { if (selected.length >= maxMemories) break; const memTokens = estimateTokens(memory.memory); const isIdentity = getMemoryCategory(memory) === "identity" || getMemoryCategory(memory) === "configuration"; // Identity/config always included if flag is set if (identityAlwaysInclude && isIdentity) { selected.push(memory); usedTokens += memTokens; continue; } // Budget check for non-identity memories if (usedTokens + memTokens > tokenBudget) continue; selected.push(memory); usedTokens += memTokens; } return selected; } ``` Sources: [integrations/openclaw/recall.ts:136-168](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts#L136-L168) > [!WARNING] > When `identityAlwaysInclude` is enabled (default), items categorized under `identity` or `configuration` bypass the token budget ceiling and are unconditionally forced into the selected context set. Sources: [integrations/openclaw/recall.ts:149-158](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts#L149-L158), [integrations/openclaw/recall.ts:254](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts#L254) ## CLI Subcommands and Authentication Helpers ### Command Subsections and Configuration Persistence The OpenClaw CLI registers subcommands under `openclaw mem0` to handle memory operations and plugin management. Management tasks include platform authentication (`init`), connectivity checks (`status`), and configuration queries (`config show`, `config get`, `config set`, `event list`, `event status`). ```typescript * Registers all `openclaw mem0 ` commands: * * Memory: * - add : Add a memory from text (--user-id, --agent-id) * - search : Search memories (--top-k, --scope, --agent-id, --user-id) * - get : Get a specific memory by ID * - list : List memories with optional filters (--user-id, --agent-id, --top-k) * - update : Update a memory's text * - delete : Delete a memory or all memories (--all, --confirm) * - import : Import memories from a JSON file * * Management: * - init : Authenticate with Mem0 Platform (email or API key) * - status : Check API connectivity and show current config * - config show : Display current plugin configuration * - config get : Get a single config value * - config set : Update a plugin config field * - event list : List recent background events * - event status: Get status of a specific event * ``` Sources: [integrations/openclaw/cli/commands.ts:4-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts#L4-L23) Configuration settings and authentication credentials are saved in `~/.openclaw/openclaw.json` under `plugins.entries.openclaw-mem0.config`. The filesystem operations rely on safe wrappers in `fs-safe.ts` to isolate file I/O from network calls and comply with security scanners. ```typescript * Plugin auth and settings are stored in ~/.openclaw/openclaw.json under * plugins.entries.openclaw-mem0.config — the single source of truth. * * Uses fs-safe.ts for all filesystem operations to pass the OpenClaw * code_safety scanner. */ ``` Sources: [integrations/openclaw/cli/config-file.ts:4-9](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/config-file.ts#L4-L9) | Config Field | Type | Default Value | Purpose | | :--- | :--- | :--- | :--- | | `apiKey` | `string` | `undefined` | Platform authentication token | | `baseUrl` | `string` | `https://api.mem0.ai` | Mem0 API base URL | | `userId` | `string` | `undefined` | Default user identity scope | | `userEmail` | `string` | `undefined` | Authenticated user email address | | `mode` | `string` | `undefined` | Operating mode for the plugin | | `autoRecall` | `boolean` | `undefined` | Automatically inject memories into prompts | | `autoCapture` | `boolean` | `undefined` | Automatically extract and save memories | | `topK` | `number` | `undefined` | Default retrieval count for searches | | `anonymousTelemetryId` | `string` | `undefined` | Telemetry tracking identifier | | `keyFingerprint` | `string` | `undefined` | SHA-256 prefix of the active API key | Sources: [integrations/openclaw/cli/config-file.ts:28-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/config-file.ts#L28-L40), [integrations/openclaw/cli/config-file.ts:19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/config-file.ts#L19) > [!WARNING] > `readFullConfig` throws an error on JSON parse failures. This fail-closed behavior prevents `writeFullConfig` from overwriting a corrupted config file with an empty state. Sources: [integrations/openclaw/cli/config-file.ts:50-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/config-file.ts#L50-L51), [integrations/openclaw/cli/config-file.ts:70-77](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/config-file.ts#L70-L77) ### Credential Resolution and Execution Trace When executing commands, configuration values and authentication states are resolved through a sequence of file checks. The execution walkthrough follows the exact verified call chain `registerCliCommands` → `readPluginAuth` → `readFullConfig` → `exists`. 1. `registerCliCommands` starts command setup in `integrations/openclaw/cli/commands.ts`. Sources: [integrations/openclaw/cli/commands.ts:398](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts#L398) 2. `readPluginAuth` is called in `integrations/openclaw/cli/config-file.ts` to load stored credentials. Sources: [integrations/openclaw/cli/config-file.ts:126-144](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/config-file.ts#L126-L144) 3. `readFullConfig` is invoked to read the persistent JSON config store. Sources: [integrations/openclaw/cli/config-file.ts:52-78](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/config-file.ts#L52-L78) 4. `exists` checks whether `~/.openclaw/openclaw.json` exists on disk via `fs-safe.ts`. Sources: [integrations/openclaw/fs-safe.ts:15-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/fs-safe.ts#L15-L17) Similarly, when reading configuration text, the call trace flows from `registerCliCommands` → `readPluginAuth` → `readFullConfig` → `readText`: 1. `registerCliCommands` initializes command bindings. Sources: [integrations/openclaw/cli/commands.ts:398](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts#L398) 2. `readPluginAuth` calls `readFullConfig`. Sources: [integrations/openclaw/cli/config-file.ts:126-144](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/config-file.ts#L126-L144) 3. `readFullConfig` calls `readText` to load file data. Sources: [integrations/openclaw/cli/config-file.ts:52-78](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/config-file.ts#L52-L78) 4. `readText` reads the file as UTF-8 text via `fs-safe.ts`. Sources: [integrations/openclaw/fs-safe.ts:10-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/fs-safe.ts#L10-L12) ```mermaid sequenceDiagram autonumber participant commands.ts participant config-file.ts participant fs-safe.ts participant node:fs commands.ts->>config-file.ts: readPluginAuth() config-file.ts->>config-file.ts: readFullConfig() config-file.ts->>fs-safe.ts: exists(OPENCLAW_CONFIG_FILE) fs-safe.ts->>node:fs: fs.existsSync() node:fs--r->fs-safe.ts: boolean fs-safe.ts--r->config-file.ts: boolean config-file.ts->>fs-safe.ts: readText(OPENCLAW_CONFIG_FILE) fs-safe.ts->>node:fs: fs.readFileSync() node:fs--r->fs-safe.ts: string fs-safe.ts--r->config-file.ts: string ``` Sources: [integrations/openclaw/cli/commands.ts:398](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts#L398), [integrations/openclaw/cli/config-file.ts:52-144](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/config-file.ts#L52-L144), [integrations/openclaw/fs-safe.ts:10-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/fs-safe.ts#L10-L17) > [!TIP] > `writeFullConfig` re-reads disk content immediately before writing to deep-merge OpenClaw-managed fields (`installs`, `slots`), preventing concurrent processes from clobbering gateway state. Sources: [integrations/openclaw/cli/config-file.ts:87-116](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/config-file.ts#L87-L116) ## Interactive Open Source Setup Wizard ### OSS Provider Configurations and Builders The OpenClaw setup wizard defines provider definitions, config builders, and flag validation logic in `integrations/openclaw/cli/oss-wizard.ts`. Three distinct categories of providers are supported: LLM providers, embedder providers, and vector store providers. Each provider definition specifies default models, URLs, API key requirements, and environment variables. | Provider Category | Supported Identifiers | Default Model / Port | Key Requirements | | :--- | :--- | :--- | :--- | | LLM | `openai`, `ollama`, `anthropic` | `gpt-5-mini`, `llama3.1:8b`, `claude-sonnet-4-5-20250514` | Requires API key for OpenAI and Anthropic; `ollama` requires a base URL. | | Embedder | `openai`, `ollama` | `text-embedding-3-small`, `nomic-embed-text` | Requires API key for OpenAI; `ollama` defaults to `http://localhost:11434`. | | Vector Store | `qdrant`, `pgvector` | Ports `6333` (Qdrant) and `5432` (PostgreSQL) | `qdrant` configures `onDisk: true`; `pgvector` requires user, password, and database name. | Sources: [integrations/openclaw/cli/oss-wizard.ts:25-52](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts#L25-L52) Config builders process user inputs or command-line flags to assemble configuration objects. `buildOssLlmConfig` validates the provider ID against `LLM_PROVIDERS` and returns the provider name alongside a config record containing the selected model and optional API key or Ollama URL. Sources: [integrations/openclaw/cli/oss-wizard.ts:78-93](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts#L78-L93) ```typescript export function buildOssLlmConfig( providerId: string, input: LlmConfigInput, ): { provider: string; config: Record } { const def = LLM_PROVIDERS.find((p) => p.id === providerId); if (!def) throw new Error(`Unknown LLM provider: ${providerId}`); const config: Record = { model: input.model || def.defaultModel, }; if (input.apiKey) config.apiKey = input.apiKey; if (providerId === "ollama") { config.url = input.url || def.defaultUrl; } return { provider: providerId, config }; } ``` Sources: [integrations/openclaw/cli/oss-wizard.ts:78-93](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts#L78-L93) ### Embedding Dimension Calculation and Vector Connectivity Embedding dimensions are resolved dynamically using `KNOWN_EMBEDDER_DIMS` or fall back to the provider default dimensions. The collection name is derived using `collectionNameForDims`, which formats the dimension count into a standard string. Sources: [integrations/openclaw/cli/oss-wizard.ts:54-66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts#L54-L66) ```typescript export const KNOWN_EMBEDDER_DIMS: Record = { "text-embedding-3-small": 1536, "text-embedding-3-large": 3072, "text-embedding-ada-002": 1536, "nomic-embed-text": 768, "mxbai-embed-large": 1024, "all-minilm": 384, "snowflake-arctic-embed": 1024, }; export function collectionNameForDims(dims: number): string { return `mem0_${dims}d`; } ``` Sources: [integrations/openclaw/cli/oss-wizard.ts:54-66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts#L54-L66) `buildOssVectorConfig` assigns vector store parameters, setting `dimension`, `embeddingModelDims`, and the computed `collectionName` when dimensions are provided. Sources: [integrations/openclaw/cli/oss-wizard.ts:131-155](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts#L131-L155) ```typescript export function buildOssVectorConfig( providerId: string, input: VectorConfigInput, ): { provider: string; config: Record } { const config: Record = {}; if (providerId === "qdrant") { config.url = input.url || "http://localhost:6333"; config.onDisk = true; if (input.apiKey) config.apiKey = input.apiKey; } else if (providerId === "pgvector") { config.host = input.host || "localhost"; config.port = parseInt(input.port || "5432", 10); if (input.user) config.user = input.user; if (input.password) config.password = input.password; config.dbname = input.dbname || "postgres"; } if (input.dims) { config.dimension = input.dims; config.embeddingModelDims = input.dims; config.collectionName = collectionNameForDims(input.dims); } return { provider: providerId, config }; } ``` Sources: [integrations/openclaw/cli/oss-wizard.ts:131-155](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts#L131-L155) Connectivity verification functions test network reachability with a 3000ms timeout. `checkOllamaConnectivity` pings `/api/tags`, `checkQdrantConnectivity` requests `/healthz`, and `checkPgConnectivity` attempts a TCP socket connection using `node:net`. Sources: [integrations/openclaw/cli/oss-wizard.ts:157-186](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts#L157-L186) ```typescript export async function checkOllamaConnectivity(url: string): Promise<{ ok: boolean; error?: string }> { try { const resp = await fetch(`${url.replace(/\/+$/, "")}/api/tags`, { signal: AbortSignal.timeout(3000) }); if (resp.ok) return { ok: true }; return { ok: false, error: `Ollama returned HTTP ${resp.status}` }; } catch { return { ok: false, error: `Cannot reach Ollama at ${url}. Install: https://ollama.com/download` }; } } export async function checkPgConnectivity(host: string, port: number): Promise<{ ok: boolean; error?: string }> { return new Promise((resolve) => { import("node:net").then(({ createConnection }) => { const sock = createConnection({ host, port, timeout: 3000 }); sock.once("connect", () => { sock.destroy(); resolve({ ok: true }); }); sock.once("timeout", () => { sock.destroy(); resolve({ ok: false, error: `PostgreSQL not reachable at ${host}:${port}` }); }); sock.once("error", () => { sock.destroy(); resolve({ ok: false, error: `PostgreSQL not reachable at ${host}:${port}. Ensure PostgreSQL with pgvector extension is running.` }); }); }); }); } export async function checkQdrantConnectivity(url: string): Promise<{ ok: boolean; error?: string }> { try { const resp = await fetch(`${url.replace(/\/+$/, "")}/healthz`, { signal: AbortSignal.timeout(3000) }); if (resp.ok) return { ok: true }; return { ok: false, error: `Qdrant returned HTTP ${resp.status}` }; } catch (err) { return { ok: false, error: `Cannot reach Qdrant at ${url}. Start it with: docker run -d -p 6333:6333 qdrant/qdrant` }; } } ``` Sources: [integrations/openclaw/cli/oss-wizard.ts:157-186](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts#L157-L186) > [!WARNING] > `validateOssFlags` enforces strict parameter dependencies: if an LLM or embedder provider requires an API key (`needsApiKey`), omitting `--oss-llm-key` or `--oss-embedder-key` returns an immediate validation error. Similarly, configuring `pgvector` without specifying `--oss-vector-user` causes validation to fail. Sources: [integrations/openclaw/cli/oss-wizard.ts:211-238](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/oss-wizard.ts#L211-L238) ## Related - [TypeScript Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/typescript-agent-core) - [OpenCode Integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/opencode-integration) --- ## Technical docs: OpenCode Integration URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/opencode-integration
Relevant source files The following files were used as context for generating this wiki page: - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/deepseek-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/pi-agent-plugin/src/memory/tools.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts) - [integrations/codex-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/mcp_server.py) - [integrations/kimi-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/mcp_server.py) - [integrations/antigravity-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py) - [integrations/claude-code-plugin/hooks/hooks.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/hooks/hooks.json) - [integrations/codex-plugin/hooks/hooks.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/hooks.json) - [integrations/claude-code-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py) - [integrations/mem0-agent-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py) - [integrations/agent-plugin-core/python/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py) - [integrations/pi-agent-plugin/src/entry.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/entry.ts) - [integrations/cursor-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/mcp_server.py) - [integrations/opencode-plugin/api-key.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts) - [integrations/opencode-plugin/project.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/project.ts) - [integrations/opencode-plugin/scope.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts)
## Overview The Mem0 OpenCode integration exposes persistent semantic memory directly inside OpenCode agent sessions [integrations/opencode-plugin/opencode-mem0.ts:2-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L2-L5). Operating via the `mem0ai` SDK without requiring a separate Model Context Protocol (MCP) server, it wires memory management into OpenCode plugin hooks to capture and recall facts across sessions [integrations/opencode-plugin/opencode-mem0.ts:2-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L2-L5). Credentials are resolved hierarchically from process environments and user shell profiles, while project identity is derived robustly from Git remotes and repository root paths [integrations/opencode-plugin/opencode-mem0.ts:32-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L32-L51), [integrations/opencode-plugin/api-key.ts:15-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L15-L29). Furthermore, the subsystem normalizes memory scopes and search filters to partition memories by project, session, or global boundaries, and intercepts turn hooks to inject relevant memory contexts into agent prompts [integrations/opencode-plugin/opencode-mem0.ts:17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L17), [integrations/opencode-plugin/scope.ts:12-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L12-L33). Sources: [integrations/opencode-plugin/opencode-mem0.ts:2-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L2-L5), [integrations/opencode-plugin/opencode-mem0.ts:17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L17), [integrations/opencode-plugin/opencode-mem0.ts:32-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L32-L51), [integrations/opencode-plugin/api-key.ts:15-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L15-L29), [integrations/opencode-plugin/scope.ts:12-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L12-L33) ## OpenCode Plugin Architecture and Lifecycle ### Initialization and SDK Wiring The OpenCode plugin acts as the public entry point for memory integration, exporting a standard `Plugin` interface that wires native memory tools backed directly by the `mem0ai` SDK without requiring an intermediate Model Context Protocol server [integrations/opencode-plugin/opencode-mem0.ts:2-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L2-L8). During startup, the plugin resolves ambient environment variables such as `MEM0_USER_ID` and `MEM0_APP_ID`, falling back to operating system username and Git repository metadata when explicit configuration is absent [integrations/opencode-plugin/opencode-mem0.ts:23-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L23-L51). It integrates with the TypeScript agent lifecycle by invoking `createMemoryLifecycle` from the common agent core, binding turn interception hooks, and initializing project-level coding categories against the Mem0 platform [integrations/opencode-plugin/opencode-mem0.ts:20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L20), [integrations/opencode-plugin/opencode-mem0.ts:115-149](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L115-L149). > [!NOTE] > The OpenCode plugin bypasses MCP transport layers entirely, instantiating the `MemoryClient` directly in TypeScript to register native tools for adding, searching, and managing memories [integrations/opencode-plugin/opencode-mem0.ts:4-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L4-L8). Sources: [integrations/opencode-plugin/opencode-mem0.ts:2-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L2-L8), [integrations/opencode-plugin/opencode-mem0.ts:20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L20), [integrations/opencode-plugin/opencode-mem0.ts:23-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L23-L51), [integrations/opencode-plugin/opencode-mem0.ts:115-149](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L115-L149) ### Lifecycle Constants and Matching Patterns The plugin evaluates conversation turns against pre-compiled regular expressions and category fingerprints to determine when to trigger memory capture, project state synchronization, or context resumption [integrations/opencode-plugin/opencode-mem0.ts:98-160](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L98-L160). | Constant Name | Type | Value / Pattern | Purpose | | --- | --- | --- | --- | | `CODING_CATEGORIES` | `string[]` | 17 standard categories (`architecture_decisions`, `api_design`, etc.) | Defines custom project categories synchronized via `autoSetupCategories` [integrations/opencode-plugin/opencode-mem0.ts:98-104](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L98-L104) | | `NUDGE_RE` | `RegExp` | `/\b(remember\s+(this|that)|memorize|save\s+this|...)\b/i` | Detects explicit user instructions to store memories [integrations/opencode-plugin/opencode-mem0.ts:151-152](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L151-L152) | | `RESUME_RE` | `RegExp` | `/where\s+(did\s+)?(we|I)\s+(leave|left)\s+off\|.../i` | Identifies requests to load previous session context or state [integrations/opencode-plugin/opencode-mem0.ts:154-156](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L154-L156) | | `ERROR_STRONG_RE` | `RegExp` | `/Traceback \(most recent call last\)\|panic: \|.../` | Matches critical runtime errors and panics in tool outputs [integrations/opencode-plugin/opencode-mem0.ts:157-158](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L157-L158) | | `WRITE_TOOLS` | `Set` | `["Write", "Edit", "MultiEdit", "write", "edit", "multiEdit"]` | Identifies file-modifying tools that trigger automatic memory flushing [integrations/opencode-plugin/opencode-mem0.ts:160](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L160) | Sources: [integrations/opencode-plugin/opencode-mem0.ts:98-104](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L98-L104), [integrations/opencode-plugin/opencode-mem0.ts:151-160](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L151-L160) ## API Key Resolution and Authentication ### Credential Resolution Strategy The OpenCode plugin resolves API keys hierarchically, checking active runtime environment variables before scanning user shell profile files in the home directory [integrations/opencode-plugin/api-key.ts:15-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L15-L27). The `resolveApiKey` function first inspects `env.MEM0_API_KEY` (defaulting to `process.env`), returning the explicit value immediately if present and non-empty [integrations/opencode-plugin/api-key.ts:15-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L15-L17). When no explicit environment variable is configured, the resolver iterates through a fixed sequence of user profile files located in the user's home directory [integrations/opencode-plugin/api-key.ts:5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L5), [integrations/opencode-plugin/api-key.ts:19-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L19-L27). > [!NOTE] > If all candidate profile files are missing, unreadable, or contain no valid export statements, `resolveApiKey` falls back to returning an empty string [integrations/opencode-plugin/api-key.ts:25-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L25-L29). Sources: [integrations/opencode-plugin/api-key.ts:5-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L5-L29) ### Profile Parsing and Environment Files When inspecting user shell configurations, the resolver evaluates each line of a profile file using `parseApiKeyLine` to extract assignment expressions [integrations/opencode-plugin/api-key.ts:7-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L7-L13), [integrations/opencode-plugin/api-key.ts:19-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L19-L24). The parser matches lines containing optional `export` keywords followed by `MEM0_API_KEY=`, strips inline comments and enclosing single or double quotes, and rejects values that reference unexpanded shell variables beginning with a dollar sign [integrations/opencode-plugin/api-key.ts:7-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L7-L12). | Profile File / Source | Target Variable | Parsing Rules | Fallback Behavior | | --- | --- | --- | --- | | `process.env` | `MEM0_API_KEY` | Direct trimmed string evaluation | Proceeds to filesystem profiles if undefined or empty [integrations/opencode-plugin/api-key.ts:15-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L15-L17) | | `.zshrc` | `MEM0_API_KEY` | Matches `export MEM0_API_KEY=...`, strips `#` comments & quotes [integrations/opencode-plugin/api-key.ts:5-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L5-L13) | Skips file on read errors, continues sequence [integrations/opencode-plugin/api-key.ts:19-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L19-L27) | | `.bashrc` | `MEM0_API_KEY` | Matches `export MEM0_API_KEY=...`, strips `#` comments & quotes [integrations/opencode-plugin/api-key.ts:5-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L5-L13) | Skips file on read errors, continues sequence [integrations/opencode-plugin/api-key.ts:19-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L19-L27) | | `.zprofile` | `MEM0_API_KEY` | Matches `export MEM0_API_KEY=...`, strips `#` comments & quotes [integrations/opencode-plugin/api-key.ts:5-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L5-L13) | Skips file on read errors, continues sequence [integrations/opencode-plugin/api-key.ts:19-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L19-L27) | | `.bash_profile` | `MEM0_API_KEY` | Matches `export MEM0_API_KEY=...`, strips `#` comments & quotes [integrations/opencode-plugin/api-key.ts:5-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L5-L13) | Skips file on read errors, continues sequence [integrations/opencode-plugin/api-key.ts:19-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L19-L27) | | `.profile` | `MEM0_API_KEY` | Matches `export MEM0_API_KEY=...`, strips `#` comments & quotes [integrations/opencode-plugin/api-key.ts:5-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L5-L13) | Returns `""` if exhausted without match [integrations/opencode-plugin/api-key.ts:25-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L25-L29) | Sources: [integrations/opencode-plugin/api-key.ts:5-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/api-key.ts#L5-L29) ## Workspace Project Identity Extraction ### Project Identity Derivation Logic The OpenCode workspace derives its unique project identifier by evaluating runtime environment variables, git repository remotes, and local working directory structures [integrations/opencode-plugin/opencode-mem0.ts:32-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L32-L51). The `getProjectId` function first inspects `process.env.MEM0_APP_ID`, returning this explicit configuration immediately if present [integrations/opencode-plugin/opencode-mem0.ts:33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L33). When no application ID environment variable is defined, the function executes a series of asynchronous shell commands via the tagged template literal `$` to query git state [integrations/opencode-plugin/opencode-mem0.ts:34-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L34-L41). > [!NOTE] > Git remote parsing extracts the repository's owner and repository name, providing an identity that remains stable across clones, worktrees, and custom host aliases [integrations/opencode-plugin/opencode-mem0.ts:34-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L34-L39). ```typescript async function getProjectId($: any): Promise { if (process.env.MEM0_APP_ID) return process.env.MEM0_APP_ID; // Prefer the git remote's owner/repo — stable across clones, worktrees, and // sub-directories (handles https + ssh, incl. custom host aliases). try { const r = await $`git remote get-url origin`.quiet(); const project = parseProjectFromRemote(r.stdout.toString()); if (project) return project; } catch { } // No usable remote: use the git repo ROOT dir name, not cwd (which may be a // sub-directory, or your home dir if OpenCode was launched outside a repo). try { const r = await $`git rev-parse --show-toplevel`.quiet(); const top = r.stdout.toString().trim(); if (top) return basename(top); } catch { } return basename(process.cwd()); } ``` Sources: [integrations/opencode-plugin/opencode-mem0.ts:32-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L32-L51) ### Fallback Resolution and Branch Detection When a git remote is unavailable or parsing fails, `getProjectId` attempts to locate the repository root via `git rev-parse --show-toplevel` and extracts its basename [integrations/opencode-plugin/opencode-mem0.ts:42-49](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L42-L49). This prevents sub-directories or home directory launches from polluting project scopes [integrations/opencode-plugin/opencode-mem0.ts:42-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L42-L43). If all git queries fail, the function falls back to the basename of the current working directory (`process.cwd()`) [integrations/opencode-plugin/opencode-mem0.ts:50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L50). Concurrently, branch identification is handled by `getBranch`, which executes `git branch --show-current` and defaults to `"main"` if the command throws an error or yields an empty string [integrations/opencode-plugin/opencode-mem0.ts:53-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L53-L60). | Resolution Method | Command / Source | Target Property | Fallback Behavior | | --- | --- | --- | --- | | Environment Override | `process.env.MEM0_APP_ID` | Project Identifier | Proceeds to git remote check if undefined [integrations/opencode-plugin/opencode-mem0.ts:33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L33) | | Git Remote Parsing | `git remote get-url origin` | Owner/Repository | Proceeds to repository root discovery on failure [integrations/opencode-plugin/opencode-mem0.ts:37-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L37-L41) | | Repository Root | `git rev-parse --show-toplevel` | Top-level Basename | Falls back to `process.cwd()` basename [integrations/opencode-plugin/opencode-mem0.ts:45-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L45-L51) | | Branch Detection | `git branch --show-current` | Current Git Branch | Defaults to `"main"` if detached or non-git directory [integrations/opencode-plugin/opencode-mem0.ts:55-59](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L55-L59) | ```typescript export { parseProjectFromRemote } from "../agent-plugin-core/typescript/src/identity.ts"; ``` Sources: [integrations/opencode-plugin/opencode-mem0.ts:32-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L32-L60), [integrations/opencode-plugin/project.ts:1-1](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/project.ts#L1-L1) ## Scope Normalization and Search Filtering ### Scoping Parameters and Default Resolution Memory read and write operations are governed by scope configurations that dictate whether memories apply globally, to the current project, or to an active session. The scope subsystem normalizes user preferences and applies runtime parameters using shared utilities from the [TypeScript agent core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/typescript-agent-core) [integrations/opencode-plugin/scope.ts:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L1-L6). The `resolveDefaultScope` function inspects user settings stored in `~/.mem0/settings.json`, falling back to standard scoping rules via `normalizeScope` [integrations/opencode-plugin/opencode-mem0.ts:74-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L74-L96), [integrations/opencode-plugin/scope.ts:37-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L37-L39). Sources: [integrations/opencode-plugin/opencode-mem0.ts:74-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L74-L96), [integrations/opencode-plugin/scope.ts:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L1-L6), [integrations/opencode-plugin/scope.ts:37-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L37-L39) > [!TIP] > The default scope reads fresh from persistent settings on every operation, allowing users to modify their active scope via the `mem0-scope` skill without restarting OpenCode [integrations/opencode-plugin/opencode-mem0.ts:89-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L89-L96). ### Scope Guidance and Filter Adaptation Memory tools rely on explicit scope instructions defined by `SCOPE_GUIDANCE`, which directs agents on when to omit the scope parameter for normal queries, restrict it to the current run using `"session"`, or escalate to `"global"` after user authorization [integrations/opencode-plugin/scope.ts:41-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L41-L42). Search filters and write parameters adapt these scopes into concrete identifiers using `scopeSearchFilters` and `scopeWriteParams` [integrations/opencode-plugin/scope.ts:12-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L12-L33). | Scope Function | Input Parameters | Return Structure | Purpose | | --- | --- | --- | --- | | `scopeSearchFilters` | `scope`, `userId`, `appId`, `runId` | `Record` | Builds search query filters matching the active scope level [integrations/opencode-plugin/scope.ts:12-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L12-L19) | | `scopeWriteParams` | `scope`, `userId`, `appId`, `runId` | `{ user_id: string; app_id?: string; run_id?: string }` | Formats parameters for memory storage operations [integrations/opencode-plugin/scope.ts:21-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L21-L33) | | `asScope` | Raw scope input | `Scope` | Normalizes arbitrary scope strings via core utilities [integrations/opencode-plugin/scope.ts:35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L35) | | `resolveDefaultScope` | `settings` object | `Scope` | Extracts and normalizes default scope from settings file [integrations/opencode-plugin/scope.ts:37-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L37-L39) | ```typescript export function scopeSearchFilters( scope: Scope, userId: string, appId: string, runId: string, ): Record { return sharedSearchFilters(scope, context(userId, appId, runId)); } export function scopeWriteParams( scope: Scope, userId: string, appId: string, runId: string, ): { user_id: string; app_id?: string; run_id?: string } { const values = scopeAddParams(scope, context(userId, appId, runId)); return { user_id: values.userId, ...(values.appId ? { app_id: values.appId } : {}), ...(values.runId ? { run_id: values.runId } : {}), }; } export const asScope = normalizeScope; export function resolveDefaultScope(settings: Record | null | undefined): Scope { return normalizeScope(settings?.default_scope); } ``` Sources: [integrations/opencode-plugin/scope.ts:12-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/scope.ts#L12-L39) ## Turn Hooks and Memory Injection ### Interception Hooks and Message Extraction Conversation streams and agent turns in OpenCode are intercepted through lifecycle hooks that monitor session flow, extract relevant user text, and manage background capture or prompt assembly [integrations/deepseek-plugin/src/index.ts:130-198](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L130-L198). Regular expressions such as `NUDGE_RE` detect explicit user memory directives like "remember this", "memorize", or "don't forget", while `RESUME_RE` identifies patterns for continuing previous work like "where did we leave off" or "catch me up" [integrations/opencode-plugin/opencode-mem0.ts:151-156](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L151-L156). Additionally, error patterns such as `ERROR_STRONG_RE` capture stack traces and compiler panics (`Traceback (most recent call last)`, `panic:`, `FATAL:`, `error[E\d+:]`) to trigger automated memory preservation [integrations/opencode-plugin/opencode-mem0.ts:157-159](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L157-L159). ```typescript const NUDGE_RE = /\b(remember\s+(this|that)|memorize|save\s+this|note\s+(this|that)|don'?t\s+forget|always\s+remember|never\s+forget|keep\s+(this|that)\s+in\s+(mind|memory)|store\s+(this|that))\b/i; const RESUME_RE = /where\s+(did\s+)?(we|I)\s+(leave|left)\s+off|continue\s+(from\s+)?(where|last)|what\s+were\s+we\s+(working|doing)|pick\s+up\s+where|resume\s+(from\s+|where\s+)|what.s\s+the\s+(current|latest)\s+(state|status)|catch\s+me\s+up|where\s+are\s+we/i; const ERROR_STRONG_RE = /Traceback \(most recent call last\)|panic: |FATAL:|error\[E\d+\]/; const ERROR_MULTI_RE = /(Error:|Exception:)/g; const WRITE_TOOLS = new Set(["Write", "Edit", "MultiEdit", "write", "edit", "multiEdit"]); ``` Sources: [integrations/opencode-plugin/opencode-mem0.ts:151-160](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L151-L160) > [!WARNING] > Write tools such as `Write`, `Edit`, and `MultiEdit` (tracked in `WRITE_TOOLS`) modify codebase state and require careful handling during conversation captures to prevent duplicating raw file diffs into long-term semantic memory [integrations/opencode-plugin/opencode-mem0.ts:160](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts#L160). ### Prompt Compaction and Context Injection Prompt assembly phases leverage the TypeScript agent core lifecycle (`createMemoryLifecycle`) to prepare conversation histories and sanitize user inputs before executing semantic similarity searches against the Mem0 platform [integrations/deepseek-plugin/src/index.ts:110-144](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L110-L144). When a user prompt is submitted, the system filters out non-user message sources, strips extraneous metadata, and prepares the text for recall [integrations/deepseek-plugin/src/index.ts:136-140](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L136-L140). Retrieved memory results are formatted with clear section headers and injected directly into the system prompt context array under descriptors like `mem0:recall` or `` [integrations/deepseek-plugin/src/index.ts:166-169](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L166-L169), [integrations/openclaw/index.ts:685](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L685). ```typescript ctx.on("system-prompt/assemble", async (_input, context, next): Promise => { const assembly = await next(); const { agent, signal } = context; if (!agent || signal?.aborted) return assembly; const state = stateFor(agent.session); const prompt = state.lifecycle.prepareConversation( agent.session .deriveMessages() .filter((message) => message.role === "user" && message.source?.kind === "user"), ).at(-1)?.content; if (!prompt) return assembly; const memoryContext = await state.lifecycle.recall(prompt, true, async (query) => { const started = Date.now(); try { const result = await client.search(query, { filters: resolveSearchFilters({}, userId), topK: AUTO_RECALL_LIMIT, }); captureEvent("deepseek.recall.auto", { success: true, duration_ms: Date.now() - started, result_count: result.results?.length ?? 0, }, client); return result; } catch (err) { captureEvent("deepseek.recall.auto", { success: false, duration_ms: Date.now() - started, error_kind: errorKind(err), }, client); throw err; } }); if (!memoryContext || signal?.aborted) return assembly; return { ...assembly, contexts: [...assembly.contexts, { name: "mem0:recall", text: memoryContext }], }; }); ``` Sources: [integrations/deepseek-plugin/src/index.ts:130-170](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts#L130-L170), [integrations/openclaw/index.ts:685](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L685) ## Related - [TypeScript Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/typescript-agent-core) - [OpenClaw Integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/openclaw-integration) --- ## Technical docs: Pi Agent Plugin URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/pi-agent-plugin
Relevant source files The following files were used as context for generating this wiki page: - [integrations/pi-agent-plugin/src/commands.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/commands.ts) - [integrations/pi-agent-plugin/src/attribution.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/attribution.ts) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/pi-agent-plugin/src/memory/tools.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts) - [integrations/pi-agent-plugin/src/entry.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/entry.ts) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/pi-agent-plugin/src/prompt.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/prompt.ts) - [cli/python/src/mem0_cli/output.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/output.py) - [integrations/agent-plugin-core/python/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py) - [integrations/mem0-agent-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py) - [integrations/pi-agent-plugin/src/memory/formatting.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/formatting.ts) - [integrations/pi-agent-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/index.ts) - [integrations/mem0-agent-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py) - [integrations/claude-code-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py) - [integrations/antigravity-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py) - [integrations/agent-plugin-core/python/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py) - [cli/node/src/output.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/output.ts) - [integrations/kimi-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/mcp_server.py) - [integrations/codex-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/mcp_server.py) - [integrations/antigravity-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_cli.py) - [integrations/claude-code-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_cli.py) - [integrations/agent-plugin-core/python/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py) - [integrations/cursor-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/mcp_server.py) - [integrations/agent-plugin-core/python/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/telemetry.py)
## Overview The Pi Agent Plugin integrates Mem0's persistent semantic memory directly into the Pi Coding Agent, managing the extension lifecycle, request attribution, tool execution, interactive user commands, and context injection. Designed to maintain long-term memory across sessions, the plugin automatically binds workspace project scopes, applies client attribution headers, and injects relevant memory policies into the agent's startup stream. ## Extension Lifecycle and Startup Registration ### Initialization and Configuration Binding The extension entry point exports a default function, `mem0Extension`, which accepts an `ExtensionAPI` instance from `@earendil-works/pi-coding-agent`. Execution begins by invoking `loadConfig()` to read settings from the environment or disk configuration paths. ```typescript export default function mem0Extension(pi: ExtensionAPI): void { const config = loadConfig(); if (!config.apiKey) { console.warn("[mem0] No API key found. Set MEM0_API_KEY or add apiKey to ~/.pi/agent/mem0-config.json. Extension disabled."); return; } ``` Sources: [integrations/pi-agent-plugin/src/entry.ts:24-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/entry.ts#L24-L30) If `config.apiKey` is absent, a warning message is emitted to the console and the initialization terminates immediately, disabling the extension without throwing an unhandled exception. > [!WARNING] > Omitting `MEM0_API_KEY` or failing to supply an `apiKey` in `~/.pi/agent/mem0-config.json` halts extension registration entirely, bypassing tool, command, and lifecycle event bindings. Sources: [integrations/pi-agent-plugin/src/entry.ts:27-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/entry.ts#L27-L30) ### User ID Resolution Strategy When initializing scope context, the plugin resolves the active user identifier through `resolveUserId(configUserId: string)`. The resolution sequence evaluates configuration overrides, environment variables, and system user data in a strict fallback order. | Priority | Source | Condition | Fallback / Return Value | | :--- | :--- | :--- | :--- | | 1 | `configUserId` parameter | Non-empty string | Returns `configUserId` directly | | 2 | `process.env.USER` | Environment variable is set | Returns `process.env.USER` | | 3 | `process.env.USERNAME` | Environment variable is set | Returns `process.env.USERNAME` | | 4 | `os.userInfo().username` | System call succeeds | Returns system username string | | 5 | Catch block / Final default | System call throws error | Returns `"default"` | ```typescript export function resolveUserId(configUserId: string): string { if (configUserId) return configUserId; if (process.env.USER) return process.env.USER; if (process.env.USERNAME) return process.env.USERNAME; try { return os.userInfo().username; } catch { return "default"; } } ``` Sources: [integrations/pi-agent-plugin/src/entry.ts:17-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/entry.ts#L17-L22) ### Client Setup and Event Handlers Once authenticated, the plugin instantiates a `MemoryClient` with the verified API key and decorates it using `applySurfaceHeaders(mem0)`. It constructs initial scope and telemetry contexts before registering memory tools, CLI commands, and auto-capture listeners. ```typescript const mem0 = new MemoryClient({ apiKey: config.apiKey }); // Every path below shares this client: automatic recall, capture, the memory // tools and deletion as well as the slash commands. Attribution belongs here // rather than on individual calls, or everything except the commands reports // as generic SDK traffic. applySurfaceHeaders(mem0); const scopeCtx: ScopeContext = { userId: resolveUserId(config.userId), appId: "", runId: "unknown", }; function getScopeCtx(): ScopeContext { return scopeCtx; } const telemetryCtx = { apiKey: config.apiKey }; const lifecycle = createMemoryLifecycle(); // ── Register tool + commands + auto-capture ───────────────────────── registerMemoryTool(pi, mem0, config, getScopeCtx, telemetryCtx); registerCommands(pi, mem0, config, getScopeCtx, telemetryCtx); setupAutoCapture(pi, mem0, config, getScopeCtx, telemetryCtx, lifecycle); captureEvent("pi.plugin.registered", { auto_capture: config.autoCapture, default_scope: config.defaultScope, }, telemetryCtx); ``` Sources: [integrations/pi-agent-plugin/src/entry.ts:32-61](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/entry.ts#L32-L61) The extension binds to three primary host agent lifecycle events: - `session_start`: Begins the [memory lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle) session, detects the application ID from the current working directory (`ctx.cwd`), extracts the run ID from the session file, and optionally overrides the user ID if configured. - `before_agent_start`: Appends the `MEMORY_POLICY` prompt instruction and performs guaranteed memory recall by querying the memory client using resolved search filters. - `session_shutdown`: Captures telemetry events signaling the termination of the active session. ```typescript // ── session_start: detect project + session, reconstruct scope ────── pi.on("session_start", async (_event, ctx) => { lifecycle.beginSession(); scopeCtx.appId = detectAppId(ctx.cwd); const sessionFile = ctx.sessionManager?.getSessionFile?.(); scopeCtx.runId = detectRunId(sessionFile); if (config.userId) { scopeCtx.userId = config.userId; } captureEvent("pi.session.start", {}, telemetryCtx); }); // ── before_agent_start: append memory policy and recall ───────────── pi.on("before_agent_start", async (event, _ctx) => { let extra = MEMORY_POLICY; // Guaranteed retrieval: prefetch memories relevant to this prompt so the // agent always has them, rather than depending on it to call the tool. const recall = await lifecycle.recall( event.prompt ?? "", config.contextInjection, (q) => mem0.search(q, { filters: resolveSearchFilters("project", scopeCtx) }), ); if (recall) extra += "\n\n" + recall; return { systemPrompt: (event.systemPrompt ?? "") + "\n\n" + extra, }; }); // ── session_shutdown ──────────────────────────────────────────────── pi.on("session_shutdown", async () => { captureEvent("pi.session.stop", {}, telemetryCtx); }); ``` Sources: [integrations/pi-agent-plugin/src/entry.ts:62-98](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/entry.ts#L62-L98) ## Surface Attribution and Request Headers ### Attribution Metadata and Stack Bounding The Pi Agent Plugin decorates outbound requests through the `MemoryClient` instance to identify traffic originating from the extension. Surface identification constants declare the platform source and application identity, while package version resolution reads the plugin version dynamically from `package.json` with a fallback to `"unknown"`. ```typescript export const PLATFORM_SOURCE = "PI_AGENT"; /** Host app the plugin runs inside. Allowlisted server-side. */ export const PLATFORM_APPLICATION = "pi"; const PLUGIN_VERSION = (() => { try { return JSON.parse( fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8"), ).version as string; } catch { return "unknown"; } })(); ``` Sources: [integrations/pi-agent-plugin/src/attribution.ts:5-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/attribution.ts#L5-L18) To track client call chains without fragmenting client identifiers, `boundedStack` appends the plugin's client string while enforcing strict capacity constraints. It limits the stack to a maximum of 4 entries and 200 total characters by dropping whole entries rather than slicing strings mid-identifier. > [!WARNING] > Slicing client header strings would sever identifiers and leave malformed fragments that the platform parses as autonomous client names. Bounding drops entire entries to preserve token integrity. Sources: [integrations/pi-agent-plugin/src/attribution.ts:20-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/attribution.ts:20-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/attribution.ts:20-40)) ### Request Header Application The `applySurfaceHeaders` function stamps surface identity parameters onto the shared `MemoryClient` headers object once during extension startup. Setting headers directly on the client instance ensures that automatic recall, memory capture, tool executions, and deletions all transmit attribution metadata. | Header Name | Configuration Rule | Value / Behavior | | :--- | :--- | :--- | | `X-Mem0-Source` | Set-once if empty or whitespace | `PLATFORM_SOURCE` (`"PI_AGENT"`) | | `X-Application` | Set-once if empty or whitespace | `PLATFORM_APPLICATION` (`"pi"`) | | `X-Mem0-Client` | Append-only stack | `mem0-pi-agent/${PLUGIN_VERSION}` bounded by `boundedStack` | ```typescript export function applySurfaceHeaders(client: MemoryClient): void { const headers = client.headers as Record; if (!headers["X-Mem0-Source"]?.trim()) headers["X-Mem0-Source"] = PLATFORM_SOURCE; if (!headers["X-Application"]?.trim()) headers["X-Application"] = PLATFORM_APPLICATION; const existing = (headers["X-Mem0-Client"] ?? "") .split(",") .map((part) => part.trim()) .filter(Boolean); headers["X-Mem0-Client"] = boundedStack(existing, `mem0-pi-agent/${PLUGIN_VERSION}`); } ``` Sources: [integrations/pi-agent-plugin/src/attribution.ts:55-65](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/attribution.ts#L55-L65) ## Interactive User Commands and Diagnostics ### Command Registration and Memory Manipulation The plugin registers interactive commands via the `ExtensionAPI` interface to allow developers to manually store, search, delete, and browse memories directly within the terminal interface. Each command validates user input, resolves scope filters, interacts with the `MemoryClient`, and dispatches structured feedback messages to the UI. | Command Name | Description | Required Arguments | Underlying Action | | :--- | :--- | :--- | :--- | | `/mem0-remember` | Store a memory verbatim (no inference) | Text string | `mem0.add([{ role: "user", content: text }], { infer: false, ... })` | | `/mem0-forget` | Delete memories matching a natural language query | Natural language query | `searchMemories(query)` followed by `mem0.delete(target.id)` | | `/mem0-search` | Semantic search across memories | Search query | `mem0.search(query, { filters, threshold, topK: 10, rerank: true })` | | `/mem0-tour` | Browse all memories grouped by category | Optional scope (`project`, `session`, `global`) | `mem0.getAll({ filters })` | ```typescript pi.registerCommand("mem0-remember", { description: "Store a memory verbatim (no inference)", handler: async (args, ctx) => { const text = args?.trim(); if (!text) { ctx.ui.notify("Usage: /mem0-remember ", "warning"); return; } const addParams = resolveAddParams(config.defaultScope, getScopeCtx()); const result = await mem0.add( [{ role: "user", content: text }], { ...addParams, customCategories: DEFAULT_CUSTOM_CATEGORIES, infer: false, source: PLATFORM_SOURCE }, ); captureCommandEvent("mem0-remember", {}, telemetryCtx); const storedItems = (Array.isArray(result) ? result : []) .map((m) => (m as { memory?: string }).memory) .filter((m): m is string => Boolean(m)); const items = storedItems.length > 0 ? storedItems : [text]; sendFeedback( "mem0-remember", [`**Stored to ${config.defaultScope} memory**`, ...items.map((m) => `- ${m}`)].join("\n"), ); }, }); ``` Sources: [integrations/pi-agent-plugin/src/commands.ts:48-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/commands.ts#L48-L73) > [!NOTE] > The search helper explicitly injects `source: PLATFORM_SOURCE` and `rerank: true` into the `SearchMemoryOptions` parameter block while utilizing a fixed top-K ceiling of 10. > > Sources: [integrations/pi-agent-plugin/src/commands.ts:32-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/commands.ts#L32-L46) ### Deletion and Browsing Workflows When a user invokes `/mem0-forget`, the command searches for matching memories using the default scope. If multiple matching records are returned, the plugin invokes `ctx.ui.select` to present compact formatted labels so the developer can pick the exact record to remove. If a single match is found, it triggers a confirmation prompt via `ctx.ui.confirm` before executing the deletion. ```typescript pi.registerCommand("mem0-forget", { description: "Delete memories matching a natural language query", handler: async (args, ctx) => { const query = args?.trim(); if (!query) { ctx.ui.notify("Usage: /mem0-forget ", "warning"); return; } const memories = await searchMemories(query, config.defaultScope); if (memories.length === 0) { captureCommandEvent("mem0-forget", { result_count: 0 }, telemetryCtx); sendFeedback("mem0-forget", `**No matches for "${query}"** — nothing to forget.`); return; } const forgotten = (mem: Parameters[0]) => { captureCommandEvent("mem0-forget", { deleted_count: 1 }, telemetryCtx); sendFeedback( "mem0-forget", [`**Forgotten from ${config.defaultScope} memory**`, `- ${formatMemoryCompact(mem)}`].join("\n"), ); }; if (memories.length === 1) { const target = memories[0]; const confirmed = await ctx.ui.confirm("Delete this memory?", formatMemoryCompact(target)); if (!confirmed) { sendFeedback("mem0-forget", "**Cancelled** — no memories deleted."); return; } await mem0.delete(target.id); forgotten(target); return; } const labels = memories.map((m) => formatMemoryCompact(m)); const selected = await ctx.ui.select( `Found ${pluralize(memories.length, "match", "matches")} for "${query}" — which should I delete?`, labels, ); if (!selected) { sendFeedback("mem0-forget", "**Cancelled** — no memories deleted."); return; } const idx = labels.indexOf(selected); if (idx < 0) return; const target = memories[idx]; await mem0.delete(target.id); forgotten(target); }, }); ``` Sources: [integrations/pi-agent-plugin/src/commands.ts:75-127](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/commands.ts#L75-L127) The `/mem0-tour` command accepts an optional scope argument (`project`, `session`, or `global`). If omitted, it falls back to `config.defaultScope`. It validates the raw input against permitted scope strings, retrieves all records matching the scope filters, groups them by category, and sends formatted output to the telemetry and UI streams. ```typescript pi.registerCommand("mem0-tour", { description: "Browse all memories grouped by category", handler: async (args, ctx) => { const raw = args?.trim().toLowerCase(); const validScopes: Scope[] = ["project", "session", "global"]; if (raw && !validScopes.includes(raw as Scope)) { ctx.ui.notify(`Invalid scope "${raw}". Must be one of: ${validScopes.join(", ")}`, "warning"); return; } const scope: Scope = (raw as Scope) || config.defaultScope; const filters = resolveSearchFilters(scope, getScopeCtx()); const result = await mem0.getAll({ filters }); const memories = result.results ?? []; if (memories.length === 0) { captureCommandEvent("mem0-tour", { memory_count: 0, scope }, telemetryCtx); ``` Sources: [integrations/pi-agent-plugin/src/commands.ts:157-172](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/commands.ts#L157-L172) ## Tool Execution and Agent Guidance ### Tool Registration and Parameter Schema The plugin registers the `mem0_memory` tool through the `registerMemoryTool` function, exposing six specific memory actions to the agent. Parameter validation relies on TypeBox schema definitions and `StringEnum` types that govern valid operation inputs and scoping. | Parameter Name | Type | Allowed Values / Description | | :--- | :--- | :--- | | `action` | StringEnum | `"search"`, `"add"`, `"get_all"`, `"update"`, `"delete"`, `"delete_all"` | | `query` | String | Search text required for action `"search"` | | `content` | String | Memory text required for actions `"add"` and `"update"` | | `memory_id` | String | Target memory ID required for actions `"update"` and `"delete"` | | `scope` | StringEnum | `"project"`, `"session"`, `"global"` | ```typescript parameters: Type.Object({ action: StringEnum( [ "search", "add", "get_all", "update", "delete", "delete_all", ] as const, { description: "Memory operation to run: \"search\" (semantic recall of earlier work in this repository), \"add\" (save a new fact/preference/decision), \"get_all\" (list everything in scope, no query needed), \"update\" (replace an existing memory's text by id), \"delete\" (remove one memory by id), \"delete_all\" (wipe every memory in the scope -- destructive, only on explicit request).", }, ), query: Type.Optional( Type.String({ description: `Search text -- required for action "search". ${SEARCH_QUERY_DESCRIPTION}`, }), ), content: Type.Optional( Type.String({ description: "Memory text -- required for action \"add\" (the fact to store) and \"update\" (the replacement text).", }), ), memory_id: Type.Optional( Type.String({ description: "Target memory's ID -- required for \"update\" and \"delete\". Use an ID returned by a prior \"search\" or \"get_all\".", }), ), scope: Type.Optional( StringEnum(["project", "session", "global"] as const, { description: "Where to read/write: \"project\" (default -- this repo), \"session\" (this run only), or \"global\" (across ALL projects; only when the user explicitly wants cross-project recall). Omit for normal queries.", }), ), }), ``` Sources: [integrations/pi-agent-plugin/src/memory/tools.ts:157-196](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts#L157-L196) > [!WARNING] > Action `delete_all` wipes every memory within the specified scope. Because this operation is destructive, execution requires explicit user authorization and must never be triggered implicitly by standard agent autonomy routines. > > Sources: [integrations/pi-agent-plugin/src/memory/tools.ts:124-132](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts#L124-L132), [integrations/pi-agent-plugin/src/memory/tools.ts:169-170](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts#L169-L170) ### Execution Dispatch and Output Limits The `buildToolExecute` factory returns an asynchronous handler that processes tool parameters, checks abort signals, resolves tool scopes, and dispatches calls to the underlying `MemoryClient`. Tool outputs are bound by strict output length constraints to protect agent context windows. | Constraint Constant | Threshold Value | Enforcement Behavior | | :--- | :--- | :--- | | `MAX_OUTPUT_LINES` | 200 lines | Truncates lists exceeding line count | | `MAX_OUTPUT_BYTES` | 50,000 bytes | Truncates string length exceeding byte limit | ```typescript const MAX_OUTPUT_LINES = 200; const MAX_OUTPUT_BYTES = 50_000; function normalizeMemoryId(id: string): string { return id.replace(/^\[?mem0:([0-9a-f-]{36})\]?$/i, "$1"); } function truncateOutput(text: string): string { const lines = text.split("\n"); if (lines.length <= MAX_OUTPUT_LINES && text.length <= MAX_OUTPUT_BYTES) { return text; } const kept = lines.slice(0, MAX_OUTPUT_LINES); let result = kept.join("\n"); if (result.length > MAX_OUTPUT_BYTES) { result = result.slice(0, MAX_OUTPUT_BYTES); } const dropped = lines.length - kept.length; if (dropped > 0 || text.length > MAX_OUTPUT_BYTES) { result += `\n\n[Output truncated: showing ${kept.length} of ${lines.length} lines]`; } return result; } ``` Sources: [integrations/pi-agent-plugin/src/memory/tools.ts:19-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts#L19-L43) Memory identifiers returned by search results or passed to updates are automatically normalized via `normalizeMemoryId`, which strips bracketed wrappers like `[mem0:uuid]` down to raw UUID strings. Telemetry wrappers track execution latency, success status, and result match counts across all tool invocations. ```typescript function normalizeMemoryId(id: string): string { return id.replace(/^\[?mem0:([0-9a-f-]{36})\]?$/i, "$1"); } ``` Sources: [integrations/pi-agent-plugin/src/memory/tools.ts:22-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts#L22-L24), [integrations/pi-agent-plugin/src/memory/tools.ts:200-218](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts#L200-L218) > [!NOTE] > Tool execution checks `signal?.aborted` at the start of actions such as `search`, `add`, and `delete_all`, throwing an immediate `"Cancelled"` error if the asynchronous operation is aborted. > > Sources: [integrations/pi-agent-plugin/src/memory/tools.ts:63](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts#L63), [integrations/pi-agent-plugin/src/memory/tools.ts:75](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts#L75), [integrations/pi-agent-plugin/src/memory/tools.ts:125](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts#L125) ### Agent Prompt Cues and Scoping Guidance The plugin provides system prompt policies via `MEMORY_POLICY` and explicit prompt guidelines inside tool registrations to instruct the agent on proactive memory storage and proper scope selection. ```typescript export const MEMORY_POLICY = ` You have persistent semantic memory via the mem0_memory tool, powered by Mem0. Relevant memories may be auto-injected under . Use mem0_memory with action "search" ${SEARCH_WHEN}. Be proactive about saving: - Save important facts, preferences, goals, decisions, lessons learned, identity, relationships, and routines the user shares. Scope (do not change unless explicitly asked): - "project" (default): memories for this project — use for all normal queries - "session": memories from this session only - "global": all memories across projects — ONLY when the user explicitly asks for cross-project search Memory persists across sessions and devices via Mem0's cloud. `; ``` Sources: [integrations/pi-agent-plugin/src/prompt.ts:3-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/prompt.ts#L3-L17) The agent is instructed to use the `"project"` scope by default for all normal queries, restricting `"global"` cross-project searches strictly to instances where the user has explicitly requested it. Normal queries omit the scope parameter entirely to let the system fall back to the project default automatically. ```typescript "Always use the default project scope unless the user EXPLICITLY asks to search across all projects — only after the user selects /mem0-scope global use scope \"global\"", "Do NOT pass scope at all for normal queries — omitting it uses the project default automatically", ``` Sources: [integrations/pi-agent-plugin/src/memory/tools.ts:154-155](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts#L154-L155) ## Memory Formatting and Age Representation ### Presentation Formats and List Renderers The Pi agent plugin delegates memory formatting, prose rendering, and age duration calculations directly to shared helpers provided by the core TypeScript agent library [TypeScript Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/typescript-agent-core). The local `formatting.ts` module re-exports `formatAge`, `formatMemoryCompact`, `formatMemoryList`, and `groupByCategory` alongside the `MemoryLike` interface type. ```typescript export { formatAge, formatMemoryCompact, formatMemoryList, groupByCategory, } from "../../../agent-plugin-core/typescript/src/formatting.ts"; ``` Sources: [integrations/pi-agent-plugin/src/memory/formatting.ts:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/formatting.ts#L1-L6) Memories are rendered in different presentation structures depending on whether they appear in compact dialog selections, user command feedbacks, or structured multi-item lists. Compact representations format single memories for command prompts, confirmations, and deletion feedback workflows, whereas list formats organize multiple retrieved entries into structured Markdown outputs. When browsing memories via commands like `/mem0-tour`, `groupByCategory` sorts and aggregates memories into categorized sections using default custom categories. ```typescript import { formatMemoryList, formatMemoryCompact, groupByCategory } from "./memory/formatting.ts"; ``` Sources: [integrations/pi-agent-plugin/src/commands.ts:7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/commands.ts#L7), [integrations/pi-agent-plugin/src/commands.ts:92-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/commands.ts#L92-L97) > [!NOTE] > Formatting functions rely on the underlying `MemoryLike` schema definition to extract properties such as memory content, creation timestamps, and category groupings without coupling directly to specific platform transport schemas. > > Sources: [integrations/pi-agent-plugin/src/memory/formatting.ts:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/formatting.ts#L1-L6) ## Related - [TypeScript Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/typescript-agent-core) --- ## Technical docs: Hermes Plugin URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/hermes-plugin
Relevant source files The following files were used as context for generating this wiki page: - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/hermes-plugin-mem0/_backend.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py) - [integrations/hermes-plugin-mem0/_openai_llm.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_openai_llm.py) - [integrations/hermes-plugin-mem0/_oss_providers.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_oss_providers.py) - [integrations/hermes-plugin-mem0/_setup.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py)
## Overview The Hermes Mem0 plugin integrates memory capture, semantic search, and lifecycle hooks into agent execution loops through a unified `MemoryProvider` interface. Operating across cloud, self-hosted, and open-source deployments, the plugin solves context limitations and synchronization bottlenecks by handling asynchronous turn syncing, circuit-breaking failure recovery, and background memory prefetching. Key architectural decisions include strict dimension validation for local vector stores, a dedicated OpenAI LLM adapter bypassing router constraints for OSS pipelines, and an interactive setup wizard with connectivity probes. Sources: [integrations/hermes-plugin-mem0/__init__.py:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py#L1-L8), [integrations/hermes-plugin-mem0/_backend.py:26-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py#L26-L30), [integrations/hermes-plugin-mem0/_setup.py:1-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py#L1-L3) ## MemoryProvider Lifecycle and Tool Execution ### Initialization and Configuration Resolution The `Mem0MemoryProvider` implements the Hermes `MemoryProvider` lifecycle, managing configuration loading, backend instantiation, and circuit breaker states. Configuration resolves by combining default parameters, environment variables via `get_secret()`, and JSON files stored in `$HERMES_HOME/mem0.json`. The user identifier uses a strict precedence rule where operator-configured values override gateway-native IDs, while the literal placeholder `hermes-user` is treated as unset so automated setup wizards retain gateway-native values. ```python def _load_config() -> dict: """Env vars provide defaults; $HERMES_HOME/mem0.json overrides individual keys. Layering avoids a silent failure when the JSON file exists but lacks fields like ``api_key`` that the user set in ``.env``.""" from hermes_constants import get_hermes_home # Identity (user/agent id), host and mode are .env values like the key: read them through the # profile scope too, or a secondary profile's memories land in the default profile's account. # A scope-less multiplex caller raises here on purpose — that is a spawn-site bug, and # swallowing it would silently route the turn's memories to the default profile. config = {"mode": get_secret("MEM0_MODE", "") or "platform", "host": get_secret("MEM0_HOST", "") or "", "agent_id": get_secret("MEM0_AGENT_ID", "") or "hermes", "oss": {}} if user_id := get_secret("MEM0_USER_ID", ""): # only when explicitly configured, so initialize() can fall back to the gateway-native id config["user_id"] = user_id file_cfg = read_json_or_empty(get_hermes_home() / "mem0.json") config.update({k: v for k, v in file_cfg.items() if v is not None and v != ""}) # MEM0_API_KEY authenticates the Platform and self-hosted HTTP backends; pure OSS mode builds its # backend from the local ``oss`` config and has no platform credential to resolve, so a profile # scope WITHOUT the key must still load an OSS config (#99121 as it stands today: the caller is # scoped, the scope is just empty). Decided after mem0.json overrode the env defaults because # the file may be what selects ``oss``. Scope-less callers already raised above. if config.get("mode", "platform") == "oss": config.setdefault("api_key", "") elif not config.get("api_key"): config["api_key"] = get_secret("MEM0_API_KEY", "") return config ``` Sources: [integrations/hermes-plugin-mem0/__init__.py:76-101](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py#L76-L101) ```python def initialize(self, session_id: str, **kwargs) -> None: self._config = cfg = _load_config() self._mode, self._api_key, self._host, self._agent_id = cfg.get("mode", "platform"), cfg.get("api_key", ""), cfg.get("host", ""), cfg.get("agent_id", "hermes") # user_id precedence: operator-configured (env/mem0.json) > gateway-native id (kwargs) > _DEFAULT_USER_ID. # The literal placeholder counts as unset so wizard users still get gateway-native ids. configured = cfg.get("user_id") self._user_id = (None if configured == _DEFAULT_USER_ID else configured) or kwargs.get("user_id") or _DEFAULT_USER_ID # Persisted rerank preference: default for mem0_search when the model omits ``rerank``. Platform-only. _rr = cfg.get("rerank", False) self._rerank_default = _rr.lower() in ("true", "1", "yes") if isinstance(_rr, str) else bool(_rr) self._channel = kwargs.get("platform") or "cli" try: self._sync_max_chars = int(cfg.get("sync_max_chars") or _SYNC_MSG_MAX_CHARS) except (ValueError, TypeError): self._sync_max_chars = _SYNC_MSG_MAX_CHARS self._backend = self._create_backend() if self._backend and not self._atexit_registered: atexit.register(self.shutdown) self._atexit_registered = True ``` Sources: [integrations/hermes-plugin-mem0/__init__.py:228-247](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py#L228-L247) > [!NOTE] > A scope-less multiplex caller will trigger a deliberate exception during configuration loading. This prevents secondary profile memories from accidentally leaking into the default profile's account when a spawn-site bug omits profile scoping. ### Turn Lifecycle and Prefetching Mechanics Agent turns integrate with asynchronous memory prefetching and background synchronization. When a turn begins via `on_turn_start()`, background prefetching queries the vector backend for relevant memories using the current message. The `prefetch()` method checks the result of this background thread with a hot-path timeout limit defined by `_PREFETCH_WAIT_SECS` (3 seconds). If the backend is slow or unresponsive, prefetching yields an empty string, leaving `mem0_search` as the functional backstop. ```python def on_turn_start(self, turn_number: int, message: str, **kwargs) -> None: self._start_prefetch(message) def _consume_prefetch_result(self, query: str) -> str | None: """Pop the finished prefetch body for ``query`` (None if absent or still running).""" with self._prefetch_lock: if self._prefetch_query != query or not self._prefetch_done: return None result, self._prefetch_result, self._prefetch_done = self._prefetch_result, "", False return result def _start_prefetch(self, query: str) -> None: backend = self._backend if not query or backend is None or self._is_breaker_open(): return def _run(): results = self._try(lambda: self._search(query, rerank=self._rerank_default, backend=backend), logger.debug, "Mem0 prefetch failed: %s") lines = [r.get("memory", "") for r in (results or []) if r.get("memory")] body = "## Mem0 Memory\n" + "\n".join(f"- {line}" for line in lines) if lines else "" with self._prefetch_lock: if self._prefetch_query == query: self._prefetch_result, self._prefetch_done = body, True with self._prefetch_lock: if self._prefetch_query == query and (self._prefetch_done or (self._prefetch_thread and self._prefetch_thread.is_alive())): return self._prefetch_query, self._prefetch_result, self._prefetch_done = query, "", False self._prefetch_thread = spawn_context_thread(_run, name="mem0-prefetch") self._prefetch_thread.start() def prefetch(self, query: str, *, session_id: str = "") -> str: """Recall memories for the CURRENT question with a short hot-path wait.""" if (cached := self._consume_prefetch_result(query)) is not None: return cached self._start_prefetch(query) with self._prefetch_lock: thread = self._prefetch_thread if self._prefetch_query == query else None if thread: thread.join(timeout=_PREFETCH_WAIT_SECS) return self._consume_prefetch_result(query) or "" # slow backend: skip injection; mem0_search remains the backstop ``` Sources: [integrations/hermes-plugin-mem0/__init__.py:263-304](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py#L263-L304) Concurrently, completed agent turns are synchronized back to Mem0 for server-side fact extraction via `sync_turn()`. To prevent input token overflows on models with restricted context windows, user and assistant messages are truncated using `_truncate_for_sync()`. This function scans for sentence boundary delimiters (`。", "!", "?", ".", "!", "?"`) within the character budget `_SYNC_MSG_MAX_CHARS` (defaulting to 450 characters) to ensure extracted statements remain grammatically coherent. ```python # Sentence ends recognized when trimming a synced message. Deliberately unordered: # the LAST boundary of ANY kind wins, so one CJK stop early in a mixed-script turn # cannot outrank a Latin stop near the end of the window. ``".\n"`` is not listed — # its index can never exceed the bare ``"."`` it starts with. _SYNC_SENTENCE_ENDS = ("。", "!", "?", ".", "!", "?") def _truncate_for_sync(text: str, max_len: int = _SYNC_MSG_MAX_CHARS) -> str: """Cap a synced message at its last sentence boundary within ``max_len``. Short messages pass through unchanged; long ones keep the last complete sentence inside the window so fact extraction still sees coherent statements, with a hard cut as fallback when no boundary exists (or one only appears in the first third of the window, which usually means unsegmented input). """ if len(text) <= max_len: return text window = text[:max_len] cut = max(window.rfind(sep) for sep in _SYNC_SENTENCE_ENDS) if cut > max_len // 3: return text[:cut + 1] return text[:max_len] ``` Sources: [integrations/hermes-plugin-mem0/__init__.py:46-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py#L46-L68) ```python def sync_turn(self, user_content: str, assistant_content: str, *, session_id: str = "") -> None: """Send the turn to Mem0 for server-side fact extraction (non-blocking).""" if self._backend is None or self._is_breaker_open(): return def _sync(): if self._backend is not None: messages = [ {"role": "user", "content": _truncate_for_sync(user_content, self._sync_max_chars)}, {"role": "assistant", "content": _truncate_for_sync(assistant_content, self._sync_max_chars)}, ] self._try(lambda: self._add(messages, infer=True), logger.warning, "Mem0 sync failed: %s") with self._sync_lock: prev = self._sync_thread if prev and prev.is_alive(): prev.join(timeout=5.0) if prev.is_alive(): return with self._sync_lock: self._sync_thread = spawn_context_thread(_sync, name="mem0-sync") self._sync_thread.start() ``` Sources: [integrations/hermes-plugin-mem0/__init__.py:305-327](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py#L305-L327) ### Tool Dispatching and Error Handling The provider maps four core memory management tools to internal execution handlers. Each tool call validates required arguments, checks backend initialization state, and enforces circuit-breaker policies. | Tool Name | Required Parameters | Error Label | Client-Error Policy | Target Handler | | :--- | :--- | :--- | :--- | :--- | | `mem0_search` | `query` | Search failed | `skip` | `_tool_search` | | `mem0_add` | `content` | Failed to store | `count` | `_tool_add` | | `mem0_update` | `memory_id`, `text` | Update failed | `not_found` | `_tool_update` | | `mem0_delete` | `memory_id` | Delete failed | `not_found` | `_tool_delete` | ```python _TOOL_HANDLERS = { "mem0_search": (("query",), "Search failed", _tool_search, "skip"), "mem0_add": (("content",), "Failed to store", _tool_add, "count"), "mem0_update": (("memory_id", "text"), "Update failed", _tool_update, "not_found"), "mem0_delete": (("memory_id",), "Delete failed", _tool_delete, "not_found"), } ``` Sources: [integrations/hermes-plugin-mem0/__init__.py:369-374](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py#L369-L374) Mutations (`mem0_update` and `mem0_delete`) invoke `_ensure_owns_memory()` to verify that target memory UUIDs belong to the caller's active `user_id` before executing write operations, raising a `PermissionError` if ownership validation fails. ```python def _ensure_owns_memory(self, memory_id: str) -> None: """Reject a mutation on a memory that doesn't belong to the caller's user_id.""" memory = self._backend.get(memory_id) if not memory: raise ValueError(f"Memory not found: {memory_id}") owner = memory.get("user_id") if isinstance(memory, dict) else None if owner and owner != self._user_id: raise PermissionError(f"Memory {memory_id} does not belong to this user.") def _tool_update(self, args: dict) -> str: self._ensure_owns_memory(args["memory_id"]) return json.dumps(self._backend.update(args["memory_id"], args["text"])) def _tool_delete(self, args: dict) -> str: self._ensure_owns_memory(args["memory_id"]) return json.dumps(self._backend.delete(args["memory_id"])) ``` Sources: [integrations/hermes-plugin-mem0/__init__.py:352-368](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py#L352-L368) > [!WARNING] > A circuit breaker trips after `_BREAKER_THRESHOLD` (5) consecutive failures, pausing subsequent background API calls and tool dispatches for `_BREAKER_COOLDOWN_SECS` (120 seconds). User-caused errors (`MemoryNotFoundError` and `ValidationError`) are classified as client errors and do not increment the failure counter, except for `mem0_add` where missing resources trigger failure tracking. ```python # Circuit breaker: after _BREAKER_THRESHOLD consecutive failures, pause API # calls for _BREAKER_COOLDOWN_SECS to avoid hammering a down server. _BREAKER_THRESHOLD, _BREAKER_COOLDOWN_SECS, _PREFETCH_WAIT_SECS = 5, 120, 3 _CLIENT_ERROR_TYPES = ("MemoryNotFoundError", "ValidationError") # Placeholder user_id. initialize() treats it as "no operator-configured user_id" # so legacy mem0.json files written by the wizard don't override gateway-native ids. _DEFAULT_USER_ID = "hermes-user" ``` Sources: [integrations/hermes-plugin-mem0/__init__.py:28-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py#L28-L34) ## Backend Architecture Across Deployment Modes ### Concrete Backend Implementations The plugin abstracts storage operations through the `Mem0Backend` abstract base class, which defines unified interfaces for searching, adding, getting, updating, and deleting memories. Subclasses implement concrete backends for cloud, HTTP, and local embedded deployments. ```python class Mem0Backend(ABC): """Unified interface over Platform (MemoryClient), self-hosted (HTTP) and OSS (Memory) backends. update()/delete() are template methods: subclasses implement raw ``_update``/``_delete``.""" @abstractmethod def search(self, query: str, *, filters: dict, top_k: int = 10, rerank: bool = False) -> list[dict]: ... @abstractmethod def add(self, messages: list, *, user_id: str, agent_id: str, infer: bool = False, metadata: dict | None = None) -> dict: ... @abstractmethod def get(self, memory_id: str) -> dict | None: ... @abstractmethod def _update(self, memory_id: str, text: str) -> None: ... @abstractmethod def _delete(self, memory_id: str) -> None: ... ``` Sources: [integrations/hermes-plugin-mem0/_backend.py:26-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py#L26-L40) Three concrete backend classes handle interaction across deployment modes: - `PlatformBackend`: Wraps the cloud `mem0.MemoryClient` SDK using the provided `api_key` for authentication. - `SelfHostedBackend`: Implements direct HTTP requests via `httpx` against a self-hosted Mem0 FastAPI server. It configures `X-API-Key` headers, supports connection-level retries (`retries=2`), and applies an extended timeout of 120 seconds (`_capture_timeout`) specifically for memory additions that trigger server-side fact extraction. - `OSSBackend`: Wraps the local `mem0.Memory` library for self-hosted OSS usage, managing [embedded vector stores](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/embedded-vector-stores) and local execution threads. ```python class PlatformBackend(Mem0Backend): """Wraps mem0.MemoryClient for Mem0 Platform (cloud API).""" def __init__(self, api_key: str): from mem0 import MemoryClient self._client = MemoryClient(api_key=api_key) def search(self, query: str, *, filters: dict, top_k: int = 10, rerank: bool = False) -> list[dict]: return _unwrap_results(self._client.search(query, filters=filters, top_k=top_k, rerank=rerank)) def add(self, messages: list, *, user_id: str, agent_id: str, infer: bool = False, metadata: dict | None = None) -> dict: return self._client.add(messages, **_add_kwargs(user_id, agent_id, infer, metadata)) def get(self, memory_id: str) -> dict | None: return self._client.get(memory_id) def _update(self, memory_id: str, text: str) -> None: self._client.update(memory_id=memory_id, text=text) def _delete(self, memory_id: str) -> None: self._client.delete(memory_id=memory_id) ``` Sources: [integrations/hermes-plugin-mem0/_backend.py:53-74](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py#L53-L74) ```python class SelfHostedBackend(Mem0Backend): """Direct HTTP backend for a self-hosted Mem0 server (the FastAPI ``server/``). mem0.MemoryClient is hardwired to the cloud API (``Authorization: Token``, ``GET /v1/ping/`` in ``__init__``), so this speaks the server's real contract: ``X-API-Key`` auth and the ``/memories`` / ``/search`` routes.""" def __init__(self, api_key: str, host: str, transport=None): import httpx headers = {"Content-Type": "application/json", **({"X-API-Key": api_key} if api_key else {})} # key omitted only for AUTH_DISABLED servers # Connect-level retries keep one dropped SYN from counting toward the breaker. ``transport`` is injectable for tests. self._client = httpx.Client(base_url=host.rstrip("/"), headers=headers, timeout=30.0, transport=transport or httpx.HTTPTransport(retries=2)) self._capture_timeout = httpx.Timeout(120.0, connect=30.0) def _json(self, method: str, path: str, **kwargs) -> Any: resp = self._client.request(method, path, **kwargs) resp.raise_for_status() return resp.json() if resp.content else {} def search(self, query: str, *, filters: dict, top_k: int = 10, rerank: bool = False) -> list[dict]: # rerank is platform-only; the self-hosted /search ignores it. user_id belongs in filters (top-level is deprecated). return _unwrap_results(self._json("POST", "/search", json={"query": query, "top_k": top_k, **({"filters": filters} if filters else {})})) def add(self, messages: list, *, user_id: str, agent_id: str, infer: bool = False, metadata: dict | None = None) -> dict: # Server-side extraction takes longer than a search or verbatim write. return self._json("POST", "/memories", json={"messages": messages, **_add_kwargs(user_id, agent_id, infer, metadata)}, timeout=self._capture_timeout if infer else self._client.timeout) def get(self, memory_id: str) -> dict | None: return self._json("GET", f"/memories/{memory_id}") def _update(self, memory_id: str, text: str) -> None: self._json("PUT", f"/memories/{memory_id}", json={"text": text}) def _delete(self, memory_id: str) -> None: self._json("DELETE", f"/memories/{memory_id}") def close(self) -> None: with suppress(Exception): self._client.close() ``` Sources: [integrations/hermes-plugin-mem0/_backend.py:76-114](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py#L76-L114) ```python class OSSBackend(Mem0Backend): """Wraps mem0.Memory for self-hosted (OSS) mode.""" def __init__(self, oss_config: dict): from ._oss_providers import EMBEDDER_PROVIDERS, KNOWN_DIMS, LLM_PROVIDERS self._local_path = None self._owner = None self._lock = nullcontext() self._closed = False def _provider_block(name: str, registry: dict) -> dict: """Copy of oss_config[name] with the legacy ``api_base`` key mapped to the provider's canonical base-URL key.""" block = dict(oss_config[name]) provider_config = dict(block.get("config", {})) legacy_base = provider_config.pop("api_base", None) canonical_key = registry.get(str(block.get("provider") or "").strip().lower(), {}).get("base_url_key") if legacy_base and canonical_key: provider_config.setdefault(canonical_key, legacy_base) if str(block.get("provider") or "").strip().lower() == "openai": from agent.secret_scope import get_secret # Resolve profile secrets before comparing configurations for sharing. provider_config["api_key"] = provider_config.get("api_key") or get_secret("OPENAI_API_KEY", "") if not provider_config["api_key"]: raise ValueError(f"OpenAI API key is required for the Hermes Mem0 OSS {name}") provider_config["openai_base_url"] = ( provider_config.get("openai_base_url") or get_secret("OPENAI_API_BASE", "") or get_secret("OPENAI_BASE_URL", "") or "https://api.openai.com/v1" ) block["config"] = provider_config return block vector_store = dict(oss_config["vector_store"]) vs_config = dict(vector_store.get("config", {})) if vs_config.get("path"): vs_config["path"] = os.path.expanduser(vs_config["path"]) embedder_config = oss_config.get("embedder", {}).get("config", {}) dims = embedder_config.get("embedding_dims") or KNOWN_DIMS.get(embedder_config.get("model", "")) if dims: vs_config["embedding_model_dims"] = dims remote = (vs_config.get("host") and vs_config.get("port")) or vs_config.get("url") or vs_config.get("api_key") if (vector_store.get("provider", "qdrant") == "qdrant" and not vs_config.get("client") and not remote and vs_config.get("https") is None): from mem0.configs.vector_stores.qdrant import QdrantConfig path = vs_config.get("path", QdrantConfig.model_fields["path"].default) if path: self._local_path = vs_config["path"] = os.path.realpath(os.path.expanduser(path)) vector_store["config"] = vs_config config = {"vector_store": vector_store, "llm": _provider_block("llm", LLM_PROVIDERS), "embedder": _provider_block("embedder", EMBEDDER_PROVIDERS), "version": "v1.1"} if self._local_path: from hermes_constants import get_hermes_home profile = os.path.realpath(get_hermes_home()) with _LOCAL_QDRANT_LOCK: owner = _LOCAL_QDRANT_MEMORIES.get(self._local_path) if owner is None: owner = _LocalQdrantMemory(self._create_memory(config, dims), deepcopy(config), profile) _LOCAL_QDRANT_MEMORIES[self._local_path] = owner else: if owner.profile != profile or owner.config != config: raise ValueError("Local Qdrant storage is already open with a different profile or configuration. " "Existing memories were preserved. Close its active sessions before changing settings, " "or use a separate storage path.") owner.users += 1 self._owner, self._lock, self._memory = owner, owner.lock, owner.memory else: self._memory = self._create_memory(config, dims) ``` Sources: [integrations/hermes-plugin-mem0/_backend.py:145-212](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py#L145-L212) ### Dimension Validation and Local Qdrant Management When initializing `OSSBackend`, the configuration checks embedding dimensions against any existing vector collection to prevent silent vector space corruption. The `_reject_dimension_mismatch()` method inspects the collection using `_detect_current_dims()` for supported providers (`qdrant` and `pgvector`). If the stored vector dimensions differ from the expected dimensions, it raises a `ValueError` without destroying existing records. ```python def _detect_current_dims(provider: str, vs_config: dict, collection_name: str) -> int | None: """Current embedding dimension of ``collection_name``, or None if it doesn't exist yet. Raises on any failure to connect/inspect so the caller can decide whether to skip the guard.""" if provider == "qdrant": from qdrant_client import QdrantClient path, url, host = vs_config.get("path"), vs_config.get("url"), vs_config.get("host") if path: client = QdrantClient(path=path) elif url: client = QdrantClient(url=url, api_key=vs_config.get("api_key")) elif host: client = QdrantClient(host=host, port=vs_config.get("port") or 6333, api_key=vs_config.get("api_key")) else: return None with closing(client): if not client.collection_exists(collection_name): return None vectors = client.get_collection(collection_name).config.params.vectors # Named-vector collections expose a dict; unnamed expose an object with .size. if isinstance(vectors, dict): vectors = next(iter(vectors.values()), None) return getattr(vectors, "size", None) elif provider == "pgvector": import psycopg2 conn_params = {k: vs_config[k] for k in ("host", "port", "user", "password", "dbname", "sslmode") if vs_config.get(k)} with closing(psycopg2.connect(**conn_params)) as conn: conn.autocommit = True with closing(conn.cursor()) as cur: cur.execute("SELECT atttypmod FROM pg_attribute WHERE attrelid = %s::regclass AND attname = 'vector'", (collection_name,)) row = cur.fetchone() return row[0] if row and row[0] > 0 else None return None ``` Sources: [integrations/hermes-plugin-mem0/_backend.py:236-267](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py#L236-L267) ```python def _reject_dimension_mismatch(provider: str, vs_config: dict, expected_dims: int) -> None: """Reject embedding dimension changes without deleting existing memories.""" collection_name = vs_config.get("collection_name", "mem0") try: current_dims = OSSBackend._detect_current_dims(provider, vs_config, collection_name) except Exception as dimension_detection_error: logger.warning( "Could not determine embedding dimensions for collection %r (%s): %s. Skipping dimension-change guard.", collection_name, provider, dimension_detection_error, ) return if current_dims is not None and current_dims != expected_dims: raise ValueError( f"Collection {collection_name!r} has {current_dims} embedding dimensions, but {expected_dims} are configured. " "Existing memories were preserved. Restore the previous embedder or use a new collection_name." ) ``` Sources: [integrations/hermes-plugin-mem0/_backend.py:270-286](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py#L270-L286) > [!CAUTION] > Local Qdrant file paths tracked in `_LOCAL_QDRANT_MEMORIES` enforce strict profile and configuration parity across sessions. If an active session attempts to open an existing local Qdrant path with a mismatched profile or different configuration parameters, `OSSBackend` raises a `ValueError` to prevent data corruption across overlapping agent instances. ```python @dataclass class _LocalQdrantMemory: memory: Any config: dict profile: str lock: Any = field(default_factory=RLock) users: int = 1 _LOCAL_QDRANT_MEMORIES: dict[str, _LocalQdrantMemory] = {} _LOCAL_QDRANT_LOCK = RLock() ``` Sources: [integrations/hermes-plugin-mem0/_backend.py:120-131](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py#L120-L131) ```python if self._local_path: from hermes_constants import get_hermes_home profile = os.path.realpath(get_hermes_home()) with _LOCAL_QDRANT_LOCK: owner = _LOCAL_QDRANT_MEMORIES.get(self._local_path) if owner is None: owner = _LocalQdrantMemory(self._create_memory(config, dims), deepcopy(config), profile) _LOCAL_QDRANT_MEMORIES[self._local_path] = owner else: if owner.profile != profile or owner.config != config: raise ValueError("Local Qdrant storage is already open with a different profile or configuration. " "Existing memories were preserved. Close its active sessions before changing settings, " "or use a separate storage path.") owner.users += 1 self._owner, self._lock, self._memory = owner, owner.lock, owner.memory else: self._memory = self._create_memory(config, dims) ``` Sources: [integrations/hermes-plugin-mem0/_backend.py:195-212](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py#L195-L212) ## Custom LLM Adapter Architecture ### DirectOpenAILLM Implementation and Configuration Mapping The plugin implements a specialized `DirectOpenAILLM` class that subclasses `OpenAILLM` to bridge Hermes pipelines into Mem0 OSS mode. This adapter bypasses the default `OpenAILLM.__init__` behavior, which redirects calls to OpenRouter when `OPENROUTER_API_KEY` is present in the environment. Instead, it delegates initialization directly to `LLMBase`, maps base configuration options, and instantiates the official OpenAI client. ```python class DirectOpenAILLM(OpenAILLM): """Use OpenAI credentials and requests regardless of router environment.""" def __init__(self, config: Optional[Union[BaseLlmConfig, OpenAIConfig, Dict]] = None): if config is None: config = OpenAIConfig() elif isinstance(config, dict): config = OpenAIConfig(**config) elif isinstance(config, BaseLlmConfig) and not isinstance(config, OpenAIConfig): fields = {k: getattr(config, k) for k in _COPIED_FIELDS} fields.update({k: getattr(config, k, None) for k in _OPTIONAL_FIELDS}) config = OpenAIConfig(**fields) ``` Sources: [integrations/hermes-plugin-mem0/_openai_llm.py:18-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_openai_llm.py#L18-L29) ```python if not config.model: config.model = "gpt-5-mini" # Configs predating the setup marker: keep the default model reasoning-safe # without overriding an explicit user choice. if config.model == "gpt-5-mini" and config.is_reasoning_model is None: config.is_reasoning_model = True # Bypass OpenAILLM.__init__ (it picks OpenRouter when OPENROUTER_API_KEY is # set); LLMBase still owns validation and supported-parameter filtering. LLMBase.__init__(self, config) # OPENAI_API_KEY / OPENAI_BASE_URL are profile credentials: read them through the secret # scope, never raw os.environ, or a multiplexed secondary's memory extraction runs on the # default profile's OpenAI account (and its proxy). from agent.secret_scope import get_secret api_key = self.config.api_key or get_secret("OPENAI_API_KEY", "") if not api_key: raise ValueError("OpenAI API key is required for the Hermes Mem0 OSS provider") from openai import OpenAI self.client = OpenAI(api_key=api_key, base_url=self.config.openai_base_url or get_secret("OPENAI_API_BASE", "") or get_secret("OPENAI_BASE_URL", "") or "https://api.openai.com/v1", timeout=120.0) ``` Sources: [integrations/hermes-plugin-mem0/_openai_llm.py:30-48](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_openai_llm.py#L30-L48) The adapter maps configuration fields between general Mem0 configuration objects and OpenAI-specific parameters using predefined tuple sets. Required fields and optional reasoning parameters are copied during initialization: | Field Category | Mapped Parameters / Fields | | :--- | :--- | | Copied Base Fields (`_COPIED_FIELDS`) | `model`, `temperature`, `api_key`, `max_tokens`, `top_p`, `top_k`, `enable_vision`, `vision_details`, `http_client_proxies` | | Optional Reasoning Fields (`_OPTIONAL_FIELDS`) | `reasoning_effort`, `is_reasoning_model` | ```python # BaseLlmConfig fields copied into OpenAIConfig; the last two may be absent on older mem0. _COPIED_FIELDS = ("model", "temperature", "api_key", "max_tokens", "top_p", "top_k", "enable_vision", "vision_details", "http_client_proxies") _OPTIONAL_FIELDS = ("reasoning_effort", "is_reasoning_model") ``` Sources: [integrations/hermes-plugin-mem0/_openai_llm.py:13-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_openai_llm.py#L13-L16) > [!NOTE] > `DirectOpenAILLM` resolves API keys and base URLs through `agent.secret_scope.get_secret` rather than raw `os.environ` lookups. This ensures multiplexed secondary agent sessions execute fact extraction using their assigned profile credentials rather than falling back to default global environment variables or proxy accounts. ```python # OPENAI_API_KEY / OPENAI_BASE_URL are profile credentials: read them through the secret # scope, never raw os.environ, or a multiplexed secondary's memory extraction runs on the # default profile's OpenAI account (and its proxy). from agent.secret_scope import get_secret api_key = self.config.api_key or get_secret("OPENAI_API_KEY", "") if not api_key: raise ValueError("OpenAI API key is required for the Hermes Mem0 OSS provider") from openai import OpenAI self.client = OpenAI(api_key=api_key, base_url=self.config.openai_base_url or get_secret("OPENAI_API_BASE", "") or get_secret("OPENAI_BASE_URL", "") or "https://api.openai.com/v1", timeout=120.0) ``` Sources: [integrations/hermes-plugin-mem0/_openai_llm.py:39-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_openai_llm.py#L39-L47) ### Response Generation and Parameter Filtering The `generate_response` method filters supported parameters using `_get_supported_params`, injects the target model and conversation messages, and handles optional parameters such as storage flags, response formats, and tool dispatching. If a `response_callback` is defined in the configuration, it executes following completion and handles invocation exceptions internally via logging. ```python def generate_response(self, messages: List[Dict[str, str]], response_format=None, tools: Optional[List[Dict]] = None, tool_choice: str = "auto", **kwargs): params = self._get_supported_params(messages=messages, **kwargs) params.update({"model": self.config.model, "messages": messages}) # No OpenRouter-only fields; ``store`` is opt-in so OpenAI-compatible endpoints never receive unknown fields. if self.config.store is not None: params["store"] = self.config.store if response_format: params["response_format"] = response_format if tools: params["tools"], params["tool_choice"] = tools, tool_choice response = self.client.chat.completions.create(**params) parsed_response = self._parse_response(response, tools) if self.config.response_callback: try: self.config.response_callback(self, response, params) except Exception: logging.error("Error running Mem0 OpenAI response callback") return parsed_response ``` Sources: [integrations/hermes-plugin-mem0/_openai_llm.py:49-66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_openai_llm.py#L49-L66) ## OSS Providers and Vector Backends ### Supported LLM, Embedder, and Vector Store Registries The Hermes Mem0 plugin manages self-hosted OSS deployment configurations using provider registries defined in `_oss_providers.py`. These registries establish default models, required environment variables, API key policies, and optional package dependencies for each supported provider. | Section | Provider ID | Label | Default Model / Target | Default URL / Config | Required Env Var | Optional Pip Dependency | | :--- | :--- | :--- | :--- | :--- | :--- | :--- | | `LLM_PROVIDERS` | `openai` | OpenAI | `gpt-5-mini` | — | `OPENAI_API_KEY` | — | | `LLM_PROVIDERS` | `ollama` | Ollama (local) | `llama3.1:8b` | `http://localhost:11434` | — | `ollama` | | `EMBEDDER_PROVIDERS` | `openai` | OpenAI | `text-embedding-3-small` | — | `OPENAI_API_KEY` | — | | `EMBEDDER_PROVIDERS` | `ollama` | Ollama (local) | `nomic-embed-text` | `http://localhost:11434` | — | `ollama` | | `VECTOR_PROVIDERS` | `qdrant` | Qdrant | `mem0_qdrant` (path) | `~/.hermes/mem0_qdrant` | — | `qdrant-client` | | `VECTOR_PROVIDERS` | `pgvector` | PGVector | `postgres` (dbname) | `localhost:5432` | — | `psycopg2-binary` | Sources: [integrations/hermes-plugin-mem0/_oss_providers.py:10-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_oss_providers.py#L10-L29) Embedding dimensions for vector storage validation are mapped via `KNOWN_DIMS`. Supported models correspond to fixed vector lengths: ```python KNOWN_DIMS: dict[str, int] = {"text-embedding-3-small": 1536, "text-embedding-3-large": 3072, "text-embedding-ada-002": 1536, "nomic-embed-text": 768} ``` Sources: [integrations/hermes-plugin-mem0/_oss_providers.py:31-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_oss_providers.py#L31-L31) ### Configuration Validation and Vector Store Defaults The validation utility `validate_oss_config` inspects configuration blocks to ensure that every active section declares a known provider identifier. For `pgvector` deployments, validation verifies that a database user is specified within the vector configuration dictionary. ```python def validate_oss_config(oss_config: dict) -> list[str]: """Validate an OSS config dict. Returns list of error strings (empty = valid).""" errors: list[str] = [] for section, registry in SECTION_REGISTRIES: block = oss_config.get(section) if not block or not isinstance(block, dict): errors.append(f"Missing required section: {section}") elif block.get("provider", "") not in registry: errors.append(f"Unknown {section} provider '{block.get('provider', '')}'. Valid: {', '.join(registry.keys())}") vs = oss_config.get("vector_store", {}) if vs.get("provider") == "pgvector" and not vs.get("config", {}).get("user"): errors.append("PGVector requires 'user' in vector_store.config") return errors ``` Sources: [integrations/hermes-plugin-mem0/_oss_providers.py:41-53](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_oss_providers.py#L41-L53) Vector configurations containing callable default targets—such as profile-specific storage directories—are resolved dynamically at runtime through `vector_default_config`. This ensures that Qdrant file paths adapt correctly to active agent profiles rather than relying on static import-time paths. ```python def vector_default_config(provider_id: str) -> dict[str, Any]: """A vector store's ``default_config`` with callable defaults resolved for the active profile.""" return {k: (v() if callable(v) else v) for k, v in VECTOR_PROVIDERS[provider_id]["default_config"].items()} ``` Sources: [integrations/hermes-plugin-mem0/_oss_providers.py:33-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_oss_providers.py#L33-L36) > [!WARNING] > When configuring `pgvector` as the vector store backend, the underlying database must have the `vector` extension enabled. The setup utility attempts to execute `CREATE EXTENSION IF NOT EXISTS vector` automatically upon establishing a connection, but uninitialized database instances will fail dimension checks during memory initialization. ```python def _ensure_pgvector_extension(pg_config: dict) -> None: try: import psycopg2 except ImportError: return defaults = {"host": "localhost", "port": 5432, "user": "postgres", "dbname": "postgres"} try: conn = psycopg2.connect(**(defaults | {k: v for k, v in pg_config.items() if k in defaults or (k == "password" and v)})) conn.autocommit = True conn.cursor().execute("CREATE EXTENSION IF NOT EXISTS vector") conn.close() print(" ✓ pgvector extension enabled") except Exception as e: print(f" Warning: Could not enable pgvector extension: {e}") ``` Sources: [integrations/hermes-plugin-mem0/_setup.py:393-407](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py#L393-L407) ## Interactive and Automated Setup Wizard ### Setup Workflows and Mode Routing The Hermes Mem0 plugin provides setup capabilities through `post_setup`, which accepts `hermes_home` and configuration dictionaries. The setup entry point processes command-line flags and routes execution depending on the selected deployment mode: `platform`, `selfhosted` (or `self-hosted`), and `oss`. When no mode flag is provided, the wizard presents an interactive curses radio list using `_curses_select` over `_MODE_ITEMS`. ```python _MODE_HANDLERS = {"oss": _setup_oss, "selfhosted": _setup_selfhosted, "self-hosted": _setup_selfhosted, "platform": _setup_platform} # Interactive picker order: Platform, Self-hosted server, Open Source. _MODE_ITEMS = [("Platform", "Mem0 Cloud API (lightweight, just needs an API key)"), ("Self-hosted server", "Connect to an existing self-hosted Mem0 server (Docker/FastAPI)"), ("Open Source", "Run Mem0 locally (self-hosted LLM + vector store)")] _MODE_PICKER = (_setup_platform, _setup_selfhosted, _setup_oss) def post_setup(hermes_home: str, config: dict) -> None: """Entry point for `hermes memory setup`: routes on --mode (platform / selfhosted / oss), else shows a picker. OSS is non-interactive only when the mode came from the flag.""" with suppress(ImportError): # mem0ai must meet the minimum version from plugin.yaml import mem0 installed_ver = getattr(mem0, "__version__", None) if installed_ver and _version_tuple(installed_ver) < (2, 0, 10): print(f"\n ⚠ mem0ai {installed_ver} installed but >=2.0.10 required.\n Run: uv pip install --python {sys.executable} 'mem0ai>=2.0.10'") flags = parse_flags(sys.argv[1:]) handler = _MODE_HANDLERS.get(flags["mode"]) flags["_mode_from_flag"] = handler is not None if handler is None: handler = _MODE_PICKER[_curses_select(" Select mode", _MODE_ITEMS, 0)] handler(hermes_home, config, flags) ``` Sources: [integrations/hermes-plugin-mem0/_setup.py:532-552](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py#L532-L552) Platform mode configures Mem0 Cloud integration by prompting for the platform API key, user and agent identifiers, and reranking preferences, subsequently writing values into `mem0.json` and `.env`. Self-hosted mode prompts for a FastAPI server URL, server API key, and user identifiers, validating server reachability via `_check_selfhosted_server`. Open Source (OSS) mode configures local or remote LLMs, embedders, and vector providers either interactively or non-interactively via flags. ### CLI Configuration Flags and Environment Persistence The command-line parser extracts configuration parameters using `parse_flags`, supporting `--dry-run` and specific flag keys defined across `_FLAG_KEYS`. Default OSS settings assign OpenAI to both the LLM and embedder slots, with Qdrant as the default vector store. | Flag Key | Target Configuration Option | Default Value | Purpose | | :--- | :--- | :--- | :--- | | `--mode` | `flags["mode"]` | *None* | Selects deployment mode (`platform`, `selfhosted`, `oss`) | | `--api-key` | `flags["api_key"]` | *None* | Sets Mem0 Platform or server API key | | `--host` | `flags["host"]` | *None* | Sets self-hosted Mem0 server URL | | `--oss-llm` | `flags["oss_llm"]` | `openai` | Selects OSS LLM provider identifier | | `--oss-llm-model` | `flags["oss_llm_model"]` | *Provider default* | Specifies LLM model name | | `--oss-llm-url` | `flags["oss_llm_url"]` | *Provider default* | Configures LLM base URL endpoint | | `--oss-embedder` | `flags["oss_embedder"]` | `openai` | Selects OSS embedder provider identifier | | `--oss-vector` | `flags["oss_vector"]` | `qdrant` | Selects vector store provider (`qdrant`, `pgvector`) | | `--dry-run` | `flags["dry_run"]` | `False` | Simulates configuration writes without modifying files | Sources: [integrations/hermes-plugin-mem0/_setup.py:108-126](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py#L108-L126) > [!CAUTION] > If `MEM0_HOST` is explicitly set within the environment variables during platform mode setup, it overrides platform routing configuration. The setup utility inspects `os.environ.get("MEM0_HOST")` and warns users to unset it or remove it from `~/.hermes/.env` to prevent accidental request redirection to a self-hosted server. ```python if os.environ.get("MEM0_HOST", "").strip(): print(f"\n ⚠ MEM0_HOST is set in your environment ({os.environ['MEM0_HOST']}). It overrides platform mode — remove it from ~/.hermes/.env (or unset it) or Hermes will keep routing to the self-hosted server.") _persist_provider_config(hermes_home, config, provider_config, env_writes, "mem0", "API keys saved to .env") ``` Sources: [integrations/hermes-plugin-mem0/_setup.py:223-225](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py#L223-L225) Secrets and environment modifications are committed atomically via `_write_env`, which parses `.env` files using `utf-8-sig` encoding, updates existing key-value mappings, and enforces a strict file permission mask of `0o600`. ```python def _write_env(env_path: Path, env_writes: dict[str, str]) -> None: env_path.parent.mkdir(parents=True, exist_ok=True) # utf-8-sig like the canonical .env readers: a BOM'd first line would miss the key match and get duplicated. existing_lines = env_path.read_text(encoding="utf-8-sig").splitlines() if env_path.exists() else [] keys = [line.split("=", 1)[0].strip() if "=" in line and not line.startswith("#") else None for line in existing_lines] new_lines = [f"{k}={env_writes[k]}" if k in env_writes else line for k, line in zip(keys, existing_lines)] new_lines += [f"{k}={v}" for k, v in env_writes.items() if k not in keys] from utils import atomic_write_text atomic_write_text(env_path, "\n".join(new_lines) + "\n", mode=0o600) ``` Sources: [integrations/hermes-plugin-mem0/_setup.py:170-180](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py#L170-L180) ### Connectivity Validation Probes and Automated Dependency Management When initializing OSS mode, `_finish_oss` invokes dependency installation through `_install_provider_deps`, which checks package requirements against `SECTION_REGISTRIES` (`ollama`, `qdrant-client`, `psycopg2-binary`) and installs missing packages using `install_specs`. Connectivity checks are subsequently executed through `_run_connectivity_checks`. ```mermaid flowchart TD A["_finish_oss()"] --> B["_write_env() & save_config()"] B --> C["_install_provider_deps()"] C --> D{"pgvector config present?"} D -- Yes --> E["_ensure_pgvector_extension()"] D -- No --> F["_activate_provider()"] E --> F F --> G["_run_connectivity_checks()"] G --> H["_print_oss_summary()"] ``` Sources: [integrations/hermes-plugin-mem0/_setup.py:273-287](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py#L273-L287) Vector store and local LLM connectivity checks validate resource accessibility prior to finalizing setup: - **Qdrant (Local path):** Validates that the parent directory of the storage path is writable using `_check_qdrant_path`. - **Qdrant (Remote URL):** Probes health endpoints via `_http_get` against `/healthz` with a 3-second timeout. - **PGVector:** Tests TCP connectivity to the database host and port using `socket.create_connection`. - **Ollama:** Verifies local server responsiveness by querying `/api/tags`. > [!NOTE] > If a local PostgreSQL instance with `pgvector` is unreachable during interactive setup and Docker is installed, the wizard offers to automatically pull the `pgvector/pgvector:pg17` image and start a container named `hermes-pgvector` with a randomly generated secure password. ```python _PGVECTOR_CONTAINER, _PGVECTOR_IMAGE = "hermes-pgvector", "pgvector/pgvector:pg17" … def _ensure_pgvector(host: str = "localhost", port: int = 5432) -> dict | None: """Ensure pgvector is reachable, offering Docker if not; returns the started container's vector_config, else None.""" if _check_pgvector(host, port)[0]: print(f" ✓ PostgreSQL reachable at {host}:{port}") return None print(f" PostgreSQL not reachable at {host}:{port}") if not shutil.which("docker"): print(" Docker not found. Install Docker to auto-start pgvector,\n or run PostgreSQL with pgvector manually.") return None with suppress(Exception): # restart our own container if it exists but is stopped result = _docker("inspect", _PGVECTOR_CONTAINER, "--format", "{{.State.Status}}", timeout=10, text=True, encoding='utf-8', errors='replace') if result.returncode == 0 and "exited" in result.stdout: print(f" Found stopped container '{_PGVECTOR_CONTAINER}', restarting...") _docker("start", _PGVECTOR_CONTAINER, timeout=15) if _pg_ready(host, port, 15): print(" ✓ PostgreSQL container restarted") return None if input(" Start pgvector via Docker? [Y/n]: ").strip().lower() not in ("", "y", "yes"): print(" Skipping Docker setup. Make sure PostgreSQL with pgvector is running.") return None password = secrets.token_urlsafe(24) try: print(f" Pulling {_PGVECTOR_IMAGE}...") _docker("pull", _PGVECTOR_IMAGE, timeout=120) print(f" Starting container '{_PGVECTOR_CONTAINER}' on port {port}...") _docker("run", "-d", "--name", _PGVECTOR_CONTAINER, "-e", f"POSTGRES_PASSWORD={password}", "-p", f"127.0.0.1:{port}:5432", _PGVECTOR_IMAGE, timeout=30, check=True) if _pg_ready(host, port, 20): print(f" ✓ pgvector running on {host}:{port}") else: print(" Warning: Container started but PostgreSQL not yet accepting connections.\n It may need a few more seconds. Config will be saved; retry later.") return {"host": host, "port": port, "user": "postgres", "password": password, "dbname": "postgres"} except subprocess.CalledProcessError as e: print(f" Failed to start Docker container: {_scrub(str(e), password)}") except Exception as e: print(f" Docker error: {_scrub(str(e), password)}") return None ``` Sources: [integrations/hermes-plugin-mem0/_setup.py:34-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py#L34-L34), [integrations/hermes-plugin-mem0/_setup.py:320-355](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py#L320-L355) ## Related - [Python Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core) --- ## Technical docs: Vercel AI SDK Integration URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/vercel-ai-sdk-integration
Relevant source files The following files were used as context for generating this wiki page: - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts) - [mem0-ts/src/community/src/integrations/langchain/mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [integrations/vercel-ai-sdk/src/mem0-facade.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-facade.ts) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [integrations/vercel-ai-sdk/src/mem0-utils.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts) - [mem0/proxy/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/proxy/main.py) - [integrations/vercel-ai-sdk/src/mem0-provider.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider.ts) - [integrations/deepseek-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts) - [integrations/hermes-plugin-mem0/_backend.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py) - [mem0/llms/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/llms/__init__.py) - [integrations/vercel-ai-sdk/src/provider-response-provider.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/provider-response-provider.ts) - [integrations/mem0-strands/python/src/mem0_strands/client.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py) - [integrations/openclaw/providers.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/providers.ts) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [integrations/vercel-ai-sdk/src/mem0-provider-selector.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider-selector.ts) - [integrations/agent-plugin-core/typescript/src/lifecycle.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/lifecycle.ts) - [mem0-ts/src/oss/src/vector_stores/langchain.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/langchain.ts) - [mem0/vector_stores/langchain.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/langchain.py) - [mem0-ts/src/oss/src/llms/lmstudio.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/lmstudio.ts) - [mem0-ts/src/oss/src/llms/langchain.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/llms/langchain.ts) - [mem0/configs/llms/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/llms/__init__.py) - [mem0/proxy/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/proxy/__init__.py) - [integrations/vercel-ai-sdk/src/stream-utils.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/stream-utils.ts)
## Overview The Vercel AI SDK integration provides a specialized provider interface that bridges Mem0 long-term memory management directly into AI applications built with the Vercel AI SDK and the Version 3 language model specification. It exposes a configurable provider factory via `[[createMem0]]` that handles base URL resolution, headers, custom fetch wrappers, and API key management across multiple underlying LLM vendors. Sources: [integrations/vercel-ai-sdk/src/mem0-provider.ts:1-140](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider.ts#L1-L140), [integrations/vercel-ai-sdk/src/mem0-facade.ts:7-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-facade.ts#L7-L42) The architecture encapsulates generation and streaming calls within a generic language model wrapper (`Mem0GenericLanguageModel`) that automatically flattens prompts, queries relevant historical memories, and injects retrieved context as system messages prior to invocation. It dynamically routes requests through selected backend providers while capturing assistant completions to persist conversational turns back into Mem0. Sources: [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts:20-170](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts#L20-L170), [integrations/vercel-ai-sdk/src/mem0-utils.ts:309-390](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L309-L390) ## Provider Architecture and Client Factory ### Provider Configuration Options The public API surface revolves around the `[[createMem0]]` factory function and the `Mem0` class facade, which initialize provider instances conforming to the Vercel AI SDK `ProviderV3` specification. The `Mem0ProviderSettings` interface defines the configuration properties accepted by `createMem0()` and the `Mem0` class constructor. | Property | Type | Default Value | Description | | :--- | :--- | :--- | :--- | | `baseURL` | `string` | `"https://api.openai.com"` | Base URL for API requests, normalized via `withoutTrailingSlash`. | | `fetch` | `typeof fetch` | `undefined` | Custom fetch implementation for request interception or testing. | | `generateId` | `() => string` | `undefined` | Internal identifier generator function. | | `headers` | `Record` | `undefined` | Custom headers included in outgoing requests. | | `name` | `string` | `undefined` | Provider or model identifier name. | | `mem0ApiKey` | `string` | `undefined` | API key specific to Mem0 services. | | `apiKey` | `string` | `undefined` | General API key for the underlying model vendor. | | `provider` | `string` | `"openai"` | Underlying LLM provider identifier. | | `modelType` | `"completion" \| "chat"` | `undefined` | Target model type constraint. | | `mem0Config` | `Mem0Config` | `undefined` | Configuration object for memory extraction and retrieval. | | `config` | `LLMProviderSettings` | `undefined` | Provider-specific configuration options passed to language models. | Sources: [integrations/vercel-ai-sdk/src/mem0-provider.ts:20-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider.ts#L20-L46) ### Provider Instance Initialization The `createMem0` function instantiates a callable `Mem0Provider` function augmented with helper methods for constructing language models. When invoked directly, the provider checks `new.target` to guard against construction via the `new` keyword and defaults to creating a generic language model via `createGenericModel`. ```typescript export function createMem0( options: Mem0ProviderSettings = { provider: "openai", } ): Mem0Provider { const baseURL = withoutTrailingSlash(options.baseURL) ?? "https://api.openai.com"; const getHeaders = () => ({ ...options.headers, }); const createGenericModel = ( modelId: Mem0ChatModelId, settings: Mem0ChatSettings = {} ) => new Mem0GenericLanguageModel( modelId, settings, { baseURL, fetch: options.fetch, headers: getHeaders(), provider: options.provider || "openai", name: options.name, mem0ApiKey: options.mem0ApiKey, apiKey: options.apiKey, mem0Config: options.mem0Config, }, options.config ); const createCompletionModel = ( modelId: Mem0ChatModelId, settings: Mem0ChatSettings = {} ) => new Mem0GenericLanguageModel( modelId, settings, { baseURL, fetch: options.fetch, headers: getHeaders(), provider: options.provider || "openai", name: options.name, mem0ApiKey: options.mem0ApiKey, apiKey: options.apiKey, mem0Config: options.mem0Config, modelType: "completion", }, options.config ); const createChatModel = ( modelId: Mem0ChatModelId, settings: Mem0ChatSettings = {} ) => new Mem0GenericLanguageModel( modelId, settings, { baseURL, fetch: options.fetch, headers: getHeaders(), provider: options.provider || "openai", name: options.name, mem0ApiKey: options.mem0ApiKey, apiKey: options.apiKey, mem0Config: options.mem0Config, modelType: "chat", }, options.config ); const provider = function ( modelId: Mem0ChatModelId, settings: Mem0ChatSettings = {} ) { if (new.target) { throw new Error( "The Mem0 model function cannot be called with the new keyword." ``` Sources: [integrations/vercel-ai-sdk/src/mem0-provider.ts:48-140](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider.ts#L48-L140) > [!WARNING] > Calling the returned `Mem0Provider` instance with the `new` keyword throws an explicit runtime error: `"The Mem0 model function cannot be called with the new keyword."` Sources: [integrations/vercel-ai-sdk/src/mem0-provider.ts:125-129](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider.ts#L125-L129) ### The Mem0 Class Facade In parallel with `createMem0`, the `Mem0` class provides an alternative object-oriented facade wrapping base URL normalization, header aggregation, and model factory methods for chat and completion interfaces. ```typescript export class Mem0 { readonly baseURL: string readonly headers?: any constructor(options: Mem0ProviderSettings = { provider: 'openai', }) { this.baseURL = withoutTrailingSlash(options.baseURL) ?? 'https://api.openai.com' this.headers = options.headers } private get baseConfig() { return { baseURL: this.baseURL, headers: this.headers, } } chat(modelId: Mem0ChatModelId, settings: Mem0ChatSettings = {}) { return new Mem0GenericLanguageModel(modelId, settings, { provider: 'openai', modelType: 'chat', ...this.baseConfig, }) } completion(modelId: Mem0ChatModelId, settings: Mem0ChatSettings = {}) { return new Mem0GenericLanguageModel(modelId, settings, { provider: 'openai', modelType: 'completion', ...this.baseConfig, }) } } ``` Sources: [integrations/vercel-ai-sdk/src/mem0-facade.ts:7-42](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-facade.ts#L7-L42) ## Generic Language Model Wrapper Lifecycle ### Wrapper Initialization and V3 Specification Conformance The `Mem0GenericLanguageModel` class implements the Vercel AI SDK `LanguageModelV3` specification, exposing version `v3` and supporting wildcard URLs (`*`) matched by `/.*/`. The constructor accepts a chat model identifier, chat settings, a configuration object, and optional provider settings, defaulting the underlying provider string to `"openai"` if unspecified. ```typescript export class Mem0GenericLanguageModel implements LanguageModelV3 { readonly specificationVersion = "v3"; readonly supportedUrls: Record = { '*': [/.*/] }; constructor( public readonly modelId: Mem0ChatModelId, public readonly settings: Mem0ChatSettings, public readonly config: Mem0ChatConfig, public readonly provider_config?: Mem0ProviderSettings ) { this.provider = config.provider ?? "openai"; } provider: string; ``` Sources: [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts:20-35](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts#L20-L35) > [!NOTE] > The generic language model wrapper assigns `specificationVersion = "v3"` and exposes a wildcard `supportedUrls` mapping so that requests targeting any endpoint can be intercepted and enhanced with memory context. Sources: [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts:21-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts#L21-L24) ### Intercepting Generation and Streaming Calls When `doGenerate` or `doStream` is invoked with `LanguageModelV3CallOptions`, the wrapper constructs provider and memory configurations, initializes a `Mem0ClassSelector`, and processes conversational prompts through `processMemories`. This internal lifecycle method attempts to persist new memories before retrieving relevant historical context, formatting retrieved items into a specialized system prompt prepended to the prompt array. ```typescript private async processMemories(messagesPrompts: LanguageModelV3Message[], mem0Config: Mem0ConfigSettings) { try { // Add new memories — await to ensure writes complete before returning try { await addMemories(messagesPrompts, mem0Config); } catch (e) { console.error("Error while adding memories"); } // Get memories (always returns an array since graph support is removed) const memories: any[] = await getMemories(messagesPrompts, mem0Config) ?? []; const mySystemPrompt = "These are the memories I have stored. Give more weightage to the question by users and try to answer that first. You have to modify your answer based on the memories I have provided. If the memories are irrelevant you can ignore them. Also don't reply to this section of the prompt, or the memories, they are only for your reference. The System prompt starts after text System Message: \n\n"; let memoriesText = ""; try { memoriesText = memories ?.map((memory: any) => `Memory: ${memory?.memory}\n\n`) .join("\n\n"); } catch (e) { console.error("Error while parsing memories"); } const memoriesPrompt = `System Message: ${mySystemPrompt} ${memoriesText} `; // Clone the prompt array to avoid mutating the caller's reference on retries const updatedPrompts = [...messagesPrompts]; if (memories?.length > 0) { const systemPrompt: LanguageModelV3Message = { role: "system", content: memoriesPrompt }; updatedPrompts.unshift(systemPrompt); } return { memories, messagesPrompts: updatedPrompts }; } catch (e) { console.error("Error while processing memories"); return { memories: [], messagesPrompts: [...messagesPrompts] }; } } ``` Sources: [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts:37-78](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts#L37-L78) > [!CAUTION] > If memory processing encounters an exception during either persistence or retrieval, the error is caught, logged to `console.error`, and execution safely falls back to returning the original unmodified prompt array without historical context. Sources: [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts:38-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts#L38-L44), [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts:74-77](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts#L74-L77) ### Result Augmentation and Stream Handling In `doGenerate`, once the underlying provider model generates a response, the wrapper checks if any memories were retrieved. If memories are present, it constructs a `LanguageModelV3Source` object with a generated random identifier (`mem0-` followed by randomized alphanumeric characters) and attaches provider metadata containing the raw memories and concatenated memory text before returning the augmented result. ```typescript if (!memories || memories?.length === 0) { return ans; } const mem0Source: LanguageModelV3Source = { type: "source", sourceType: "url", id: "mem0-" + generateRandomId(), url: "https://app.mem0.ai?utm_source=oss&utm_medium=vercel-ai-sdk-src", title: "Mem0 Memories", providerMetadata: { mem0: { memories: memories, memoriesText: memories ?.map((memory: any) => memory?.memory) .join("\n\n"), }, }, }; return { ...ans, content: [...(ans.content ?? []), mem0Source], }; } ``` Sources: [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts:108-132](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts#L108-L132) Conversely, `doStream` delegates to the underlying provider model's `doStream` method using the updated memory-injected prompts, returning the raw stream response or throwing an error if streaming fails or remains unimplemented. ```typescript async doStream(options: LanguageModelV3CallOptions): Promise { try { const provider = this.config.provider; const mem0_api_key = this.config.mem0ApiKey; const settings: Mem0ProviderSettings = { provider: provider, mem0ApiKey: mem0_api_key, apiKey: this.config.apiKey, modelType: this.config.modelType, } const mem0Config: Mem0ConfigSettings = { mem0ApiKey: mem0_api_key, ...this.config.mem0Config, ...this.settings, } const selector = new Mem0ClassSelector(this.modelId, settings, this.provider_config); const { messagesPrompts: updatedPrompts } = await this.processMemories(options.prompt, mem0Config); const baseModel = selector.createProvider(); const streamResponse = await baseModel.doStream({ ...options, prompt: updatedPrompts, }); // Return the full stream response, preserving all V3 fields (warnings, etc.) return streamResponse; } catch (error) { console.error("Error in doStream:", error); throw new Error("Streaming failed or method not implemented."); } } ``` Sources: [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts:134-169](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts#L134-L169) ## Provider Selection and Response Generation ### Dynamic Provider Selection and Instantiation The `Mem0ClassSelector` class validates and instantiates the underlying AI provider. It accepts a `modelId`, `Mem0ProviderSettings`, and optional `ProviderSettings`, defaulting `provider_wrapper` to `"openai"` if `config.provider` is omitted. | Provider Identifier | Supported Status | Initialization Factory Function | | :--- | :--- | :--- | | `"openai"` | Supported | `createOpenAI()` | | `"anthropic"` | Supported | `createAnthropic()` | | `"cohere"` | Supported | `createCohere()` | | `"groq"` | Supported | `createGroq()` | | `"google"` / `"gemini"` | Supported | `createGoogleGenerativeAI()` | ```typescript class Mem0ClassSelector { modelId: string; provider_wrapper: string; config: Mem0ProviderSettings; provider_config?: ProviderSettings; static supportedProviders = ["openai", "anthropic", "cohere", "groq", "google", "gemini"]; constructor(modelId: string, config: Mem0ProviderSettings, provider_config?: ProviderSettings) { this.modelId = modelId; this.provider_wrapper = config.provider || "openai"; this.provider_config = provider_config; if(config) this.config = config; else this.config = { provider: this.provider_wrapper, }; // Check if provider_wrapper is supported if (!Mem0ClassSelector.supportedProviders.includes(this.provider_wrapper)) { throw new Error(`Model not supported: ${this.provider_wrapper}`); } } createProvider(): LanguageModelV3 { return new Mem0AITextGenerator(this.modelId, this.config , this.provider_config || {}); } } ``` Sources: [integrations/vercel-ai-sdk/src/mem0-provider-selector.ts:5-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider-selector.ts#L5-L30) > [!WARNING] > If `provider_wrapper` does not match any entry in `Mem0ClassSelector.supportedProviders`, the constructor throws an error stating `Model not supported: ${this.provider_wrapper}`. Sources: [integrations/vercel-ai-sdk/src/mem0-provider-selector.ts:10-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider-selector.ts#L10-L10), [integrations/vercel-ai-sdk/src/mem0-provider-selector.ts:21-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-provider-selector.ts#L21-L25) ### Text Generator Dispatch and Execution The `Mem0AITextGenerator` class implements the `LanguageModelV3` specification, exposing `specificationVersion = "v3"`, a `modelId`, a fixed provider string `"mem0"`, and `supportedUrls` matching any URL pattern (`'*': [/.*/]`). Its constructor uses a switch statement over `config.provider` to instantiate the appropriate underlying SDK client. For OpenAI, it supports specific model types (`completion`, `chat`, or default `languageModel`). ```typescript class Mem0AITextGenerator implements LanguageModelV3 { readonly specificationVersion = "v3"; readonly modelId: string; readonly provider = "mem0"; readonly supportedUrls: Record = { '*': [/.*/] }; private languageModel: any; // Use any type to avoid version conflicts constructor(modelId: string, config: Mem0ProviderSettings, provider_config: ProviderSettings) { this.modelId = modelId; switch (config.provider) { case "openai": if(config?.modelType === "completion"){ this.languageModel = createOpenAI({ apiKey: config?.apiKey, ...provider_config as OpenAIProviderSettings, }).completion(modelId); } else if(config?.modelType === "chat"){ this.languageModel = createOpenAI({ apiKey: config?.apiKey, ...provider_config as OpenAIProviderSettings, }).chat(modelId); } else { this.languageModel = createOpenAI({ apiKey: config?.apiKey, ...provider_config as OpenAIProviderSettings, }).languageModel(modelId); } break; case "cohere": this.languageModel = createCohere({ apiKey: config?.apiKey, ...provider_config as CohereProviderSettings, })(modelId); break; case "anthropic": this.languageModel = createAnthropic({ apiKey: config?.apiKey, ...provider_config as AnthropicProviderSettings, }).languageModel(modelId); break; case "groq": this.languageModel = createGroq({ apiKey: config?.apiKey, ...provider_config as GroqProviderSettings, })(modelId); break; case "google": case "gemini": this.languageModel = createGoogleGenerativeAI({ apiKey: config?.apiKey, ...provider_config as GoogleGenerativeAIProviderSettings, })(modelId); break; default: throw new Error("Invalid provider"); } } ``` Sources: [integrations/vercel-ai-sdk/src/provider-response-provider.ts:10-69](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/provider-response-provider.ts#L10-L69) > [!CAUTION] > If an unrecognized provider string is passed to `Mem0AITextGenerator`, the switch statement triggers its default branch and throws an `Error("Invalid provider")`. Sources: [integrations/vercel-ai-sdk/src/provider-response-provider.ts:66-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/provider-response-provider.ts#L66-L68) The `doGenerate` and `doStream` methods delegate call options directly to the instantiated `languageModel` instance, returning typed `LanguageModelV3GenerateResult` and `LanguageModelV3StreamResult` responses respectively. ```typescript async doGenerate(options: LanguageModelV3CallOptions): Promise { const result = await this.languageModel.doGenerate(options); return result as LanguageModelV3GenerateResult; } async doStream(options: LanguageModelV3CallOptions): Promise { const result = await this.languageModel.doStream(options); return result as LanguageModelV3StreamResult; } } ``` Sources: [integrations/vercel-ai-sdk/src/provider-response-provider.ts:71-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/provider-response-provider.ts#L71-L80) ## Prompt Flattening and Memory Retrieval ### Multimodal Content Conversion and Flattening The `flattenPrompt` utility extracts and flattens user message prompts from a `LanguageModelV3Prompt` structure. It maps over prompt parts, verifying if the role is `"user"`, and handles string content, text array objects, or specific file and multimodal media types. File attachments mapped by `mediaType` yield standard text descriptions (`[PDF document]`, `[Markdown document]`, `[Image]`, or `[File attachment]`), while unhandled types resolve to `[multimodal content]`. ```typescript const flattenPrompt = (prompt: LanguageModelV3Prompt) => { try { return prompt.map((part) => { if (part.role === "user") { if (typeof part.content === 'string') { return part.content; } else if (Array.isArray(part.content)) { return part.content .filter((obj) => obj.type === 'text') .map((obj) => obj.text) .join(" "); } else if (part.content && typeof part.content === 'object' && 'type' in part.content) { const content = part.content as any; if (content.type === 'text' && content.text) { return content.text; } else if (content.type === 'file') { if (content.mediaType === 'application/pdf') { return '[PDF document]'; } else if (content.mediaType === 'text/markdown' || content.mediaType === 'application/mdx') { return '[Markdown document]'; } else if (content.mediaType && content.mediaType.startsWith('image/')) { return '[Image]'; } else { return '[File attachment]'; } } } return "[multimodal content]"; } return ""; }).join(" "); } catch (error) { console.error("Error in flattenPrompt:", error); return ""; } } ``` Sources: [integrations/vercel-ai-sdk/src/mem0-utils.ts:36-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L36-L71) Complementing prompt flattening, `convertToMem0Format` transforms messages into structures accepted by the backend. It handles string contents, text arrays, and specific attachment types including PDFs (`pdf_url`), Markdown or MDX documents (`mdx_url`), and images (`image_url`), normalizing various schema aliases. ```typescript const convertToMem0Format = (messages: LanguageModelV3Prompt) => { try { return messages.flatMap((message: any) => { try { if (typeof message.content === 'string') { return { role: message.role, content: message.content, }; } else if (Array.isArray(message.content)) { return message.content.map((obj: any) => { try { if (obj.type === "text") { return { role: message.role, content: obj.text, }; } else if (obj.type === "file") { if (obj.mediaType === "application/pdf") { return { role: message.role, content: { type: "pdf_url", pdf_url: { url: obj.data } } }; } else if (obj.mediaType === "text/markdown" || obj.mediaType === "application/mdx") { return { role: message.role, content: { type: "mdx_url", mdx_url: { url: obj.data } } }; } else if (obj.mediaType && obj.mediaType.startsWith("image/")) { return { role: message.role, content: { type: "image_url", image_url: { url: obj.data } } }; } } else if (obj.type === "image_url" || obj.type === "image") { return { role: message.role, content: { type: "image_url", image_url: { url: obj.image_url?.url || obj.image?.url || obj.url } } }; } else if (obj.type === "mdx_url" || obj.type === "mdx") { return { role: message.role, content: { type: "mdx_url", mdx_url: { url: obj.mdx_url?.url || obj.mdx?.url || obj.url } } }; } else if (obj.type === "pdf_url" || obj.type === "pdf") { return { role: message.role, content: { type: "pdf_url", pdf_url: { url: obj.pdf_url?.url || obj.pdf?.url || obj.url } } }; ``` Sources: [integrations/vercel-ai-sdk/src/mem0-utils.ts:73-154](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L73-L154) > [!NOTE] > `flattenPrompt` catches internal mapping errors and safely returns an empty string `""` rather than crashing the execution pipeline. Sources: [integrations/vercel-ai-sdk/src/mem0-utils.ts:67-70](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L67-L70) ### Memory Search and System Prompt Injection The `processMemories` method in `Mem0GenericLanguageModel` orchestrates persistence and retrieval during generation turns. It invokes `addMemories` to record conversational turns, queries relevant items via `getMemories`, and formats the retrieved records into a structured system prompt prefix. ```typescript private async processMemories(messagesPrompts: LanguageModelV3Message[], mem0Config: Mem0ConfigSettings) { try { // Add new memories — await to ensure writes complete before returning try { await addMemories(messagesPrompts, mem0Config); } catch (e) { console.error("Error while adding memories"); } // Get memories (always returns an array since graph support is removed) const memories: any[] = await getMemories(messagesPrompts, mem0Config) ?? []; const mySystemPrompt = "These are the memories I have stored. Give more weightage to the question by users and try to answer that first. You have to modify your answer based on the memories I have provided. If the memories are irrelevant you can ignore them. Also don't reply to this section of the prompt, or the memories, they are only for your reference. The System prompt starts after text System Message: \n\n"; let memoriesText = ""; try { memoriesText = memories ?.map((memory: any) => `Memory: ${memory?.memory}\n\n`) .join("\n\n"); } catch (e) { console.error("Error while parsing memories"); } const memoriesPrompt = `System Message: ${mySystemPrompt} ${memoriesText} `; // Clone the prompt array to avoid mutating the caller's reference on retries const updatedPrompts = [...messagesPrompts]; if (memories?.length > 0) { const systemPrompt: LanguageModelV3Message = { role: "system", content: memoriesPrompt }; updatedPrompts.unshift(systemPrompt); } return { memories, messagesPrompts: updatedPrompts }; } catch (e) { console.error("Error while processing memories"); return { memories: [], messagesPrompts: [...messagesPrompts] }; } } ``` Sources: [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts:37-78](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts#L37-L78) | Step | Operation | Description | | :--- | :--- | :--- | | 1 | `addMemories` | Persists user messages to the backend using `updateMemories`. | | 2 | `getMemories` | Searches for contextually relevant past memories based on the prompt. | | 3 | System Prefix | Prepends guidance instructing the model to weigh user questions against memories. | | 4 | Prompt Injection | Unshifts a system message containing the formatted memories to `updatedPrompts`. | ```typescript await addMemories(messagesPrompts, mem0Config); } catch (e) { console.error("Error while adding memories"); } // Get memories (always returns an array since graph support is removed) const memories: any[] = await getMemories(messagesPrompts, mem0Config) ?? []; const mySystemPrompt = "These are the memories I have stored. Give more weightage to the question by users and try to answer that first. You have to modify your answer based on the memories I have provided. If the memories are irrelevant you can ignore them. Also don't reply to this section of the prompt, or the memories, they are only for your reference. The System prompt starts after text System Message: \n\n"; let memoriesText = ""; try { memoriesText = memories ?.map((memory: any) => `Memory: ${memory?.memory}\n\n`) .join("\n\n"); } catch (e) { console.error("Error while parsing memories"); } const memoriesPrompt = `System Message: ${mySystemPrompt} ${memoriesText} `; // Clone the prompt array to avoid mutating the caller's reference on retries const updatedPrompts = [...messagesPrompts]; if (memories?.length > 0) { const systemPrompt: LanguageModelV3Message = { role: "system", content: memoriesPrompt }; updatedPrompts.unshift(systemPrompt); } ``` Sources: [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts:41-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts#L41-L71) Similarly, `retrieveMemories` flattens the input prompt or string, searches internal memories via `searchInternalMemories`, and constructs a formatted system instruction string if results exist. ```typescript const retrieveMemories = async (prompt: LanguageModelV3Prompt | string, config?: Mem0ConfigSettings) => { try { const message = typeof prompt === 'string' ? prompt : flattenPrompt(prompt); const systemPrompt = "These are the memories I have stored. Give more weightage to the question by users and try to answer that first. You have to modify your answer based on the memories I have provided. If the memories are irrelevant you can ignore them. Also don't reply to this section of the prompt, or the memories, they are only for your reference. The System prompt starts after text System Message: \n\n"; const data = await searchInternalMemories(message, config); // The API response may be a flat array or an object with a results key const memories: any[] = Array.isArray(data) ? data : (data?.results ?? []); if (memories.length === 0) { return ""; } const memoriesText = memories .map((memory: any) => `Memory: ${memory.memory}\n\n`) .join("\n\n"); return `System Message: ${systemPrompt} ${memoriesText}`; } catch (error) { console.error("Error in retrieveMemories:", error); throw error; } } ``` Sources: [integrations/vercel-ai-sdk/src/mem0-utils.ts:367-389](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L367-L389) > [!WARNING] > If memory retrieval returns an empty array or fails, `processMemories` clones the original prompt array without unshifting a system message, preventing empty injection blocks. Sources: [integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts:63-76](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts#L63-L76) ## Memory Persistence and Post-Generation Hooks ### Adding Memories and Request Payload Assembly The `addMemories` utility formats input prompts into structured message payloads before sending them to the backend via `updateMemories`. If the input is a plain string, it wraps it in a single user message object; otherwise, it translates the messages using `convertToMem0Format`. ```typescript const addMemories = async (messages: LanguageModelV3Prompt, config?: Mem0ConfigSettings) => { try { let finalMessages: Array = []; if (typeof messages === "string") { finalMessages = [{ role: "user", content: messages }]; } else { finalMessages = convertToMem0Format(messages); } const response = await updateMemories(finalMessages, config); return response; } catch (error) { console.error("Error in addMemories:", error); throw error; } } ``` Sources: [integrations/vercel-ai-sdk/src/mem0-utils.ts:309-323](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L309-L323) The `updateMemories` function loads authentication credentials via `loadApiKey` checking the `MEM0_API_KEY` environment variable or configuration settings. It constructs a POST request body containing the messages along with optional identifiers and parameters. | Property | Field Type | Description | | :--- | :--- | :--- | | `messages` | `Array` | The array of conversation turns to persist. | | `user_id` | `string` | Optional user scope identifier. | | `app_id` | `string` | Optional application scope identifier. | | `agent_id` | `string` | Optional agent scope identifier. | | `run_id` | `string` | Optional execution run identifier. | | `metadata` | `object` | Optional custom metadata key-value pairs. | | `infer` | `boolean` | Optional flag controlling memory extraction inference. | ```typescript const updateMemories = async (messages: Array, config?: Mem0ConfigSettings) => { try { const apiKey = loadApiKey({ apiKey: config?.mem0ApiKey, environmentVariableName: "MEM0_API_KEY", description: "Mem0", }); const body: Record = { messages }; if (config?.user_id) body.user_id = config.user_id; if (config?.app_id) body.app_id = config.app_id; if (config?.agent_id) body.agent_id = config.agent_id; if (config?.run_id) body.run_id = config.run_id; if (config?.metadata) body.metadata = config.metadata; if (config?.infer != null) body.infer = config.infer; ``` Sources: [integrations/vercel-ai-sdk/src/mem0-utils.ts:325-340](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L325-L340) > [!NOTE] > `updateMemories` targets the `/v3/memories/add/` endpoint on the configured host, defaulting to `https://api.mem0.ai` when no host setting is provided. Sources: [integrations/vercel-ai-sdk/src/mem0-utils.ts:354-355](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L354-L355) ### HTTP Headers and Version Attribution When dispatching requests to the Mem0 backend, `updateMemories` attaches explicit attribution and authorization headers to every outgoing fetch call. ```typescript const options = { method: 'POST', headers: { Authorization: `Token ${apiKey}`, 'Content-Type': 'application/json', // Surface attribution. Set-once by contract: this wrapper is the // outermost layer on these raw fetch calls. 'X-Mem0-Source': 'VERCEL_AI_SDK', 'X-Mem0-Client': `mem0-vercel-ai-provider/${PROVIDER_VERSION}` }, body: JSON.stringify(body), }; ``` Sources: [integrations/vercel-ai-sdk/src/mem0-utils.ts:341-352](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L341-L352) | Header Name | Value / Source | Purpose | | :--- | :--- | :--- | | `Authorization` | `Token ${apiKey}` | Authenticates the API request using the loaded credentials. | | `Content-Type` | `application/json` | Specifies the JSON serialization format of the request body. | | `X-Mem0-Source` | `VERCEL_AI_SDK` | Surfaces provider attribution set-once by contract as the outermost layer. | | `X-Mem0-Client` | `mem0-vercel-ai-provider/${PROVIDER_VERSION}` | Identifies the client wrapper version, resolving to `dev` unbundled or `__MEM0_PROVIDER_VERSION__` at build time. | ```typescript declare const __MEM0_PROVIDER_VERSION__: string | undefined; // Replaced at build time by tsup `define`. The fallback only applies when the // source is run unbundled, such as in tests. const PROVIDER_VERSION = typeof __MEM0_PROVIDER_VERSION__ !== "undefined" ? __MEM0_PROVIDER_VERSION__ : "dev"; … const options = { method: 'POST', headers: { Authorization: `Token ${apiKey}`, 'Content-Type': 'application/json', // Surface attribution. Set-once by contract: this wrapper is the // outermost layer on these raw fetch calls. 'X-Mem0-Source': 'VERCEL_AI_SDK', 'X-Mem0-Client': `mem0-vercel-ai-provider/${PROVIDER_VERSION}` }, body: JSON.stringify(body), }; ``` Sources: [integrations/vercel-ai-sdk/src/mem0-utils.ts:1-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L1-L7), [integrations/vercel-ai-sdk/src/mem0-utils.ts:341-352](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L341-L352) > [!WARNING] > If the fetch response status is not successful (`!response.ok`), `updateMemories` throws an HTTP error containing the response status code. Sources: [integrations/vercel-ai-sdk/src/mem0-utils.ts:356-358](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts#L356-L358) ## Streaming Chunk Transformation and Filtering ### Stream Interceptor Pipeline The streaming architecture utilizes `filterStream` to process and intercept response chunks generated during `doStream` invocations. The interceptor wraps an original `ReadableStream` by acquiring its reader and constructing a new stream that reads chunks asynchronously within a continuous loop. ```typescript async function filterStream(originalStream: ReadableStream) { const reader = originalStream.getReader(); const filteredStream = new ReadableStream({ async start(controller) { while (true) { const { done, value } = await reader.read(); if (done) { controller.close(); break; } try { const chunk = JSON.parse(value); if (chunk.type !== "step-finish") { controller.enqueue(value); } } catch (error) { if (!(value.type==='step-finish')) { controller.enqueue(value); } } } } }); return filteredStream; } ``` Sources: [integrations/vercel-ai-sdk/src/stream-utils.ts:1-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/stream-utils.ts:1-26) ### Chunk Filtering Logic As chunks are read from the underlying stream, the transformation pipeline attempts to parse each value as JSON. If parsing succeeds, chunks whose `.type` property matches `"step-finish"` are filtered out and omitted from the output controller. ```typescript try { const chunk = JSON.parse(value); if (chunk.type !== "step-finish") { controller.enqueue(value); } } catch (error) { ``` Sources: [integrations/vercel-ai-sdk/src/stream-utils.ts:11-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/stream-utils.ts:11-16) > [!NOTE] > If JSON parsing throws an error, the interceptor falls back to checking the raw value object directly, enqueuing the chunk only if its type field is not equal to `'step-finish'`. Sources: [integrations/vercel-ai-sdk/src/stream-utils.ts:16-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/stream-utils.ts:16-20) ## Related - [Typescript Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/typescript-core/typescript-core) --- ## Technical docs: Mem0 Strands URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/mem0-strands
Relevant source files The following files were used as context for generating this wiki page: - [integrations/mem0-strands/python/src/mem0_strands/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/__init__.py) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/mem0-strands/python/src/mem0_strands/store.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/mem0-strands/python/src/mem0_strands/client.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [integrations/antigravity-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_cli.py) - [integrations/cursor-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_cli.py) - [mem0-ts/src/community/src/integrations/langchain/mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts) - [mem0/embeddings/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/embeddings/__init__.py) - [integrations/mem0-strands/python/src/mem0_strands/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py) - [mem0-ts/src/oss/src/prompts/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/prompts/index.ts) - [mem0/memory/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/__init__.py) - [mem0/client/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py) - [integrations/mem0-strands/python/pyproject.toml](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/pyproject.toml) - [mem0/configs/embeddings/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/configs/embeddings/__init__.py) - [mem0/vector_stores/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/vector_stores/__init__.py) - [mem0/client/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/__init__.py) - [mem0/configs/rerankers/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/__init__.py#L1-L11), [integrations/mem0-strands/python/src/mem0_strands/store.py:1-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py#L1-L10) ## 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/pyproject.toml#L6-L6) | | `version` | `0.1.1` | [integrations/mem0-strands/python/pyproject.toml:7-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/pyproject.toml#L7-L7) | | `requires-python` | `>=3.10` | [integrations/mem0-strands/python/pyproject.toml:10-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/pyproject.toml#L10-L10) | | `license` | `Apache-2.0` | [integrations/mem0-strands/python/pyproject.toml:11-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/pyproject.toml#L11-L11) | Sources: [integrations/mem0-strands/python/pyproject.toml:1-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/pyproject.toml#L1-L25) 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/pyproject.toml#L27-L32) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/pyproject.toml#L27-L28) ### 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. ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/__init__.py#L13-L26) ## 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py#L66-L84) | | `agent_id` | Mem0 agent namespace | [integrations/mem0-strands/python/src/mem0_strands/store.py:67-85](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py#L67-L85) | | `run_id` | Mem0 run/session namespace | [integrations/mem0-strands/python/src/mem0_strands/store.py:68-86](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py#L68-L86) | | `app_id` | Mem0 app namespace (platform only) | [integrations/mem0-strands/python/src/mem0_strands/store.py:69-87](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py#L69-L87) | Sources: [integrations/mem0-strands/python/src/mem0_strands/store.py:50-124](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py#L50-L124) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py#L115-L123) ### 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`. ```python @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._client ``` Sources: [integrations/mem0-strands/python/src/mem0_strands/store.py:141-166](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py#L141-L166) 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`). ```python DEFAULT_MAX_SEARCH_RESULTS = 5 ``` Sources: [integrations/mem0-strands/python/src/mem0_strands/store.py:48-48](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py#L48-L48) ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py#L168-L186) ## 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 a `ValueError` because the store executes synchronous SDK operations inside worker threads via `asyncio.to_thread`. - Configuration `config`: Imports `Memory` from `mem0` and constructs a self-hosted backend via `Memory.from_config(config)`, setting `is_platform` to `False`. - Default hosted platform: Imports `MemoryClient` from `mem0`, resolves the API key from the explicit `api_key` argument or the `MEM0_API_KEY` environment variable, and initializes the platform client with an optional custom `host`. ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py#L60-L113) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py#L24-L28), [integrations/mem0-strands/python/src/mem0_strands/client.py:81-89](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py#L81-L89) ### 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. ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py#L114-L126) 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. ```python # 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py#L30-L34) ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py#L127-L129) 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py#L131-L144) | | `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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py#L146-L154) | | `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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py#L155-L163) | Sources: [integrations/mem0-strands/python/src/mem0_strands/client.py:131-164](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py#L131-L164) 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. ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/client.py#L166-L176) ## 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. ```python ``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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py#L10-L19) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/store.py#L187-L191) ## 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. ```python 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py#L55-L68) > [!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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py#L9-L13) 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. ```python Opt out with MEM0_TELEMETRY=false. ``` Sources: [integrations/mem0-strands/python/src/mem0_strands/telemetry.py:12-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py#L12-L12) ```python except Exception: pass ``` Sources: [integrations/mem0-strands/python/src/mem0_strands/telemetry.py:69-70](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py#L69-L70) ### 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py#L49-L53) 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](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py#L36-L37) | | `"401"`, `"403"`, `"unauthor"`, or `"forbidden"` | `"auth"` | [integrations/mem0-strands/python/src/mem0_strands/telemetry.py:38-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py#L38-L39) | | `"429"` or `"rate limit"` | `"rate-limited"` | [integrations/mem0-strands/python/src/mem0_strands/telemetry.py:40-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py#L40-L41) | | `"500"`, `"502"`, `"503"`, or `"504"` | `"server-error"` | [integrations/mem0-strands/python/src/mem0_strands/telemetry.py:42-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py#L42-L43) | | `"400"` or `"422"` | `"bad-request"` | [integrations/mem0-strands/python/src/mem0_strands/telemetry.py:44-45](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py#L44-L45) | | Unmatched exceptions | `type(exc).__name__` | [integrations/mem0-strands/python/src/mem0_strands/telemetry.py:46-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py#L46-L46) | Sources: [integrations/mem0-strands/python/src/mem0_strands/telemetry.py:33-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py#L33-L46) ## Related - [Memory Lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle) --- ## Technical docs: n8n Workflow Nodes URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/n8n-workflow-nodes
Relevant source files The following files were used as context for generating this wiki page: - [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/openclaw/cli/commands.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts) - [integrations/n8n-nodes-mem0/index.js](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/index.js) - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [cli/node/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/index.ts) - [mem0-ts/src/community/src/integrations/langchain/mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.json) - [integrations/n8n-nodes-mem0/package.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/package.json) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [integrations/vercel-ai-sdk/src/mem0-utils.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts) - [integrations/pi-agent-plugin/src/memory/tools.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts) - [integrations/openclaw/openclaw.plugin.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/openclaw.plugin.json) - [integrations/openclaw/providers.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/providers.ts) - [integrations/zapier-mem0/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts) - [integrations/opencode-plugin/index.d.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/index.d.ts) - [mem0/memory/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/__init__.py) - [integrations/zapier-mem0/src/creates/add_memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/add_memory.ts) - [integrations/openclaw/types.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/types.ts) - [cli/cli-spec.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/cli-spec.json) - [mem0-ts/src/community/package.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/package.json)
## Overview The Mem0 community node package for n8n integrates long-term memory capabilities into automated workflows and AI agent pipelines. By leveraging the Mem0 platform API, the node allows workflows to add, search, retrieve, update, and delete persistent memories across conversations with support for tool usage in AI nodes. Sources: [integrations/n8n-nodes-mem0/package.json:2-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/package.json#L2-L12), [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:20-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L20-L41) ## Community Node Package Registration ### Package Metadata and n8n Bindings The `@mem0/n8n-nodes-mem0` package identifies as an n8n community node package via its package.json manifest. The package requires Node.js `>=20.15` and exposes an `n8n` configuration block that declares the node API version, compiled credential files, and compiled node files. ```json { "name": "@mem0/n8n-nodes-mem0", "version": "0.1.4", "engines": { "node": ">=20.15" }, "n8n": { "n8nNodesApiVersion": 1, "credentials": [ "dist/credentials/Mem0Api.credentials.js" ], "nodes": [ "dist/nodes/Mem0/Mem0.node.js" ] } } ``` Sources: [integrations/n8n-nodes-mem0/package.json:2-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/package.json#L2-L3), [integrations/n8n-nodes-mem0/package.json:24-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/package.json#L24-L26), [integrations/n8n-nodes-mem0/package.json:43-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/package.json#L43-L51) > [!NOTE] > The `n8n` property in package.json dictates how the n8n runtime discovers compiled JavaScript assets under the `dist/` directory rather than raw TypeScript source files. Sources: [integrations/n8n-nodes-mem0/package.json:43-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/package.json#L43-L51) ### Node Declaration and Categories The node definition file `Mem0.node.json` registers the package identifier `@mem0/n8n-nodes-mem0.mem0` with version `1.0` and codex version `1.0`. It classifies the node under the `AI` category with a subcategory of `Memory`, and points to official documentation URLs for primary features and credentials. | Metadata Property | Value / Target | | :--- | :--- | | Node Identifier | `@mem0/n8n-nodes-mem0.mem0` | | Node Version | `1.0` | | Codex Version | `1.0` | | Categories | `AI` (Subcategory: `Memory`) | | Primary Documentation | `https://docs.mem0.ai/integrations/n8n` | | Credential Documentation | `https://docs.mem0.ai/integrations/n8n` | Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.json:1-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.json#L1-L20) ### Entry Point Mechanics The root entry point `index.js` acts as a placeholder module export for the package. Because n8n loads nodes and credentials directly via the paths declared in the `n8n` key of `package.json`, this entry point remains intentionally empty. ```javascript // n8n loads nodes and credentials via the "n8n" key in package.json. // This entry point is intentionally empty. module.exports = {}; ``` Sources: [integrations/n8n-nodes-mem0/index.js:1-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/index.js#L1-L3) ## Public Node Interface and Properties ### Node Metadata and Connectivity The `Mem0` class implements the `INodeType` interface, exposing core descriptive metadata and connection capabilities used by the n8n runtime. The node is categorized under the `transform` group, operates on version `1`, and displays a subtitle derived from the current operation and resource parameters `={{$parameter["operation"] + ": " + $parameter["resource"]}}`. Its visual representation uses light and dark SVG icons (`file:mem0.svg`), and it supports execution as a tool (`usableAsTool: true`) inside n8n AI Agent nodes. The node accepts a single main input and produces a single main output, requiring the `mem0Api` credential for authentication. ```typescript description: INodeTypeDescription = { displayName: 'Mem0', name: 'mem0', icon: { light: 'file:mem0.svg', dark: 'file:mem0.svg' }, group: ['transform'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Add, search, and manage long-term memories with Mem0', defaults: { name: 'Mem0', }, // Makes the node available to the AI Agent (Tools Agent) node. usableAsTool: true, inputs: [NodeConnectionTypes.Main], outputs: [NodeConnectionTypes.Main], credentials: [ { name: 'mem0Api', required: true, }, ], ``` Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:21-41](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L21-L41) > [!NOTE] > Setting `usableAsTool: true` in the node description allows LLM agent workflows within n8n to discover and invoke Mem0 memory actions as callable tools without manual routing setup. Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:32-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L32-L34) ### Supported Resources and Operations The user interface exposes a primary `Resource` selector fixed to the `memory` value, alongside an `Operation` dropdown that dictates the action executed against the platform. Each operation maps to specific backend capabilities for managing long-term memories. | Operation Name | Value Identifier | Action Description | | :--- | :--- | :--- | | Add | `add` | Extract and store memories from messages | | Delete | `delete` | Delete a single memory by ID | | Get | `get` | Retrieve a single memory by ID | | Get Many | `getAll` | List stored memories for an entity | | Search | `search` | Semantic search over stored memories | | Update | `update` | Update the text or metadata of a memory | Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:43-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L43-L96) ```typescript { displayName: 'Resource', name: 'resource', type: 'options', noDataExpression: true, options: [{ name: 'Memory', value: 'memory' }], default: 'memory', }, { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: ['memory'] } }, options: [ { name: 'Add', value: 'add', action: 'Add a memory', description: 'Extract and store memories from messages', }, { name: 'Delete', value: 'delete', action: 'Delete a memory', description: 'Delete a single memory by ID', }, { name: 'Get', value: 'get', action: 'Get a memory', description: 'Retrieve a single memory by ID', }, { name: 'Get Many', value: 'getAll', action: 'Get many memories', description: 'List stored memories for an entity', }, { name: 'Search', value: 'search', action: 'Search memories', description: 'Semantic search over stored memories', }, { name: 'Update', value: 'update', action: 'Update a memory', description: 'Update the text or metadata of a memory', }, ], default: 'add', }, ``` Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:43-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L43-L96) ### Parameter Configuration Properties The node properties define UI input fields conditionally displayed based on the selected resource and operation. For the `add` operation, users provide conversation messages with configurable roles (`User`, `Assistant`, `System`), a `User ID` string, a boolean `Wait for Completion` flag (defaulting to `true`), and additional fields. Additional fields include string identifiers (`agent_id`, `app_id`), custom categories (`custom_categories`), custom instructions (`custom_instructions`), and optional exclusion filters. ```typescript { displayName: 'Messages', name: 'messages', placeholder: 'Add Message', type: 'fixedCollection', typeOptions: { multipleValues: true }, displayOptions: { show: { resource: ['memory'], operation: ['add'] } }, default: {}, description: 'The conversation messages to extract memories from', options: [ { name: 'message', displayName: 'Message', values: [ { displayName: 'Role', name: 'role', type: 'options', options: [ { name: 'User', value: 'user' }, { name: 'Assistant', value: 'assistant' }, { name: 'System', value: 'system' }, ], default: 'user', }, { displayName: 'Content', name: 'content', type: 'string', typeOptions: { rows: 2 }, default: '', }, ], }, ], }, { displayName: 'User ID', name: 'userId', type: 'string', default: '', displayOptions: { show: { resource: ['memory'], operation: ['add'] } }, description: 'Associate the memories with this user', }, { displayName: 'Wait for Completion', name: 'waitForCompletion', type: 'boolean', default: true, displayOptions: { show: { resource: ['memory'], operation: ['add'] } }, description: 'Whether to poll until memory extraction finishes and return the resulting memories. Turn off to return immediately with the event ID.', }, { displayName: 'Additional Fields', name: 'addFields', type: 'collection', placeholder: 'Add Field', default: {}, displayOptions: { show: { resource: ['memory'], operation: ['add'] } }, options: [ { displayName: 'Agent ID', name: 'agent_id', type: 'string', default: '', }, { displayName: 'App ID', name: 'app_id', type: 'string', default: '', }, { displayName: 'Custom Categories', name: 'custom_categories', type: 'json', default: '', description: 'Optional taxonomy for categorising extracted memories, as a JSON array of {category: description} objects', ``` Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:99-190](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L99-L190) For retrieval and management operations, the UI exposes pagination parameters (`Page`, `Page Size`, `Return All`), direct ID lookup fields (`Memory ID`), and update parameters (`Text`, `Metadata (JSON)`). ```typescript { displayName: 'Page', name: 'page', type: 'number', typeOptions: { minValue: 1 }, default: 1, displayOptions: { show: { resource: ['memory'], operation: ['getAll'], returnAll: [false] }, }, }, { displayName: 'Page Size', name: 'pageSize', type: 'number', typeOptions: { minValue: 1 }, default: 50, displayOptions: { show: { resource: ['memory'], operation: ['getAll'] } }, }, // ---- Get / Update / Delete (by ID) ------------------------------- { displayName: 'Memory ID', name: 'memoryId', type: 'string', default: '', required: true, displayOptions: { show: { resource: ['memory'], operation: ['get', 'update', 'delete'] }, }, }, { displayName: 'Text', name: 'text', type: 'string', default: '', displayOptions: { show: { resource: ['memory'], operation: ['update'] } }, description: 'The new memory text', }, { displayName: 'Metadata (JSON)', name: 'metadata', type: 'json', default: '', displayOptions: { show: { resource: ['memory'], operation: ['update'] } }, }, ], ``` Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:322-367](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L322-L367) ## Execution Routing and Operation Dispatch ### Execution Loop Initialization The `execute` method serves as the primary entry point for node execution within the n8n runtime, implementing the `IExecuteFunctions` interface. It retrieves input execution items via `this.getInputData()` and fetches authentication credentials for `mem0Api`. A local helper closure named `request` is constructed to execute authenticated HTTP methods against the base URL, which defaults to `https://api.mem0.ai` if unconfigured. Every outgoing request injects first-party usage attribution by attaching `source: 'N8N'` to the query parameters object. ```typescript async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); const returnData: INodeExecutionData[] = []; const credentials = await this.getCredentials('mem0Api'); const baseUrl = (credentials.baseUrl as string) || 'https://api.mem0.ai'; const request = async ( method: IHttpRequestMethods, url: string, body?: IDataObject, qs?: IDataObject, ): Promise => { const options: IHttpRequestOptions = { method, url: `${baseUrl}${url}`, json: true, ...(body ? { body } : {}), // First-party usage attribution: the backend reads `source` (same as OpenClaw). qs: { source: 'N8N', ...(qs ?? {}) }, }; return (await this.helpers.httpRequestWithAuthentication.call( this, 'mem0Api', options, )) as IDataObject; }; ``` Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:370-395](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L370-L395) > [!WARNING] > The `request` helper automatically appends `source: 'N8N'` to all query strings; callers passing custom query parameters must ensure key collisions do not unintentionally override usage attribution. Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:376-395](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L376-L395) ### Item Iteration and Operation Matching Control flow iterates over each input item using a `for` loop, wrapping item-level execution in a `try...catch` block to handle runtime errors gracefully. For each item, the current operation identifier is retrieved via `this.getNodeParameter('operation', i)`. When the operation equals `add`, the node extracts structured messages from the UI fixed collection and validates that at least one message is present. If no messages exist, a `NodeOperationError` is thrown with the corresponding item index. ```typescript for (let i = 0; i < items.length; i++) { try { const operation = this.getNodeParameter('operation', i) as string; let responseData: IDataObject | IDataObject[] = {}; if (operation === 'add') { const messagesUi = this.getNodeParameter('messages.message', i, []) as IDataObject[]; if (!messagesUi.length) { throw new NodeOperationError(this.getNode(), 'At least one message is required', { itemIndex: i, }); } ``` Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:397-408](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L397-L408) The request body for memory addition is populated with mapped message roles and content, alongside an inference flag that defaults to true. Optional parameters such as `user_id` are conditionally attached to the payload if provided by the workflow configuration. ```typescript const addFields = this.getNodeParameter('addFields', i, {}) as IDataObject; const body: IDataObject = { messages: messagesUi.map((m) => ({ role: m.role, content: m.content })), infer: addFields.infer !== undefined ? addFields.infer : true, }; const userId = this.getNodeParameter('userId', i, '') as string; if (userId) body.user_id = userId; ``` Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:409-415](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L409-L415) ## Entity Scoping and Filter Construction ### Constructing Filter Criteria The `Mem0` node constructs entity scoping and query filter parameters using workflow node parameters fetched during execution. When configuring search, retrieval, or listing operations, parameters such as user identifiers, agent identifiers, application identifiers, run identifiers, and metadata filters are collected to scope requests against the Mem0 platform backend. ```typescript const userId = this.getNodeParameter('userId', i, '') as string; if (userId) body.user_id = userId; ``` Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:414-415](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L414-L415) > [!NOTE] > Entity identifiers such as `user_id`, `agent_id`, `app_id`, and `run_id` act as primary boundaries for isolating long-term memory retrieval across distinct users and agent execution sessions. Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:414-415](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L414-L415) ## Platform API Client Requests ### HTTP Dispatch and Credentials Communication with the Mem0 platform relies on credentials resolved through n8n's `this.getCredentials('mem0Api')` mechanism. The base URL defaults to `https://api.mem0.ai` if not specified in the credential configuration. The local request helper constructs options containing the HTTP method, the resolved endpoint URL, and JSON parsing rules, while injecting `source: 'N8N'` into query parameters for attribution tracking. ```typescript const credentials = await this.getCredentials('mem0Api'); const baseUrl = (credentials.baseUrl as string) || 'https://api.mem0.ai'; const request = async ( method: IHttpRequestMethods, url: string, body?: IDataObject, qs?: IDataObject, ): Promise => { const options: IHttpRequestOptions = { method, url: `${baseUrl}${url}`, json: true, ...(body ? { body } : {}), // First-party usage attribution: the backend reads `source` (same as OpenClaw). qs: { source: 'N8N', ...(qs ?? {}) }, }; return (await this.helpers.httpRequestWithAuthentication.call( this, 'mem0Api', options, )) as IDataObject; }; ``` Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:373-395](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L373-L395) > [!WARNING] > The helper method executes requests using `this.helpers.httpRequestWithAuthentication.call`, which automatically signs outgoing payloads with the stored API key credentials bound to the `mem0Api` name. Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:390-394](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L390-L394) ### Asynchronous Event Polling Memory addition operations frequently run asynchronously on the platform backend, returning an event identifier and an initial status rather than immediate memory objects. When `waitForCompletion` is enabled, the workflow polls the platform status endpoint to resolve the long-running transaction. ```typescript // Poll settings for asynchronous (infer=true) memory addition. const POLL_INTERVAL_MS = 1500; const MAX_POLL_ATTEMPTS = 40; // ~60s ceiling ``` Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:16-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L16-L19) Polling uses a fixed interval and attempt ceiling to balance responsiveness against execution timeouts: | Constant | Value | Description | | :--- | :--- | :--- | | `POLL_INTERVAL_MS` | `1500` | Delay in milliseconds between consecutive poll status checks. | | `MAX_POLL_ATTEMPTS` | `40` | Maximum number of poll requests before timing out (~60 seconds ceiling). | Sources: [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:17-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L17-L18) ```typescript const pollEvent = async (z: ZObject, eventId: string): Promise => { for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) { const res = await z.request({ url: `/v1/event/${eventId}/`, method: 'GET' }); const data = res.data as EventResponse; const status = data && data.status; if (status === 'SUCCEEDED') { return data; } if (status === 'FAILED') { const reason = (data && (data.error || data.message)) || 'unknown error'; throw new z.errors.Error(`Mem0 memory event ${eventId} failed: ${reason}`, 'Mem0EventFailed', 400); } await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS)); } throw new z.errors.Error( `Timed out waiting for memory event ${eventId}. The add was accepted and is ` + `likely still completing on the server — a timeout here does not mean it failed.`, 'Mem0Timeout', 408, ); }; ``` Sources: [integrations/zapier-mem0/src/creates/add_memory.ts:8-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/add_memory.ts#L8-L28) > [!NOTE] > If polling exhausts `MAX_POLL_ATTEMPTS`, the operation throws a timeout error indicating that the memory addition was accepted and is still processing asynchronously on the server. Sources: [integrations/zapier-mem0/src/creates/add_memory.ts:22-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/add_memory.ts#L22-L27) ## Related - [Zapier Integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/zapier-integration) - [Server API Endpoints](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/server-api-endpoints) --- ## Technical docs: Zapier Integration URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/zapier-integration
Relevant source files The following files were used as context for generating this wiki page: - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/zapier-mem0/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts) - [integrations/zapier-mem0/src/searches/get_memories.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/searches/get_memories.ts) - [integrations/zapier-mem0/src/searches/search_memories.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/searches/search_memories.ts) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/deepseek-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/openclaw/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/backend/platform.ts) - [integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/zapier-mem0/src/authentication.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/authentication.ts) - [server/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/main.py) - [integrations/zapier-mem0/src/creates/add_memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/add_memory.ts) - [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py) - [integrations/openclaw/tools/memory-delete.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-delete.ts) - [integrations/pi-agent-plugin/src/memory/tools.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts) - [integrations/vercel-ai-sdk/src/mem0-utils.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts) - [integrations/zapier-mem0/src/creates/delete_memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/delete_memory.ts) - [integrations/openclaw/tools/memory-search.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/tools/memory-search.ts) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [integrations/pi-agent-plugin/src/commands.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/commands.ts) - [integrations/openclaw/providers.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/providers.ts) - [mem0-ts/src/client/mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/mem0.ts) - [mem0/client/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/client/main.py) - [integrations/zapier-mem0/src/middleware.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/middleware.ts)
## Overview The Zapier Integration package provides native Zapier workflow automation nodes for Mem0, built on the `zapier-platform-core` library. It enables seamless integration between external applications and Mem0's memory backend by exposing core actions and searches such as adding new memories, searching semantically, retrieving lists of user memories, and deleting outdated or incorrect memory entries. Sources: [integrations/zapier-mem0/src/index.ts:1-31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L1-L31) ## Application Architecture and Package Registration ### Core Binding and Package Exports The Zapier integration packages its entire application structure as an exported CommonJS module object bound to `zapier-platform-core`. The integration imports `version as platformVersion` directly from `zapier-platform-core` and pairs it with the application version read dynamically from `package.json`. ```typescript import { version as platformVersion } from 'zapier-platform-core'; import authentication from './authentication'; import { includeApiKey, handleBadResponses } from './middleware'; import addMemory from './creates/add_memory'; import deleteMemory from './creates/delete_memory'; import searchMemories from './searches/search_memories'; import getMemories from './searches/get_memories'; import pkg from '../package.json'; const app = { version: pkg.version, platformVersion, ``` Sources: [integrations/zapier-mem0/src/index.ts:1-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L1-L12) The main `app` configuration object aggregates request interceptors, response handlers, authentication definitions, and operational modules under explicit dictionary keys. Creates and searches are dynamically keyed using the `.key` property exported by each individual action module. ```typescript authentication, beforeRequest: [includeApiKey], afterResponse: [handleBadResponses], creates: { [addMemory.key]: addMemory, [deleteMemory.key]: deleteMemory, }, searches: { [searchMemories.key]: searchMemories, [getMemories.key]: getMemories, }, resources: {}, triggers: {}, }; export = app; ``` Sources: [integrations/zapier-mem0/src/index.ts:14-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L14-L33) > [!NOTE] > The `resources` and `triggers` collections are initialized as empty objects in the core application definition, meaning the integration relies entirely on explicit creates and searches rather than polling triggers or dynamic resource descriptors. Sources: [integrations/zapier-mem0/src/index.ts:29-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L29-L30) ### Application Component Mapping The application configuration wires together authentication modules, request/response middleware functions, and four primary workflow operations split equally between creates and searches. | Component Category | Target Property | Mapped Module / Identifier | Source File Reference | | :--- | :--- | :--- | :--- | | **Platform Version** | `platformVersion` | `zapier-platform-core` version export | [integrations/zapier-mem0/src/index.ts:1-1](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L1-L1) | | **App Version** | `version` | `package.json` version field (`pkg.version`) | [integrations/zapier-mem0/src/index.ts:8-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L8-L11) | | **Authentication** | `authentication` | `./authentication` module | [integrations/zapier-mem0/src/index.ts:2-14](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L2-L14) | | **Request Middleware** | `beforeRequest` | `[includeApiKey]` | [integrations/zapier-mem0/src/index.ts:3-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L3-L16) | | **Response Middleware** | `afterResponse` | `[handleBadResponses]` | [integrations/zapier-mem0/src/index.ts:3-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L3-L17) | | **Create Action** | `creates[addMemory.key]` | `./creates/add_memory` | [integrations/zapier-mem0/src/index.ts:4-20](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L4-L20) | | **Create Action** | `creates[deleteMemory.key]` | `./creates/delete_memory` | [integrations/zapier-mem0/src/index.ts:5-21](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L5-L21) | | **Search Operation** | `searches[searchMemories.key]` | `./searches/search_memories` | [integrations/zapier-mem0/src/index.ts:6-25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L6-L25) | | **Search Operation** | `searches[getMemories.key]` | `./searches/get_memories` | [integrations/zapier-mem0/src/index.ts:7-26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L7-L26) | Sources: [integrations/zapier-mem0/src/index.ts:1-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts#L1-L33) ## API Key Authentication Handshake ### Authentication Configuration and Test The integration implements custom API key authentication where credentials are submitted with the header format `Authorization: Token `, matching the convention used across Mem0's SDKs. Connection verification is performed by the `test` function executing a `GET` request against the `/v1/ping/` endpoint. ```typescript // Custom (API key) authentication for Mem0. // The key is sent as "Authorization: Token " (matches Mem0's SDKs). const test = (z: ZObject, _bundle: Bundle) => z.request({ url: '/v1/ping/', method: 'GET' }); export default { type: 'custom', test, ``` Sources: [integrations/zapier-mem0/src/authentication.ts:3-9](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/authentication.ts#L3-L9) The authentication schema defines two configuration fields: `apiKey` and `baseUrl`. The `apiKey` field uses a `password` type so the Zapier platform masks the secret in the connection UI, and it specifies a default help text directing users to retrieve keys starting with `m0-` from the dashboard. The `baseUrl` field is optional, defaults to `https://api.mem0.ai`, and permits overriding for self-hosted or non-default deployments. ```typescript fields: [ { key: 'apiKey', label: 'Mem0 API Key', // `password` so Zapier masks the key in the connection UI (it is a secret). type: 'password', required: true, helpText: 'Your Mem0 API key (starts with `m0-`). Create one at [app.mem0.ai](https://app.mem0.ai) → Settings → API Keys.', }, { key: 'baseUrl', label: 'Base URL', type: 'string', required: false, default: 'https://api.mem0.ai', helpText: 'Override only for self-hosted or non-default deployments.', }, ], ``` Sources: [integrations/zapier-mem0/src/authentication.ts:10-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/authentication.ts#L10-L28) > [!TIP] > The `connectionLabel` property is explicitly set to `{{user_email}}`, which extracts the account email directly from the `/v1/ping/` test response to let users distinguish between multiple configured Mem0 accounts inside the Zap editor. Sources: [integrations/zapier-mem0/src/authentication.ts:29-33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/authentication.ts#L29-L33) ### Field Definition Reference | Field Key | Label | Type | Required | Default Value | Description / Help Text | | :--- | :--- | :--- | :--- | :--- | :--- | | `apiKey` | Mem0 API Key | `password` | `true` | *None* | Mem0 API key starting with `m0-`, obtainable at `app.mem0.ai` under Settings → API Keys | | `baseUrl` | Base URL | `string` | `false` | `https://api.mem0.ai` | Override URL for self-hosted or non-default deployments | Sources: [integrations/zapier-mem0/src/authentication.ts:10-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/authentication.ts#L10-L28) ## HTTP Middleware and Request Interception ### Request Interception and Bearer Token Injection The `includeApiKey` request middleware operates as a `beforeRequest` interceptor, processing every outbound request object before transmission. When `bundle.authData.apiKey` is present, it initializes or mutates `request.headers` and applies the authorization token using the `Token ` format. If `request.url` begins with a forward slash (`/`), the middleware resolves it against the configured base URL, defaulting to `https://api.mem0.ai` if `bundle.authData.baseUrl` is omitted or empty, and strips any trailing slashes from the base URL string. ```typescript export const includeApiKey = ( request: MutableRequest, _z: ZObject, bundle: Bundle, ): MutableRequest => { if (bundle.authData && bundle.authData.apiKey) { request.headers = request.headers || {}; request.headers.Authorization = `Token ${bundle.authData.apiKey}`; } // Resolve relative URLs against the configured base URL. if (request.url && request.url.startsWith('/')) { const base = (bundle.authData && bundle.authData.baseUrl) || 'https://api.mem0.ai'; request.url = `${base.replace(/\/$/, '')}${request.url}`; } return request; }; ``` Sources: [integrations/zapier-mem0/src/middleware.ts:4-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/middleware.ts#L4-L19) > [!WARNING] > Because Zapier's `z.request` client does not throw exceptions on non-2xx HTTP status codes by default, omitting or bypassing response interception causes server errors (4xx and 5xx responses) to flow downstream as fake successes, turning empty search results or error payloads into incorrectly created memory records. Sources: [integrations/zapier-mem0/src/middleware.ts:21-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/middleware.ts#L21-L23) ### Response Parsing and Error Handling The `handleBadResponses` middleware functions as an `afterResponse` interceptor, evaluating status codes on incoming `ZResponse` objects. It explicitly catches HTTP 401 Unauthorized and 403 Forbidden responses, throwing a Zapier `AuthenticationError` with the message `'The Mem0 API key you supplied is invalid or lacks access.'`. For any response status of 400 or greater, it extracts error details from `response.data` or falls back to `response.content` and `'unknown error'`. ```typescript export const handleBadResponses = ( response: ZResponse, z: ZObject, _bundle: Bundle, ): ZResponse => { if (response.status === 401 || response.status === 403) { throw new z.errors.Error( 'The Mem0 API key you supplied is invalid or lacks access.', 'AuthenticationError', response.status, ); } if (response.status >= 400) { const data = (response.data as Record) || {}; const raw = data.detail || data.error || data.message || response.content || 'unknown error'; // DRF sometimes returns detail as an object/array; stringify so the // thrown message never renders as "[object Object]". const detail = typeof raw === 'string' ? raw : JSON.stringify(raw); throw new z.errors.Error( `Mem0 API request failed (HTTP ${response.status}): ${detail}`, 'Mem0ApiError', response.status, ); } return response; }; ``` Sources: [integrations/zapier-mem0/src/middleware.ts:24-48](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/middleware.ts#L24-L48) > [!NOTE] > Django Rest Framework (DRF) frequently returns error details as structured objects or arrays rather than plain strings. The response parser checks whether the raw detail value is a string, and if not, serializes it via `JSON.stringify` to prevent thrown error messages from rendering as `[object Object]`. Sources: [integrations/zapier-mem0/src/middleware.ts:38-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/middleware.ts#L38-L47) ## Memory Creation Action and Polling ### Execution Flow and Input Processing The `add_memory` create operation collects input fields and submits them to the Mem0 API. Boolean inputs such as `infer` and `waitForCompletion` arrive from Zapier as strings (`'true'` or `'false'`) and undergo explicit string comparison coercion to preserve user selections. The request body constructs a messages array using `bundle.inputData.role` (defaulting to `'user'`) and `bundle.inputData.content`, tagging the source attribution as `'ZAPIER'`. Optional scope identifiers (`user_id`, `agent_id`, `run_id`) and extraction controls (`custom_instructions`, `includes`, `excludes`) are appended when provided. Metadata and custom categories accept either raw objects or JSON-encoded strings, throwing an `InvalidInput` error with a 400 status if parsing fails. ```typescript const perform = async (z: ZObject, bundle: Bundle): Promise => { // Zapier boolean fields can arrive as the strings 'true'/'false'; coerce // explicitly so "Infer = No" / "Wait = No" are honored. const infer = String(bundle.inputData.infer) !== 'false'; // Waiting is opt-in (the poll path can exceed Zapier's step timeout). const wait = String(bundle.inputData.waitForCompletion) === 'true'; const body: Record = { messages: [{ role: bundle.inputData.role || 'user', content: bundle.inputData.content }], infer, // Attribution is recorded server-side by Mem0 from this source tag. source: 'ZAPIER', }; if (bundle.inputData.user_id) body.user_id = bundle.inputData.user_id; if (bundle.inputData.agent_id) body.agent_id = bundle.inputData.agent_id; if (bundle.inputData.run_id) body.run_id = bundle.inputData.run_id; if (bundle.inputData.metadata) { try { body.metadata = typeof bundle.inputData.metadata === 'string' ? JSON.parse(bundle.inputData.metadata) : bundle.inputData.metadata; } catch { throw new z.errors.Error('Metadata must be valid JSON.', 'InvalidInput', 400); } } // Custom extraction controls (optional): steer what the API extracts. if (bundle.inputData.custom_instructions) { body.custom_instructions = bundle.inputData.custom_instructions; } if (bundle.inputData.custom_categories) { try { body.custom_categories = typeof bundle.inputData.custom_categories === 'string' ? JSON.parse(bundle.inputData.custom_categories) : bundle.inputData.custom_categories; } catch { throw new z.errors.Error('Custom Categories must be valid JSON.', 'InvalidInput', 400); } } if (bundle.inputData.includes) body.includes = bundle.inputData.includes; if (bundle.inputData.excludes) body.excludes = bundle.inputData.excludes; ``` Sources: [integrations/zapier-mem0/src/creates/add_memory.ts:30-72](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/add_memory.ts:30-72) > [!WARNING] > Zapier requires at least one entity ID scope. While `user_id` is marked required in the input field definitions, omitting entity scopes or supplying malformed JSON string inputs for `metadata` or `custom_categories` triggers an immediate validation exception before network dispatch. Sources: [integrations/zapier-mem0/src/creates/add_memory.ts:52-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/add_memory.ts:52-54), [integrations/zapier-mem0/src/creates/add_memory.ts:117-117](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/add_memory.ts:117-117) ### Asynchronous Event Polling When `waitForCompletion` is enabled (`true`), the execution handler checks whether the initial `POST /v3/memories/add/` response returned an `event_id` with an unresolved status (`PENDING` or `RUNNING`). If so, it invokes `pollEvent`, which queries `GET /v1/event/{eventId}/` across a bounded retry budget. ```typescript const pollEvent = async (z: ZObject, eventId: string): Promise => { for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) { const res = await z.request({ url: `/v1/event/${eventId}/`, method: 'GET' }); const data = res.data as EventResponse; const status = data && data.status; if (status === 'SUCCEEDED') { return data; } if (status === 'FAILED') { const reason = (data && (data.error || data.message)) || 'unknown error'; throw new z.errors.Error(`Mem0 memory event ${eventId} failed: ${reason}`, 'Mem0EventFailed', 400); } await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS)); } throw new z.errors.Error( `Timed out waiting for memory event ${eventId}. The add was accepted and is ` + `likely still completing on the server — a timeout here does not mean it failed.`, 'Mem0Timeout', 408, ); }; ``` Sources: [integrations/zapier-mem0/src/creates/add_memory.ts:8-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/add_memory.ts:8-28) | Constant | Value | Description / Purpose | | :--- | :--- | :--- | | `POLL_INTERVAL_MS` | `1500` | Delay in milliseconds between consecutive status check requests during event polling | | `MAX_POLL_ATTEMPTS` | `40` | Maximum number of poll attempts, bounding the total poll budget to 60 seconds under Zapier's per-step execution timeout | Sources: [integrations/zapier-mem0/src/creates/add_memory.ts:3-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/add_memory.ts:3-5) > [!NOTE] > If polling exhausts all 40 attempts without reaching a terminal status, the function throws a `Mem0Timeout` error (HTTP 408). This indicates that memory addition was accepted and is still processing server-side; a timeout here does not mean the operation failed. Sources: [integrations/zapier-mem0/src/creates/add_memory.ts:22-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/add_memory.ts:22-27) ## Memory Deletion Action Execution ### Operation Handling and Identifier Validation The `delete_memory` create definition exposes a Zapier integration action that targets individual memory records for removal. Its operation object exports a single `perform` function which accepts the core Zapier context object `z` and the `bundle` payload. Input validation is enforced at the field definition layer, where the `memory_id` input field is explicitly designated as a required string. ```typescript export default { key: 'delete_memory', noun: 'Memory', display: { label: 'Delete Memory', description: 'Deletes a memory by its ID.', }, operation: { perform, inputFields: [{ key: 'memory_id', label: 'Memory ID', type: 'string', required: true }], sample: { message: 'Memory deleted successfully' }, }, }; ``` Sources: [integrations/zapier-mem0/src/creates/delete_memory.ts:12-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/delete_memory.ts:12-24) > [!CAUTION] > Deleting a memory is a destructive and irreversible operation. Once the delete request reaches the upstream backend, the memory record and its embedded vector states are permanently removed from the user store. Sources: [integrations/zapier-mem0/src/creates/delete_memory.ts:12-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/delete_memory.ts:12-18) ### Request Execution and Status Reporting The `perform` handler constructs an HTTP `DELETE` request targeting the Mem0 endpoint. To prevent URL malformation, the memory identifier extracted from `bundle.inputData.memory_id` is passed through `String()` casting and `encodeURIComponent()` encoding before interpolation into the path string `/v1/memories/${encodeURIComponent(String(bundle.inputData.memory_id))}/`. ```typescript const perform = async (z: ZObject, bundle: Bundle) => { // Trailing slash required (Django APPEND_SLASH); id encoded so a stray slash can't mistarget the path. const response = await z.request({ url: `/v1/memories/${encodeURIComponent(String(bundle.inputData.memory_id))}/`, method: 'DELETE', }); return response.data || { message: 'Deleted', memory_id: bundle.inputData.memory_id }; }; ``` Sources: [integrations/zapier-mem0/src/creates/delete_memory.ts:3-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/delete_memory.ts:3-10) > [!NOTE] > A trailing slash is explicitly required on the request URL to accommodate Django's `APPEND_SLASH` routing configuration. Furthermore, encoding the identifier guarantees that stray slash characters within a malformed ID cannot mistarget the URL path hierarchy. Sources: [integrations/zapier-mem0/src/creates/delete_memory.ts:4-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/delete_memory.ts:4-8) Upon receiving a successful response from the server, the handler returns `response.data` if present. If the API response body is empty or lacks data, it falls back to a default status object containing a success message and the target identifier: `{ message: 'Deleted', memory_id: bundle.inputData.memory_id }`. ```typescript return response.data || { message: 'Deleted', memory_id: bundle.inputData.memory_id }; ``` Sources: [integrations/zapier-mem0/src/creates/delete_memory.ts:9-9](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/creates/delete_memory.ts:9-9) ## Search and Retrieval Operations ### Semantic Query Searching The search functionality in the Zapier integration exposes a dedicated search module that queries stored memories using natural language semantics. The `perform` function constructs a POST request body containing the search `query`, an explicit `output_format` set to `'v1.1'`, and a `top_k` parameter calculated by clamping the user-supplied `limit` input to a minimum of 1 with a default ceiling of 50. If a `user_id` is provided in `bundle.inputData`, it is nested within a `filters` object. ```typescript const perform = async (z: ZObject, bundle: Bundle): Promise => { const body: Record = { query: bundle.inputData.query, output_format: 'v1.1', top_k: Math.max(1, Math.floor(Number(bundle.inputData.limit) || 50)), }; if (bundle.inputData.user_id) body.filters = { user_id: bundle.inputData.user_id }; ``` Sources: [integrations/zapier-mem0/src/searches/search_memories.ts:3-10](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/searches/search_memories.ts:3-10) The search request is dispatched against the endpoint `/v3/memories/search/`. To ensure robust output handling across varying payload structures, the response data is guarded against null or empty bodies by checking if it is an array or an object containing a `results` property. ```typescript const response = await z.request({ url: '/v3/memories/search/', method: 'POST', body, }); // Searches must return an array; guard against a null/empty body. const data = response.data as Memory[] | { results?: Memory[] } | null; return Array.isArray(data) ? data : data?.results ?? []; }; ``` Sources: [integrations/zapier-mem0/src/searches/search_memories.ts:11-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/searches/search_memories.ts:11-19) > [!NOTE] > The search module defines its schema with three input fields: `query` (string, required), `user_id` (string, required), and `limit` (integer, defaulting to 50). All searches enforce user-level scoping via filters when the identifier is present. Sources: [integrations/zapier-mem0/src/searches/search_memories.ts:30-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/searches/search_memories.ts:30-34) ### Filter Extraction and Memory Listing Memory listing operations are managed through the `get_memories` search definition, which retrieves all stored memories for a specific user. The execution handler dynamically builds an optional filter object containing the `user_id` when supplied in the bundle input data. ```typescript const perform = async (z: ZObject, bundle: Bundle): Promise => { const body: Record = {}; if (bundle.inputData.user_id) body.filters = { user_id: bundle.inputData.user_id }; ``` Sources: [integrations/zapier-mem0/src/searches/get_memories.ts:3-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/searches/get_memories.ts:3-6) Pagination parameters are passed via URL query parameters, utilizing `Math.max(1, Math.floor(...))` to sanitize both `page` and `limit` values. The `limit` field maps to `page_size` with a default of 50, whereas `page` defaults to 1. The request is sent as a POST payload to `/v3/memories/`. ```typescript const response = await z.request({ url: '/v3/memories/', method: 'POST', params: { page: Math.max(1, Math.floor(Number(bundle.inputData.page) || 1)), page_size: Math.max(1, Math.floor(Number(bundle.inputData.limit) || 50)), }, body, }); ``` Sources: [integrations/zapier-mem0/src/searches/get_memories.ts:7-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/searches/get_memories.ts:7-15) > [!TIP] > Both search and listing modules incorporate identical result-normalization guards. If the upstream server returns a response object wrapped in a results property rather than a raw array, the parser automatically unwraps it or falls back to an empty array to prevent downstream execution halts. Sources: [integrations/zapier-mem0/src/searches/get_memories.ts:16-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/searches/get_memories.ts:16-18) ## Related - [n8n Workflow Nodes](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/n8n-workflow-nodes) - [Server API Endpoints](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/server-api-endpoints) --- ## Technical docs: Documentation Agent Skills URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/ecosystem-integrations/documentation-agent-skills
Relevant source files The following files were used as context for generating this wiki page: - [skills/mem0/scripts/mem0_doc_search.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/openclaw/skill-loader.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/skill-loader.ts) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/deepseek-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/pi-agent-plugin/src/commands.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/commands.ts) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/agent-plugin-core/python/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py) - [cli/python/src/mem0_cli/commands/agent_rush_cmd.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/commands/agent_rush_cmd.py) - [integrations/mem0-agent-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py) - [integrations/codex-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/mcp_server.py) - [integrations/kimi-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/mcp_server.py) - [integrations/claude-code-plugin/adapters/claude/transcript.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/transcript.py) - [integrations/antigravity-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py) - [integrations/cursor-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/mcp_server.py) - [integrations/pi-agent-plugin/src/memory/tools.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts) - [cli/node/src/commands/agent-rush.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/commands/agent-rush.ts) - [integrations/openclaw/recall.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/recall.ts) - [integrations/claude-code-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py) - [integrations/agent-plugin-core/typescript/src/prompts.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/prompts.ts) - [scripts/check-llms-txt-coverage.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/check-llms-txt-coverage.py)
## Overview Documentation Agent Skills provide specialized capabilities and tooling that empower coding assistants to retrieve, verify, and maintain technical documentation and contextual knowledge. By establishing lightweight, on-demand search engines and verification scripts, the architecture prevents context bloat in developer environments while keeping reference materials accessible. Sources: [skills/mem0/scripts/mem0_doc_search.py:3-21](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L3-L21), [scripts/check-llms-txt-coverage.py:2-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/check-llms-txt-coverage.py#L2-L22) These skills bridge documentation distribution formats—such as `llms.txt` indices and Markdown source trees—with interactive agent runtimes and local Model Context Protocol servers. Through dynamic skill resolution, automated coverage audits, and structured prompt rendering, agents can query specific reference pages, audit missing documentation links, and expose semantic memory search tools uniformly across various IDE plugins. Sources: [skills/mem0/scripts/mem0_doc_search.py:6-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L6-L7), [integrations/openclaw/skill-loader.ts:323-327](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/skill-loader.ts#L323-L327), [integrations/agent-plugin-core/python/mcp_server.py:2-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L2-L3), [scripts/check-llms-txt-coverage.py:2-7](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/check-llms-txt-coverage.py#L2-L7) ## Documentation search tool architecture ### Engine configuration and security boundaries The documentation search utility defined in `mem0_doc_search.py` operates as an on-demand retrieval engine that queries `https://docs.mem0.ai/llms.txt` and fetches remote Markdown documentation pages. To prevent unauthorized redirection or arbitrary payload ingestion, hardcoded security constants govern all outgoing HTTP connections. The host domain `docs.mem0.ai` is enforced through URL splitting checks, and custom HTTP redirect handlers intercept intermediate location headers to verify that subsequent requests remain bound to the trusted host. ```python DOCS_BASE = "https://docs.mem0.ai" DOCS_HOST = urllib.parse.urlsplit(DOCS_BASE).netloc LLMS_INDEX = f"{DOCS_BASE}/llms.txt" ENTRY_URL = re.compile(r"\((https?://[^)\s]+)\)") MAX_RESULTS = 20 MAX_PAGE_CHARS = 10000 MAX_INDEX_BYTES = 2_000_000 SECTION_PREFIXES = { "platform": ("/platform/",), "api": ("/api-reference/",), "open-source": ("/open-source/", "/components/"), "integrations": ("/integrations",), } def require_docs_url(url: str) -> None: """Exit unless the URL is an https URL on the Mem0 docs host.""" parts = urllib.parse.urlsplit(url) if parts.scheme != "https" or parts.netloc != DOCS_HOST: sys.exit(f"Refusing to fetch {url}: only {DOCS_BASE} is allowed") class DocsRedirectHandler(urllib.request.HTTPRedirectHandler): """Follow redirects only when they stay on the Mem0 docs host.""" def redirect_request(self, req, fp, code, msg, headers, newurl): require_docs_url(newurl) return super().redirect_request(req, fp, code, msg, headers, newurl) ``` Sources: [skills/mem0/scripts/mem0_doc_search.py:33-62](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L33-L62) The engine enforces strict resource boundaries during HTTP retrieval operations. Byte-size caps and truncation limits prevent denial-of-service conditions or excessive memory consumption when reading remote index files or documentation assets over the network. | Constant Name | Value | Purpose | | :--- | :--- | :--- | | `DOCS_BASE` | `https://docs.mem0.ai` | Canonical documentation root URL | | `MAX_RESULTS` | `20` | Maximum matching search URLs returned in query payloads | | `MAX_PAGE_CHARS` | `10000` | Character truncation threshold for individual documentation pages | | `MAX_INDEX_BYTES` | `2_000_000` | Maximum byte size allowed for the `llms.txt` index file | ```python DOCS_BASE = "https://docs.mem0.ai" DOCS_HOST = urllib.parse.urlsplit(DOCS_BASE).netloc LLMS_INDEX = f"{DOCS_BASE}/llms.txt" ENTRY_URL = re.compile(r"\((https?://[^)\s]+)\)") MAX_RESULTS = 20 MAX_PAGE_CHARS = 10000 MAX_INDEX_BYTES = 2_000_000 ``` Sources: [skills/mem0/scripts/mem0_doc_search.py:33-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L33-L39) > [!CAUTION] > Bypassing `require_docs_url` or removing the `DocsRedirectHandler` check allows open redirect vectors to fetch arbitrary external content into the agent context stream. Every redirect is validated against `DOCS_HOST` before the request is dispatched. ```python def require_docs_url(url: str) -> None: """Exit unless the URL is an https URL on the Mem0 docs host.""" parts = urllib.parse.urlsplit(url) if parts.scheme != "https" or parts.netloc != DOCS_HOST: sys.exit(f"Refusing to fetch {url}: only {DOCS_BASE} is allowed") class DocsRedirectHandler(urllib.request.HTTPRedirectHandler): """Follow redirects only when they stay on the Mem0 docs host.""" def redirect_request(self, req, fp, code, msg, headers, newurl): require_docs_url(newurl) return super().redirect_request(req, fp, code, msg, headers, newurl) ``` Sources: [skills/mem0/scripts/mem0_doc_search.py:49-61](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L49-L61) ### Section filtering and token scoring mechanics Documentation search queries are processed by tokenizing the input string using regular expression matching and scoring index entries based on frequency and title appearance. When a section filter is supplied, index entries are pre-filtered against predefined URL prefix mappings before score calculation occurs. ```python SECTION_PREFIXES = { "platform": ("/platform/",), "api": ("/api-reference/",), "open-source": ("/open-source/", "/components/"), "integrations": ("/integrations",), } ``` Sources: [skills/mem0/scripts/mem0_doc_search.py:41-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L41-L46) The engine maps user-facing section keys to their corresponding documentation route paths. The supported section mappings and URL prefixes are structured as follows: | Section Key | URL Prefixes | Target Documentation Area | | :--- | :--- | :--- | | `platform` | `("/platform/",)` | Platform features and managed services | | `api` | `("/api-reference/",)` | REST API specifications and endpoints | | `open-source` | `("/open-source/", "/components/")` | Open-source core libraries and modular components | | `integrations` | `("/integrations",)` | Third-party integrations and agent frameworks | ```python SECTION_PREFIXES = { "platform": ("/platform/",), "api": ("/api-reference/",), "open-source": ("/open-source/", "/components/"), "integrations": ("/integrations",), } ``` Sources: [skills/mem0/scripts/mem0_doc_search.py:41-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L41-L46) > [!NOTE] > The search scoring algorithm evaluates both general occurrence in the entry string and specific appearance in the page title segment, doubling points for matches occurring in the title substring before sorting results in descending order. ```python def search_docs(query: str, section: str | None = None) -> dict: """Search the llms.txt index for entries matching the query terms, best matches first.""" terms = re.findall(r"[a-z0-9_]+", query.lower()) entries = index_entries() if section in SECTION_PREFIXES: entries = [e for e in entries if entry_path(e).startswith(SECTION_PREFIXES[section])] scored = [] for entry in entries: haystack = entry.lower() title = haystack.split("]")[0] score = sum((term in haystack) + (term in title) for term in terms) if score: scored.append((score, entry)) scored.sort(key=lambda item: -item[0]) return { "source": "llms_txt_index", "query": query, "matching_urls": [entry for _, entry in scored[:MAX_RESULTS]], "suggestion": "Fetch specific pages with --page for detailed content", } ``` Sources: [skills/mem0/scripts/mem0_doc_search.py:93-115](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L93-L115) ## Documentation index parsing and retrieval ### Index extraction and entry parsing The retrieval engine processes the remote `llms.txt` document index by fetching the raw bytes and parsing line-item entries that conform to Markdown link structures. The `index_entries` function verifies that the fetched payload does not exceed `MAX_INDEX_BYTES` before splitting lines and extracting targets beginning with `- [`. ```python def index_entries() -> list: """Return the page entries listed in the llms.txt index.""" content, truncated = fetch_url(LLMS_INDEX, MAX_INDEX_BYTES) if truncated: sys.exit(f"Error: {LLMS_INDEX} exceeds {MAX_INDEX_BYTES} bytes") return [line.strip()[2:] for line in content.splitlines() if line.strip().startswith("- [")] ``` Sources: [skills/mem0/scripts/mem0_doc_search.py:79-85](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L79-L85) Individual index lines are evaluated using a compiled regular expression pattern `ENTRY_URL` that isolates target URLs enclosed in parentheses. The helper function `entry_path` parses the matched URI to extract its relative URL path for downstream prefix filtering. ```source skills/mem0/scripts/mem0_doc_search.py:36| ENTRY_URL = re.compile(r"\((https?://[^)\s]+)\)") skills/mem0/scripts/mem0_doc_search.py:87-91 ``` Sources: [skills/mem0/scripts/mem0_doc_search.py:36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L36), [skills/mem0/scripts/mem0_doc_search.py:87-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L87-L91) > [!WARNING] > If the `llms.txt` payload exceeds 2 megabytes, `index_entries` immediately terminates execution via `sys.exit` to prevent excessive heap allocation during remote agent execution. ```python content, truncated = fetch_url(LLMS_INDEX, MAX_INDEX_BYTES) if truncated: sys.exit(f"Error: {LLMS_INDEX} exceeds {MAX_INDEX_BYTES} bytes") ``` Sources: [skills/mem0/scripts/mem0_doc_search.py:81-83](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L81-L83) ### Page fetching and markdown resolution When an agent requests a specific documentation page via path parameter, `fetch_page` constructs the target URL by joining `DOCS_BASE` with the requested path, ensuring that missing `.md` file extensions are automatically appended. ```python def fetch_page(page_path: str) -> dict: """Fetch a specific documentation page as markdown.""" parts = urllib.parse.urlsplit(urllib.parse.urljoin(DOCS_BASE, page_path)) path = parts.path.rstrip("/") if not path.endswith(".md"): path = f"{path}.md" url = urllib.parse.urlunsplit((parts.scheme, parts.netloc, path, "", "")) ``` Sources: [skills/mem0/scripts/mem0_doc_search.py:118-124](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L118-L124) Fetched content is bounded by `MAX_PAGE_CHARS` multiplied by four during retrieval to detect downstream truncation, returning a dictionary containing the resolved URL, truncated content string, and a boolean flag indicating whether size limits were exceeded. ```source skills/mem0/scripts/mem0_doc_search.py:38| MAX_PAGE_CHARS = 10000 skills/mem0/scripts/mem0_doc_search.py:125-126 ``` Sources: [skills/mem0/scripts/mem0_doc_search.py:38](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L38), [skills/mem0/scripts/mem0_doc_search.py:125-126](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L125-L126) | Function Name | Return Type | Purpose | | :--- | :--- | :--- | | `get_index()` | `dict` | Returns total page count, full URL list, and available section keys | | `list_section(section)` | `dict` | Returns matching index entries for a validated section or error payload | | `fetch_page(page_path)` | `dict` | Resolves `.md` routes and retrieves markdown content with truncation status | ```python def get_index() -> dict: """Fetch the full documentation index from llms.txt.""" urls = index_entries() return {"total_pages": len(urls), "urls": urls, "sections": list(SECTION_PREFIXES)} def list_section(section: str) -> dict: """List the llms.txt index entries in a documentation section.""" if section not in SECTION_PREFIXES: return {"error": f"Unknown section: {section}", "available": list(SECTION_PREFIXES)} pages = [e for e in index_entries() if entry_path(e).startswith(SECTION_PREFIXES[section])] return {"section": section, "pages": pages} ``` Sources: [skills/mem0/scripts/mem0_doc_search.py:129-141](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/skills/mem0/scripts/mem0_doc_search.py#L129-L141) ## Agent skill definition and loading ### Skill resolution and triage prompt loading The OpenClaw skill loading mechanism dynamically resolves skill files such as `memory-triage`, merges domain-specific overlays, and handles user configuration knobs to construct injectable system prompts. The function `loadSkill` reads the skill file, parses its frontmatter and body, and incorporates configuration parameters like categories, importance thresholds, credential scanning patterns, and custom inclusion or exclusion rules. ```typescript export function loadSkill( skillName: string, config: SkillsConfig = {}, ): LoadedSkill | null { const raw = readSkillFile(skillName); if (!raw) return null; const parsed = parseSkillFile(raw); const parts: string[] = [parsed.body]; // Domain overlays only apply to the skill they target (checked via applies_to) if (config.domain) { const overlay = readDomainOverlay(config.domain, skillName); if (overlay) { parts.push("\n" + overlay); } } // Inject user-configured categories into triage skill prompt if (skillName === "memory-triage" && config.categories) { const mergedCats = resolveCategories(config); parts.push(renderCategoriesBlock(mergedCats)); } // Inject triage knobs (importanceThreshold, credentialPatterns) if (skillName === "memory-triage") { const knobs = renderTriageKnobs(config); if (knobs) parts.push(knobs); } // Append user custom rules to the triage prompt. if (skillName === "memory-triage" && config.customRules) { const rulesBlock: string[] = ["\n## User Custom Rules\n"]; if (config.customRules.include?.length) { rulesBlock.push("Additionally extract:"); for (const rule of config.customRules.include) { rulesBlock.push(`- ${rule}`); } } if (config.customRules.exclude?.length) { rulesBlock.push("\nAdditionally skip:"); for (const rule of config.customRules.exclude) { rulesBlock.push(`- ${rule}`); } } parts.push(rulesBlock.join("\n")); } return { name: skillName, prompt: parts.join("\n"), frontmatter: parsed.frontmatter, }; } ``` Sources: [integrations/openclaw/skill-loader.ts:324-377](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/skill-loader.ts#L324-L377) > [!NOTE] > Domain overlays apply exclusively to the specific skill they target by evaluating the `applies_to` attribute during file parsing, ensuring that domain-specific guidelines do not bleed into unrelated agent skills. ```typescript if (config.domain) { const overlay = readDomainOverlay(config.domain, skillName); if (overlay) { parts.push("\n" + overlay); } } ``` Sources: [integrations/openclaw/skill-loader.ts:335-340](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/skill-loader.ts#L335-L340) ### Config injection and rule rendering User configurations such as active categories and custom extraction rules are formatted into markdown blocks and appended directly to the `memory-triage` skill prompt. When custom rules exceed the character budget defined by `COMPACT_CUSTOM_RULE_CHAR_BUDGET`, the loader condenses the rule text into a compact summary view containing rule counts and truncated previews. ```typescript function renderCompactCustomRules(config: SkillsConfig): string[] { const includeRules = config.customRules?.include ?? []; const excludeRules = config.customRules?.exclude ?? []; if (includeRules.length === 0 && excludeRules.length === 0) { return []; } const formatRuleList = (label: string, rules: string[]) => `${label}: ${rules.map((rule) => `"${rule}"`).join("; ")}`; const lines: string[] = []; if (includeRules.length > 0) { lines.push(formatRuleList("Include", includeRules)); } if (excludeRules.length > 0) { lines.push(formatRuleList("Exclude", excludeRules)); } const combined = lines.join(" "); if (combined.length <= COMPACT_CUSTOM_RULE_CHAR_BUDGET) { return ["## Active Custom Rules", "", ...lines.map((line) => `- ${line}`)]; } const preview = [ ...includeRules.slice(0, COMPACT_CUSTOM_RULE_PREVIEW_LIMIT).map((rule) => `include "${rule}"`), ...excludeRules.slice(0, COMPACT_CUSTOM_RULE_PREVIEW_LIMIT).map((rule) => `exclude "${rule}"`), ]; return [ "## Active Custom Rules", "", `- ${includeRules.length} include rule(s) and ${excludeRules.length} exclude rule(s) are configured.`, `- Prompt kept compact, full rule text omitted because it exceeded ${COMPACT_CUSTOM_RULE_CHAR_BUDGET} characters.`, ...(preview.length > 0 ? [`- Preview: ${preview.join("; ")}`] : []), ]; } ``` Sources: [integrations/openclaw/skill-loader.ts:256-293](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/skill-loader.ts#L256-L293) > [!WARNING] > If custom rule descriptions exceed 280 characters, full rule text is omitted from the prompt injection, replaced by a count summary and a preview limited to two items per rule category to prevent prompt bloat. ```typescript const COMPACT_CUSTOM_RULE_CHAR_BUDGET = 280; const COMPACT_CUSTOM_RULE_PREVIEW_LIMIT = 2; … if (combined.length <= COMPACT_CUSTOM_RULE_CHAR_BUDGET) { return ["## Active Custom Rules", "", ...lines.map((line) => `- ${line}`)]; } const preview = [ ...includeRules.slice(0, COMPACT_CUSTOM_RULE_PREVIEW_LIMIT).map((rule) => `include "${rule}"`), ...excludeRules.slice(0, COMPACT_CUSTOM_RULE_PREVIEW_LIMIT).map((rule) => `exclude "${rule}"`), ]; return [ "## Active Custom Rules", "", `- ${includeRules.length} include rule(s) and ${excludeRules.length} exclude rule(s) are configured.`, `- Prompt kept compact, full rule text omitted because it exceeded ${COMPACT_CUSTOM_RULE_CHAR_BUDGET} characters.`, ...(preview.length > 0 ? [`- Preview: ${preview.join("; ")}`] : []), ]; ``` Sources: [integrations/openclaw/skill-loader.ts:229-230](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/skill-loader.ts#L229-L230), [integrations/openclaw/skill-loader.ts:275-292](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/skill-loader.ts#L275-L292) ### Lifecycle integration and recall strategy During the `before_prompt_build` lifecycle hook in OpenClaw, the plugin evaluates session triggers and injects the loaded triage protocol into `prependSystemContext` for provider-level caching. Depending on the configured recall strategy, the agent executes semantic searches against long-term or session-scoped memory stores and injects retrieved context into `prependContext` on every turn. ```typescript export function loadTriagePrompt(config: SkillsConfig = {}): string { // Try to load the full skill with all config-driven overlays … let systemContext = loadCompactTriagePrompt(cfg.skills ?? {}); if (isSubagent) { systemContext = "You are a subagent — use these memories for context but do not assume you are this user. Do NOT store new memories.\n\n" + systemContext; } // Dynamic recall goes in prependContext (changes every turn). // Strategy controls how much the plugin searches automatically: // "always" — long-term + session search every turn (2 searches) // "smart" — long-term search only, no session search (1 search) [default] // "manual" — no auto-recall; agent controls all search via memory_search (0 searches) let recallContext = ""; const recallEnabled = cfg.skills?.recall?.enabled !== false; const recallStrategy = cfg.skills?.recall?.strategy ?? "smart"; if (recallEnabled && recallStrategy !== "manual") { const recallStart = Date.now(); try { const query = lifecycle.prepareUserText(sanitizeQuery(event.prompt)); // Smart mode: skip session search (saves 1 API call per turn) const sessionIdForRecall = recallStrategy === "always" ? isSubagent ? undefined : sessionId : undefined; // smart: long-term only const recallResult = await skillRecall( provider, query, userId, cfg.skills ?? {}, sessionIdForRecall, ); api.logger.info( `openclaw-mem0: skills-mode recall (strategy=${recallStrategy}) injecting ${recallResult.memories.length} memories (~${recallResult.tokenEstimate} tokens)`, ); _captureEvent("openclaw.hook.recall", { strategy: recallStrategy, memory_count: recallResult.memories.length, latency_ms: Date.now() - recallStart, }); recallContext = recallResult.context; } catch (err) { api.logger.warn( `openclaw-mem0: skills-mode recall failed: ${String(err)}`, ); } } else if (recallEnabled && recallStrategy === "manual") { api.logger.info( "openclaw-mem0: skills-mode recall strategy=manual, agent controls search", ); } return { prependSystemContext: systemContext, // cached by provider prependContext: recallContext, // per-turn dynamic }; ``` Sources: [integrations/openclaw/skill-loader.ts:390-391](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/skill-loader.ts#L390-L391), [integrations/openclaw/index.ts:467-529](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L467-L529) | Recall Strategy | Session Search Behavior | Target Context Property | Search Cost per Turn | | :--- | :--- | :--- | :--- | | `"always"` | Searches both long-term and session stores | `prependContext` | 2 search calls | | `"smart"` | Searches long-term memory store only | `prependContext` | 1 search call | | `"manual"` | Disables automatic recall completely | None | 0 search calls | ```typescript // "always" — long-term + session search every turn (2 searches) // "smart" — long-term search only, no session search (1 search) [default] // "manual" — no auto-recall; agent controls all search via memory_search (0 searches) let recallContext = ""; … const sessionIdForRecall = recallStrategy === "always" ? isSubagent ? undefined : sessionId : undefined; // smart: long-term only ``` Sources: [integrations/openclaw/index.ts:476-479](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L476-L479), [integrations/openclaw/index.ts:489-494](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L489-L494) ## Documentation coverage verification workflows ### Two-way diff audit mechanisms The coverage verification script `scripts/check-llms-txt-coverage.py` executes a two-way diff between documentation source files on disk and indexed URLs in `docs/llms.txt`. It scans `docs/**/*.mdx` to construct a canonical set of repository pages, filters out paths matching ignore prefixes loaded from `scripts/llms-txt-ignore.txt`, and parses indexed URLs matching `https://docs.mem0.ai/` to identify missing entries and stale links. ```python def load_ignore_prefixes() -> list[str]: if not IGNORE_FILE.exists(): return [] prefixes: list[str] = [] for raw in IGNORE_FILE.read_text().splitlines(): line = raw.strip() if line and not line.startswith("#"): prefixes.append(line) return prefixes def canonical_repo_pages(ignore_prefixes: list[str]) -> tuple[set[str], set[str]]: all_pages: set[str] = set() included_pages: set[str] = set() for path in DOCS_DIR.rglob("*.mdx"): rel = path.relative_to(DOCS_DIR).with_suffix("").as_posix() all_pages.add(rel) if not any(rel.startswith(p) for p in ignore_prefixes): included_pages.add(rel) return all_pages, included_pages def indexed_urls(text: str) -> set[str]: urls: set[str] = set() for match in URL_RE.finditer(text): path = match.group(1).rstrip("/") if path: urls.add(path) return urls ``` Sources: [scripts/check-llms-txt-coverage.py:41-69](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/check-llms-txt-coverage.py#L41-L69) | Drift Type | Detection Logic | Default Exit Code | `--write` Mode Behavior | | :--- | :--- | :--- | :--- | | `missing` | `included_pages - linked` | `1` | Appends placeholders under `## Unclassified - needs triage` | | `stale` | `linked - all_pages` | `1` | Reported in output, but never auto-removed | ```python default read-only; exits 1 if any drift is found. --write appends placeholder entries for missing pages under a "## Unclassified - needs triage" H2 at the end of docs/llms.txt. Stale URLs are reported but never removed automatically (human decides whether a page was renamed or genuinely deleted). Exits 1 if anything was written, 0 if the file was already in sync. … missing = sorted(included_pages - linked) stale = sorted(linked - all_pages) ``` Sources: [scripts/check-llms-txt-coverage.py:9-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/check-llms-txt-coverage.py#L9-L15), [scripts/check-llms-txt-coverage.py:111-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/check-llms-txt-coverage.py#L111-L112) > [!CAUTION] > Stale URLs in `docs/llms.txt` are never automatically removed when running with `--write` because human intervention is required to determine whether a page was renamed or genuinely deleted. ```python docs/llms.txt. Stale URLs are reported but never removed automatically (human decides whether a page was renamed or genuinely deleted). Exits 1 if anything was written, … print( "\n(Stale URLs are never auto-removed: check whether the page was " "renamed, and update the link by hand.)" ) ``` Sources: [scripts/check-llms-txt-coverage.py:12-14](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/check-llms-txt-coverage.py#L12-L14), [scripts/check-llms-txt-coverage.py:122-125](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/check-llms-txt-coverage.py#L122-L125) ### Scaffolding and triage block generation When invoked with `--write`, the script formats placeholder entries for every missing documentation page and appends them to `docs/llms.txt`. If the triage header `## Unclassified - needs triage` is already present, new entries are appended directly beneath it; otherwise, a complete preamble instructing maintainers to classify scopes and rewrite descriptions is inserted before the placeholder list. ```python def format_placeholder(page: str) -> str: title = page.rsplit("/", 1)[-1].replace("-", " ").replace("_", " ").title() return ( f"- [{title}]({BASE_URL}{page}) [TODO: Platform|OSS|Both]: " "TODO - rewrite as 'Use when ...' and move into the correct section." ) def append_triage_block(text: str, missing_pages: list[str]) -> str: entries = "\n".join(format_placeholder(p) for p in missing_pages) if TRIAGE_HEADER in text: return text.rstrip() + "\n" + entries + "\n" preamble = ( "> New docs pages were detected by CI. For each entry below: replace " "the scope tag with `[Platform]`, `[OSS]`, or `[Both]`; rewrite the " "description as `Use when ...`; move it into the correct section; " "then delete this H2 once empty.\n" ) return text.rstrip() + "\n\n" + TRIAGE_HEADER + "\n\n" + preamble + "\n" + entries + "\n" … if args.write: if missing: new_text = append_triage_block(text, missing) LLMS_TXT.write_text(new_text) print( f"\nAppended {len(missing)} placeholder entries under " f"'{TRIAGE_HEADER}' in docs/llms.txt." ) ``` Sources: [scripts/check-llms-txt-coverage.py:72-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/check-llms-txt-coverage.py#L72-L91), [scripts/check-llms-txt-coverage.py:131-138](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/check-llms-txt-coverage.py#L131-L138) ```python def format_placeholder(page: str) -> str: title = page.rsplit("/", 1)[-1].replace("-", " ").replace("_", " ").title() return ( f"- [{title}]({BASE_URL}{page}) [TODO: Platform|OSS|Both]: " "TODO - rewrite as 'Use when ...' and move into the correct section." ) ``` Sources: [scripts/check-llms-txt-coverage.py:72-77](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/check-llms-txt-coverage.py#L72-L77) ## Coding assistant MCP server interfaces ### JSON-RPC protocol handling and tool registration Plugin harnesses such as [Claude Code Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin) implement a local Model Context Protocol (MCP) server over standard input and output streams using Python core scripts like `integrations/claude-code-plugin/core/mcp_server.py`. The server runs on protocol version `2024-11-05`, declaring the `search_memories` tool with read-only, idempotent, and open-world hints. Incoming JSON-RPC requests are parsed line by line from `sys.stdin`, routing methods such as `initialize`, `ping`, `tools/list`, and `tools/call` to appropriate handlers while catching deserialization and internal errors. ```python PROTOCOL_VERSION = "2024-11-05" TOOL_NAME = "search_memories" TOOL_DESCRIPTION = ( "Search memories from earlier work in this repository. Use it before " "repeating investigation or when earlier decisions, fixes, commands, or " "results may help." ) … def handle_request(message: Any) -> dict[str, Any] | None: if not isinstance(message, dict): return None request_id = message.get("id") method = message.get("method") if method == "notifications/initialized": return None if method == "initialize": requested = (message.get("params") or {}).get("protocolVersion") return { "jsonrpc": "2.0", "id": request_id, "result": { "protocolVersion": requested or PROTOCOL_VERSION, "capabilities": {"tools": {"listChanged": False}}, "serverInfo": {"name": "mem0", "version": PLUGIN_VERSION}, }, } if method == "ping": return {"jsonrpc": "2.0", "id": request_id, "result": {}} ``` Sources: [integrations/claude-code-plugin/core/mcp_server.py:21-27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L21-L27), [integrations/claude-code-plugin/core/mcp_server.py:152-172](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L152-L172) | JSON-RPC Method | Request Parameters | Response Structure / Action | | :--- | :--- | :--- | | `initialize` | `protocolVersion`, `clientInfo` | Returns protocol version, tool capabilities, and server info (`name: "mem0"`) | | `ping` | None | Returns empty result object `{}` | | `tools/list` | None | Returns `search_memories` schema, description, and annotations | | `tools/call` | `name`, `arguments`, `_meta` | Executes memory search or returns `ToolInputError` / execution failure response | ```python if method == "initialize": requested = (message.get("params") or {}).get("protocolVersion") return { "jsonrpc": "2.0", "id": request_id, "result": { "protocolVersion": requested or PROTOCOL_VERSION, "capabilities": {"tools": {"listChanged": False}}, "serverInfo": {"name": "mem0", "version": PLUGIN_VERSION}, }, } if method == "ping": return {"jsonrpc": "2.0", "id": request_id, "result": {}} if method == "tools/list": return { "jsonrpc": "2.0", "id": request_id, "result": { "tools": [ { "name": TOOL_NAME, "description": TOOL_DESCRIPTION, "inputSchema": TOOL_SCHEMA, "annotations": { "readOnlyHint": True, "idempotentHint": True, "openWorldHint": True, }, } ] }, } … if method == "tools/call": params = message.get("params") or {} if params.get("name") != TOOL_NAME: result = _tool_response("Unknown Mem0 tool.", is_error=True) else: try: result = _tool_response( call_search_memories(params.get("arguments"), _workspace_cwd(params)) ) except ToolInputError as exc: result = _tool_response(str(exc), is_error=True) except Exception: result = _tool_response("Memory search failed.", is_error=True) return {"jsonrpc": "2.0", "id": request_id, "result": result} ``` Sources: [integrations/claude-code-plugin/core/mcp_server.py:160-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L160-L191), [integrations/claude-code-plugin/core/mcp_server.py:192-205](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L192-L205) > [!WARNING] > When `tools/call` receives an unknown tool name or encounters an unexpected exception during execution, it returns an explicit tool response dictionary with `isError: True` rather than raising a protocol-level transport error. ```python params = message.get("params") or {} if params.get("name") != TOOL_NAME: result = _tool_response("Unknown Mem0 tool.", is_error=True) else: try: result = _tool_response( call_search_memories(params.get("arguments"), _workspace_cwd(params)) ) except ToolInputError as exc: result = _tool_response(str(exc), is_error=True) except Exception: result = _tool_response("Memory search failed.", is_error=True) return {"jsonrpc": "2.0", "id": request_id, "result": result} ``` Sources: [integrations/claude-code-plugin/core/mcp_server.py:193-205](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L193-L205) ### Tool schema validation and workspace resolution The tool input schema mandates a `query` string property up to 2,000 characters and accepts optional parameters including `top_k`, `category`, `scope`, and `run_id`. The server validates argument types via `_validate_arguments`, raising `ToolInputError` if unknown keys are passed or if boundary checks fail. Workspace current working directories are extracted dynamically from turn metadata or environment variables such as `CLAUDE_PROJECT_DIR` before invoking `search_memories`. ```python TOOL_SCHEMA = { "type": "object", "properties": { "query": { "type": "string", "minLength": 1, "maxLength": 2000, "description": "A direct question about earlier work in this repository.", }, "top_k": { "type": "integer", "minimum": 1, "maximum": 20, "description": "Maximum memories to return. Uses Mem0's configured default when omitted.", }, "category": { "type": "string", "enum": list(CODING_MEMORY_CATEGORY_NAMES), "description": "Optional memory category. Omit to search every category.", }, "scope": { "type": "string", "enum": list(SEARCH_SCOPES), "description": ( "Which memories to search. 'repo' (default) is the whole repository's " "shared memory plus your own preferences, 'dir' narrows the shared " "part to the current directory, 'mine' is your preferences alone." ), }, "run_id": { "type": "string", "minLength": 1, "description": ( "Optional coding-agent session ID. With any scope, restricts results to memories " "saved in that session. Omit to recall memories across sessions." ), }, }, "required": ["query"], "additionalProperties": False, } … def _validate_arguments( arguments: Any, ) -> tuple[str, int | None, str | None, str | None, str | None]: if not isinstance(arguments, dict): raise ToolInputError("Search arguments must be an object.") unknown = set(arguments) - {"query", "top_k", "category", "scope", "run_id"} if unknown: raise ToolInputError(f"Unknown search argument: {sorted(unknown)[0]}") query = arguments.get("query") if not isinstance(query, str) or not query.strip(): raise ToolInputError("query must be a non-empty string.") query = query.strip() if len(query) > 2000: raise ToolInputError("query must be at most 2,000 characters.") top_k = arguments.get("top_k") if top_k is not None and ( isinstance(top_k, bool) or not isinstance(top_k, int) or not 1 <= top_k <= 20 ): raise ToolInputError("top_k must be an integer from 1 to 20.") category = arguments.get("category") if category is not None and category not in CODING_MEMORY_CATEGORY_NAMES: raise ToolInputError("category must be one of Mem0's supported categories.") scope = arguments.get("scope") if scope is not None and scope not in SEARCH_SCOPES: raise ToolInputError(f"scope must be one of {list(SEARCH_SCOPES)}.") run_id = arguments.get("run_id") if run_id is not None: if not isinstance(run_id, str) or not run_id.strip(): raise ToolInputError("run_id must be a non-empty string.") run_id = run_id.strip() return query, top_k, category, scope, run_id ``` Sources: [integrations/claude-code-plugin/core/mcp_server.py:28-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L28-L68), [integrations/claude-code-plugin/core/mcp_server.py:75-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L75-L112) > [!NOTE] > The search scope parameter validates against allowed values defined by `SEARCH_SCOPES` (`"repo"`, `"dir"`, `"mine"`), while categories must match valid coding memory category names. ```python "scope": { "type": "string", "enum": list(SEARCH_SCOPES), "description": ( "Which memories to search. 'repo' (default) is the whole repository's " "shared memory plus your own preferences, 'dir' narrows the shared " "part to the current directory, 'mine' is your preferences alone." ), }, … category = arguments.get("category") if category is not None and category not in CODING_MEMORY_CATEGORY_NAMES: raise ToolInputError("category must be one of Mem0's supported categories.") scope = arguments.get("scope") if scope is not None and scope not in SEARCH_SCOPES: raise ToolInputError(f"scope must be one of {list(SEARCH_SCOPES)}.") ``` Sources: [integrations/claude-code-plugin/core/mcp_server.py:48-56](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L48-L56), [integrations/claude-code-plugin/core/mcp_server.py:98-104](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py#L98-L104) ## Related - [Plugin Synchronization](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/cli-tools/plugin-synchronization) --- ## Technical docs: Telemetry and Metrics URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/telemetry-ops/telemetry-and-metrics
Relevant source files The following files were used as context for generating this wiki page: - [cli/node/src/telemetry.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/telemetry.ts) - [integrations/mem0-agent-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/telemetry.py) - [mem0-ts/src/client/telemetry.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/telemetry.ts) - [mem0-ts/src/oss/src/memory/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/memory/index.ts) - [integrations/kimi-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/telemetry.py) - [server/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/telemetry.py) - [integrations/antigravity-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/telemetry.py) - [cli/python/src/mem0_cli/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/telemetry.py) - [integrations/agent-plugin-core/python/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/telemetry.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/cursor-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/telemetry.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/codex-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/telemetry.py) - [mem0/memory/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/telemetry.py) - [integrations/claude-code-plugin/core/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/telemetry.py) - [integrations/openclaw/telemetry.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/telemetry.ts) - [mem0-ts/src/client/mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/mem0.ts) - [cli/python/src/mem0_cli/telemetry_sender.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/telemetry_sender.py) - [integrations/mem0-strands/python/src/mem0_strands/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-strands/python/src/mem0_strands/telemetry.py) - [integrations/opencode-plugin/telemetry.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/telemetry.ts) - [integrations/agent-plugin-core/typescript/src/telemetry.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/telemetry.ts) - [mem0-ts/src/client/config.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/config.ts) - [integrations/pi-agent-plugin/src/telemetry.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/telemetry.ts) - [integrations/deepseek-plugin/src/telemetry.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/telemetry.ts) - [mem0-ts/src/oss/src/utils/telemetry.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/telemetry.ts)
## Overview Mem0 implements a comprehensive usage telemetry architecture across its Python core, TypeScript SDK, command-line interfaces, self-hosted server, and IDE plugins to track adoption and feature funnels via PostHog. This system handles event sampling, anonymous per-machine identifiers, authenticated account email resolution, secret redaction, and non-blocking sub-process or batched network dispatches. By default, telemetry is enabled across all runtime surfaces but can be opted out universally through the `MEM0_TELEMETRY=false` environment variable. Sources: [cli/node/src/telemetry.ts:2-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/telemetry.ts#L2-L8), [integrations/mem0-agent-plugin/core/telemetry.py:2-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/telemetry.py#L2-L18), [mem0-ts/src/client/telemetry.ts:10-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/client/telemetry.ts#L10-L15), [server/telemetry.py:1-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/telemetry.py#L1-L11), [mem0/memory/telemetry.py:14-32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/telemetry.py#L14-L32) ## Python Core Anonymous Telemetry ### Client Lifecycle and Configuration The Python core telemetry engine relies on PostHog as its underlying event collector, configured via constants defined in `mem0/memory/telemetry.py`. Telemetry activation is governed by the `MEM0_TELEMETRY` environment variable, which defaults to `"True"` and is coerced into a boolean value. If an invalid non-boolean value is supplied, a `ValueError` is raised immediately upon module import. | Constant / Variable | Type | Default Value | Description | | :--- | :--- | :--- | :--- | | `MEM0_TELEMETRY` | `bool` | `True` | Master toggle controlling whether telemetry events are captured. | | `PROJECT_API_KEY` | `str` | `"phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX"` | PostHog project API token for the mem0 platform. | | `HOST` | `str` | `"https://us.i.posthog.com"` | PostHog ingest endpoint host URL. | | `FEATURE_FLAGS_REQUEST_TIMEOUT_SECONDS` | `float` | `0.5` | Timeout threshold for PostHog feature flag queries. | Sources: [mem0/memory/telemetry.py:14-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/telemetry.py#L14-L23), [mem0/memory/telemetry.py:83-88](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/telemetry.py#L83-L88) The `AnonymousTelemetry` class manages the lifecycle of the PostHog client instance and resolves user identities through `get_or_create_user_id()`. When disabled, its internal attributes are set to `None`. The class exposes `capture_event()`, which enriches outgoing payloads with standard environment metadata including client source (`python`), package version (`mem0.__version__`), system platform, Python runtime version, OS release, and hardware machine architecture. A process-wide thread-safe lazy singleton pattern governed by `_get_oss_telemetry()` and secured via `_oss_telemetry_lock` ensures that open-source memory instances reuse a single client instance, which is safely torn down at process exit through an `atexit` hook. > [!WARNING] > Telemetry capture methods catch all exceptions internally and suppress them using a debug logger; failures in network transmission or event serialization never propagate to caller application logic. Sources: [mem0/memory/telemetry.py:76-121](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/telemetry.py#L76-L121), [mem0/memory/telemetry.py:145-184](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/telemetry.py#L145-L184), [mem0/memory/telemetry.py:192-197](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/telemetry.py#L192-L197) ### Sampling Mechanics and Lifecycle Events Open-source hot-path telemetry events are subject to probabilistic sampling to minimize ingestion volume, while critical lifecycle events bypass sampling entirely and always execute at a 100% rate. The sampling rate defaults to `0.1` (10%) and can be overridden via the `MEM0_TELEMETRY_SAMPLE_RATE` environment variable. The helper function `_parse_sample_rate()` parses incoming raw values, catches parsing exceptions, validates that the float falls within the closed interval `[0.0, 1.0]`, and falls back to the default rate on invalid configurations without raising errors. ``` +-------------------------------------------------+ | Incoming PostHog Message | +-------------------------------------------------+ | v Is event in _LIFECYCLE_EVENTS? / \ YES NO / \ v v Bypass Sampling random.random() >= MEM0_SAMPLE_RATE? (sample_rate = 1.0) / \ YES NO / \ v v Drop Message Keep Message (return None) (sample_rate = rate) ``` Sources: [mem0/memory/telemetry.py:30-48](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/telemetry.py#L30-L48), [mem0/memory/telemetry.py:53-74](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/telemetry.py#L53-L74) The `_sampling_before_send()` hook function acts as PostHog's filtering mechanism before dispatch. It inspects outgoing message dictionaries for their event identifiers against `_LIFECYCLE_EVENTS`. ```python _LIFECYCLE_EVENTS = frozenset( {"mem0.init", "mem0.reset", "mem0._create_procedural_memory", "mem0.notice_displayed", "$identify"} ) ``` Sources: [mem0/memory/telemetry.py:53-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/telemetry.py#L53-L55) For non-lifecycle events, it evaluates a random draw against `MEM0_TELEMETRY_SAMPLE_RATE`, dropping the payload if the condition holds. Surviving events are annotated with a `sample_rate` property (`1.0` for lifecycle events, or the configured sample rate for hot-path events) to allow PostHog dashboards to accurately extrapolate total event volumes. > [!NOTE] > The `$identify` event is explicitly included in `_LIFECYCLE_EVENTS` to ensure that PostHog person-merging operations are never lost to statistical sampling. Sources: [mem0/memory/telemetry.py:50-74](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/telemetry.py#L50-L74) ## TypeScript Telemetry and Notice Dispatch ### UnifiedTelemetry Client and Environment Flags The TypeScript open-source package implements telemetry dispatch through the `UnifiedTelemetry` class, which conforms to the `TelemetryClient` interface. The client initializes with a project API key (`POSTHOG_API_KEY`) and a telemetry host endpoint (`POSTHOG_HOST`), defaulting to PostHog's US ingestion URL (`https://us.i.posthog.com/i/v0/e/`). Telemetry collection can be disabled globally by setting the `MEM0_TELEMETRY` environment variable to `false`. SDK versioning is resolved at build time via bundler defines (`__MEM0_SDK_VERSION__`), falling back to `"dev"` in unbundled environments like Jest test suites. ```typescript // __MEM0_SDK_VERSION__ is inlined by tsup/esbuild's `define` at build time from // package.json. In unbundled environments (ts-jest, jest globalSetup) the // identifier is not defined, so guard with typeof to fall back safely. let version = typeof __MEM0_SDK_VERSION__ !== "undefined" ? __MEM0_SDK_VERSION__ : "dev"; // Safely check for process.env in different environments let MEM0_TELEMETRY = true; try { MEM0_TELEMETRY = process?.env?.MEM0_TELEMETRY?.toLowerCase() === "false" ? false : true; } catch (error) {} const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX"; const POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/"; const NOTICE_EVENT_NAME = "mem0.notice_displayed"; ``` Sources: [mem0-ts/src/oss/src/utils/telemetry.ts:7-22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/telemetry.ts#L7-L22) The `captureEvent` method inspects the `MEM0_TELEMETRY` boolean flag before constructing payloads. Outgoing event properties include the resolved client version, an ISO timestamp, custom properties, the PostHog library identifier (`$lib: "posthog-node"`), and a `$process_person_profile` boolean flag that disables person profile creation for anonymous distinct IDs (`anonymous` or `anonymous-supabase`). Network requests are dispatched via the global `fetch` API using HTTP POST with JSON-serialized payloads, catching and logging any transmission errors to `console.error` without disrupting application execution. > [!WARNING] > Telemetry HTTP transmission errors and environment parsing failures are caught and suppressed inside `captureEvent` and helper initializers; network failures or invalid environment values never throw exceptions up to the calling memory instance. Sources: [mem0-ts/src/oss/src/utils/telemetry.ts:13-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/telemetry.ts#L13-L18), [mem0-ts/src/oss/src/utils/telemetry.ts:55-91](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/telemetry.ts#L55-L91) ### Sampling Mechanics and Event Dispatch Hot-path event dispatch is governed by a probabilistic sampling rate defaulting to `0.1` (10%), configurable via the `MEM0_TELEMETRY_SAMPLE_RATE` environment variable. The sampling rate parser validates that parsed values fall within the numeric range `[0, 1]`. Events included in `ALWAYS_SEND_EVENTS` (`init`, `reset`, and `notice_displayed`) bypass sampling checks entirely and execute at a 100% rate (`sample_rate: 1.0`). | Configuration Constant | Default Value | Purpose | | :--- | :--- | :--- | | `POSTHOG_API_KEY` | `phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX` | PostHog project write key | | `POSTHOG_HOST` | `https://us.i.posthog.com/i/v0/e/` | Ingestion API endpoint | | `DEFAULT_SAMPLE_RATE` | `0.1` | Default sampling ratio for hot-path events | | `NOTICE_EVENT_NAME` | `mem0.notice_displayed` | Event identifier for notice telemetry | | `ALWAYS_SEND_EVENTS` | `Set(['init', 'reset', 'notice_displayed'])` | Events exempt from sampling | Sources: [mem0-ts/src/oss/src/utils/telemetry.ts:19-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/telemetry.ts#L19-L44) The `captureClientEvent` function validates that the provided `instance` object contains a `telemetryId`. If sampling drops the event, execution returns early; otherwise, it assembles `TelemetryEventData` including constructor name, method name, API host, timestamp, client version, client source (`"nodejs"`), and additional properties, appending the `sample_rate` property last so callers cannot override it. ```typescript async function captureClientEvent( eventName: string, instance: TelemetryInstance, additionalData: Record = {}, ) { if (!instance.telemetryId) { console.warn("No telemetry ID found for instance"); return; } // >= so that rate=0 drops everything and rate=1 keeps everything (Math.random() ∈ [0, 1)). const alwaysSend = ALWAYS_SEND_EVENTS.has(eventName); if (!alwaysSend && Math.random() >= MEM0_TELEMETRY_SAMPLE_RATE) { return; } const eventData: TelemetryEventData = { function: `${instance.constructor.name}`, method: eventName, api_host: instance.host, timestamp: new Date().toISOString(), client_version: version, client_source: "nodejs", ...additionalData, // sample_rate set AFTER the spread so callers can never override it sample_rate: alwaysSend ? 1.0 : MEM0_TELEMETRY_SAMPLE_RATE, }; await telemetry.captureEvent( instance.telemetryId, `mem0.${eventName}`, eventData, ); } ``` Sources: [mem0-ts/src/oss/src/utils/telemetry.ts:104-137](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/telemetry.ts#L104-L137) The dedicated `captureNoticeEvent` function handles notice display telemetry independently. It bypasses sample rate evaluation and transmits the `mem0.notice_displayed` event with `sample_rate: 1.0` whenever an instance with a valid `telemetryId` displays a notice. > [!TIP] > The `sample_rate` property is spread into `eventData` *after* `...additionalData` or `...properties` to guarantee that callers cannot accidentally or intentionally override the rate assigned by the SDK sampling logic. Sources: [mem0-ts/src/oss/src/utils/telemetry.ts:128-130](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/telemetry.ts#L128-L130), [mem0-ts/src/oss/src/utils/telemetry.ts:139-161](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/utils/telemetry.ts#L139-L161) ## CLI Telemetry and Subprocess Architecture ### CLI Telemetry Recording and Anonymous ID Resolution Both the Node.js and Python CLI packages implement anonymous usage tracking via PostHog through non-blocking, fire-and-forget mechanisms. Telemetry can be disabled by setting the `MEM0_TELEMETRY` environment variable to `false`, `0`, or `no`. When enabled, `captureEvent` evaluates distinct identifiers using a strict priority order: a cached user email retrieved from `/v1/ping/`, an MD5 hash of the configured API key, or a persistent per-machine anonymous identifier. ```typescript function getDistinctId(): string { try { const config = loadConfig(); if (config.platform.userEmail) { return config.platform.userEmail; } if (config.platform.apiKey) { return createHash("md5").update(config.platform.apiKey).digest("hex"); } } catch { /* ignore */ } try { return getOrCreateAnonymousId(); } catch { return `cli-anon-${randomUUID().replace(/-/g, "")}`; } } ``` Sources: [cli/node/src/telemetry.ts:61-78](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/telemetry.ts#L61-L78), [cli/python/src/mem0_cli/telemetry.py:51-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/telemetry.py#L51-L71) Persistent anonymous IDs are generated as random UUID-based strings prefixed with `cli-anon-` and stored locally in `~/.mem0/config.json` under `telemetry.anonymous_id` (or `telemetry.anonymous_id` in Python). This guarantees that subsequent CLI executions on the same host share a unified PostHog identity rather than falling back to ephemeral strings. When a previously anonymous session resolves to a real user email, the SDK detects the transition away from the `cli-anon-` prefix and schedules a one-shot `$identify` event to link historical anonymous logs to the authenticated profile. | Configuration Field | Source Path / Storage | Fallback Priority | | :--- | :--- | :--- | | `user_email` | `config.platform.userEmail` / `user_email` | Priority 1 (cached from `/v1/ping/`) | | `api_key` | `config.platform.apiKey` / `api_key` | Priority 2 (`MD5` hash digest) | | `anonymous_id` | `config.telemetry.anonymousId` / `anonymous_id` | Priority 3 (persistent machine UUID) | Sources: [cli/node/src/telemetry.ts:39-78](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/telemetry.ts#L39-L78), [cli/python/src/mem0_cli/telemetry.py:31-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/telemetry.py#L31-L71) ### Detached Subprocess Dispatch Architecture To prevent network operations or email resolution delays from blocking CLI command execution, telemetry events are dispatched via detached background subprocesses (`telemetry-sender.cjs` in Node.js and `mem0_cli.telemetry_sender` in Python). The parent CLI process serializes the execution context into JSON and writes it directly to the child process's standard input stream before unreferencing and exiting immediately. ```mermaid flowchart TD A[CLI Command Execution] --> B{Is Telemetry Enabled?} B -- No --> C[Return Early] B -- Yes --> D[Resolve Distinct ID & Detect Alias] D --> E[Spawn Detached Subprocess] E --> F[Write Context JSON to Stdin] F --> G[Parent Process Exits Immediately] E --> H[Subprocess Loads Context from Stdin] H --> I{Needs Email & API Key?} I -- Yes --> J[Call /v1/ping/ & Cache Email] I -- No --> K[Skip Ping] J --> L{Has Anonymous Alias?} K --> L L -- Yes --> M[Send PostHog $identify Event] L -- No --> N[Send PostHog Target Event] M --> N N --> O[Swallow All Errors & Exit Silently] ``` Sources: [cli/node/src/telemetry.ts:88-157](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/telemetry.ts#L88-L157), [cli/python/src/mem0_cli/telemetry.py:73-155](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/telemetry.py#L73-L155), [cli/python/src/mem0_cli/telemetry_sender.py:24-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/telemetry_sender.py#L24-L50) The standalone sender script reads the serialized payload from `sys.stdin` (falling back to `sys.argv[1]` for legacy compatibility), validates whether email resolution is required via `/v1/ping/`, and executes HTTP POST requests against PostHog endpoints using native URL handling (`urllib.request` in Python or `fetch`/`spawn` bindings). > [!WARNING] > All runtime exceptions, network timeouts, and persistence failures occurring inside the detached subprocess or parent spawn routine are silently caught and swallowed to guarantee that telemetry errors never disrupt CLI user experiences. Sources: [cli/node/src/telemetry.ts:154-156](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/telemetry.ts#L154-L156), [cli/python/src/mem0_cli/telemetry.py:154-155](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/telemetry.py#L154-L155), [cli/python/src/mem0_cli/telemetry_sender.py:83-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/python/src/mem0_cli/telemetry_sender.py#L83-L84) ## Server Lifecycle Telemetry and State ### Server Installation State and Client Initialization The self-hosted [FastAPI application](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application) implements server-side anonymous telemetry via `server/telemetry.py`. Telemetry is enabled by default, matching the behavior of the open-source library, but operators can opt out by setting `MEM0_TELEMETRY=false`. Persistence of installation state is handled through a JSON file stored at `/app/history/telemetry.json` (configurable via `MEM0_TELEMETRY_STATE_PATH`). State persistence and client access are managed thread-safely using a global `Lock`. When loading or saving state, file and JSON decoding exceptions are caught and logged to prevent application crashes. The PostHog client is lazily initialized with a project API key (`phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX`), a host endpoint (`https://us.i.posthog.com`), and geoIP disabling enabled. If the `posthog` package is missing from the environment, a warning is logged and client instantiation returns `None`. ```python PROJECT_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX" HOST = "https://us.i.posthog.com" ENABLED = os.environ.get("MEM0_TELEMETRY", "true").lower() not in {"0", "false", "no", "off"} STATE_PATH = Path(os.environ.get("MEM0_TELEMETRY_STATE_PATH", "/app/history/telemetry.json")) _lock = Lock() _client: Any = None _dashboard_nudge_logged = False def _load_state() -> dict[str, Any]: if not STATE_PATH.exists(): return {} try: return json.loads(STATE_PATH.read_text()) except (OSError, json.JSONDecodeError): return {} def _save_state(state: dict[str, Any]) -> None: try: STATE_PATH.parent.mkdir(parents=True, exist_ok=True) STATE_PATH.write_text(json.dumps(state)) except OSError: logging.exception("telemetry: failed to persist state") def _install_id(state: dict[str, Any]) -> str: install_id = state.get("install_id") if not install_id: install_id = str(uuid.uuid4()) state["install_id"] = install_id _save_state(state) return install_id def _get_client(): global _client if _client is not None: return _client try: from posthog import Posthog except ImportError: logging.warning("telemetry: posthog package not installed; disabling") return None _client = Posthog(project_api_key=PROJECT_API_KEY, host=HOST, disable_geoip=True) return _client ``` Sources: [server/telemetry.py:24-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/telemetry.py#L24-L71) > [!NOTE] > The server telemetry module logs an informational status message at startup when telemetry is enabled, explicitly reminding operators that setting `MEM0_TELEMETRY=false` disables event transmission. Sources: [server/telemetry.py:74-77](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/telemetry.py#L74-L77) ### Bounded Once-Per-Install Telemetry Delivery Server lifecycle telemetry enforces a strict ceiling of at most two events per installation by tracking state keys (`admin_registered_sent_at` and `onboarding_sent_at`) inside the persistent telemetry state file. | Event Name | Trigger Condition | Associated State Key | Extra Properties | | :--- | :--- | :--- | :--- | | `admin_registered` | First admin account created via wizard or API | `admin_registered_sent_at` | `email_domain`, `server_version` | | `onboarding_completed` | Setup wizard reaches its final success state | `onboarding_sent_at` | `email_domain`, `server_version`, `use_case` | ```python def _capture_once(email: str, event: str, state_key: str, extra: dict[str, Any] | None = None) -> None: if not ENABLED: return with _lock: state = _load_state() if state.get(state_key): return client = _get_client() if client is None: return try: client.capture( distinct_id=_install_id(state), event=event, properties={ "email_domain": email.rsplit("@", 1)[-1].lower() if "@" in email else "unknown", "server_version": mem0.__version__, **(extra or {}), }, ) state[state_key] = datetime.now(timezone.utc).isoformat() _save_state(state) except Exception: logging.exception("telemetry: failed to send %s event", event) def capture_admin_registered(email: str) -> None: _capture_once(email, "admin_registered", "admin_registered_sent_at") def capture_onboarding_completed(email: str, use_case: str) -> None: _capture_once(email, "onboarding_completed", "onboarding_sent_at", {"use_case": use_case}) ``` Sources: [server/telemetry.py:79-114](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/telemetry.py#L79-L114) > [!CAUTION] > Dashboard installs emit both events (`admin_registered` followed by `onboarding_completed`), whereas API-only installs emit only the `admin_registered` event because they bypass the setup wizard. Sources: [server/telemetry.py:1-5](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/telemetry.py#L1-L5) Events share common properties including the extracted `email_domain` (parsed by splitting the operator's email at the `@` symbol), the current `server_version` from `mem0.__version__`, and a randomly generated install UUID (`install_id`). The `onboarding_completed` event additionally carries the operator's freeform `use_case` string. Separately, the module provides `log_dashboard_nudge_once`, which logs a local console hint directing operators to the web dashboard upon storing their first memory. This nudge fires at most once per process and is intentionally un-gated by `MEM0_TELEMETRY` since it never sends data off-box. ```python def log_dashboard_nudge_once(dashboard_url: str) -> None: """Log a hint pointing the operator to the web dashboard the first time a memory is stored. Fires at most once per process (so a restart may re-log it on the next add — harmless for a discovery nudge). LOCAL console log only — sends nothing off-box, so it is intentionally NOT gated by MEM0_TELEMETRY. """ global _dashboard_nudge_logged if _dashboard_nudge_logged: return _dashboard_nudge_logged = True logging.info( "First memory stored. Open the dashboard at %s to view and manage your memories.", dashboard_url, ) ``` Sources: [server/telemetry.py:116-129](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/server/telemetry.py#L116-L129) ## Python Agent Plugin Spool System ### File-Based Spool Claiming and Mechanics Agent plugins operate under strict time budgets per tool call, requiring telemetry recording to avoid network access entirely. The recording method appends individual JSON lines to a local spool file without blocking execution, while a detached background process drains the spool into batched PostHog requests. To prevent concurrent senders from duplicating transmissions or interleaving flushes, the spool system uses an atomic file-based claiming mechanism governed by strict time limits and attempt counters. ```python POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX" POSTHOG_CAPTURE_URL = "https://us.i.posthog.com/i/v0/e/" POSTHOG_BATCH_URL = "https://us.i.posthog.com/batch/" EVENT_PREFIX = "code" SPOOL_LIMIT_BYTES = 256 * 1024 BATCH_SIZE = 100 SEND_TIMEOUT = 5 CLAIM_STALE_SECONDS = 120 CLAIM_EXPIRY_SECONDS = 7 * 24 * 60 * 60 # A batch is only discarded once it has genuinely been retried this many times. MAX_CLAIM_ATTEMPTS = 3 # Parked claims drained per run, after the live spool. Bounded so a long backlog # cannot turn one flush into an unbounded send loop. MAX_PARKED_PER_RUN = 3 # Added to the wait before a released claim becomes reclaimable, per attempt # already spent. Releasing straight to "reclaimable now" let two senders burn the # whole budget within seconds of one another on a single momentary failure, and # discard a batch a retry a minute later would have delivered. RETRY_COOLDOWN_SECONDS = 60 ``` Sources: [integrations/mem0-agent-plugin/core/telemetry.py:94-113](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/telemetry.py#L94-L113) | Constant Name | Value | Meaning | | :--- | :--- | :--- | | `SPOOL_LIMIT_BYTES` | `262144` (256 KB) | Maximum size threshold for the local telemetry spool file | | `BATCH_SIZE` | `100` | Maximum number of events transmitted in a single PostHog batch | | `SEND_TIMEOUT` | `5` | Network request timeout in seconds for PostHog API calls | | `CLAIM_STALE_SECONDS` | `120` | Duration after which an unreleased claim is considered stale | | `CLAIM_EXPIRY_SECONDS` | `604800` (7 days) | Absolute time limit before a parked claim expires permanently | | `MAX_CLAIM_ATTEMPTS` | `3` | Maximum number of delivery attempts before a batch is discarded | | `MAX_PARKED_PER_RUN` | `3` | Bounded limit on parked claims drained per flush run | | `RETRY_COOLDOWN_SECONDS` | `60` | Cooldown wait time added per prior attempt before a released claim re-enters service | ```python SPOOL_LIMIT_BYTES = 256 * 1024 BATCH_SIZE = 100 SEND_TIMEOUT = 5 CLAIM_STALE_SECONDS = 120 CLAIM_EXPIRY_SECONDS = 7 * 24 * 60 * 60 # A batch is only discarded once it has genuinely been retried this many times. MAX_CLAIM_ATTEMPTS = 3 # Parked claims drained per run, after the live spool. Bounded so a long backlog # cannot turn one flush into an unbounded send loop. MAX_PARKED_PER_RUN = 3 # Added to the wait before a released claim becomes reclaimable, per attempt # already spent. Releasing straight to "reclaimable now" let two senders burn the # whole budget within seconds of one another on a single momentary failure, and # discard a batch a retry a minute later would have delivered. RETRY_COOLDOWN_SECONDS = 60 ``` Sources: [integrations/mem0-agent-plugin/core/telemetry.py:98-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/telemetry.py#L98-L112) > [!CAUTION] > Releasing failed claims directly to immediate availability would allow concurrent senders to exhaust the entire timeout budget within seconds on a momentary network failure; scaling the cooldown by attempts already spent ensures resilient retries. Sources: [integrations/mem0-agent-plugin/core/telemetry.py:108-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/telemetry.py#L108-L112) ### Install State Repair and Identity Rotation Plugin telemetry maintains identity separation by storing the per-install salt in a dedicated file rather than embedding it inside shared identity dictionaries. This prevents race conditions across short-lived concurrent agent windows and ensures that background read-modify-write cycles do not overwrite separate property keys like user email addresses or repository hashes. ```python def _install_salt() -> str: """Random per-install salt, created once and memoized for the process. Deliberately its own file, claimed with O_CREAT|O_EXCL, rather than a key in the identity file. Three reasons, all of which produced wrong data when this lived in the identity dict: - Hooks are short-lived separate processes firing on every tool call, and people run more than one agent window. A read-modify-write would let each process mint its own salt, so one repository would hash several ways in the window before a writer won. - resolve_distinct_id holds a copy of the identity dict across a network call to /v1/ping/, so whichever write landed second erased the other's key — losing either the salt (repo_hash changes mid-stream) or the email (a second $identify, splitting the person). - Touching the identity file from record() would create it, and is_first_run keys off that file, so recording an event would silently suppress the install event. Published atomically, and there is deliberately no derived fallback. Creating the file with O_CREAT|O_EXCL and then writing into it leaves a window where the file exists and is empty, and a concurrent hook that reads it in that window gets nothing. Falling back to a digest of the path would hand that process a salt an attacker can compute, memoized for its whole run, which is the privacy control this function exists to provide silently turning itself off under load. The salt is written to a private temp file first and linked into place, so the name either does not exist or already has the full value. Returns "" when it genuinely cannot persist. Callers omit the hash entirely rather than emit an unsalted one. """ ``` Sources: [integrations/mem0-agent-plugin/core/telemetry.py:134-164](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/telemetry.py#L134-L164) > [!WARNING] > Creating salt files directly with `O_CREAT|O_EXCL` and writing inline exposes a vulnerable window where concurrent hooks reading an empty file receive nothing; salts must be written to a private temporary file first and atomically linked into position. Sources: [integrations/mem0-agent-plugin/core/telemetry.py:153-161](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/telemetry.py#L153-L161) The initialization module supports explicit harness overrides via `init()`, harmonizing source tags across disparate processes. The fallback naming convention enforces standard `_PLUGIN` tags across MCP servers and detached flush senders alike. ```python def init(harness: str = "", source_tag: str = "") -> None: """Override the generated identity. Optional — core/_harness_id.py is the default. The fallback shape matches memory_core.configure_harness's (``_PLUGIN``). It used to be ``MEM0__PLUGIN`` here and ``_plugin`` there, which meant one plugin could emit three different source values depending on which process happened to send the batch. """ global _harness, _source_tag _harness = harness or _DEFAULT_HARNESS _source_tag = source_tag or ( f"{_harness.upper().replace('-', '_')}_PLUGIN" if harness else _DEFAULT_SOURCE_TAG ) ``` Sources: [integrations/mem0-agent-plugin/core/telemetry.py:80-92](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/telemetry.py#L80-L92) ## TypeScript Plugin Privacy and Identity ### TypeScript Agent Plugin Telemetry Client The TypeScript agent plugin architecture centers on a unified telemetry client implemented in `integrations/agent-plugin-core/typescript/src/telemetry.ts` and specialized across runner integrations like OpenClaw, OpenCode, Pi Agent, and DeepSeek. Telemetry configuration accepts a `TelemetryConfig` interface defining distinct identity resolvers, event name translators, delivery handlers, and enablement check functions. ```typescript export interface TelemetryConfig { host: string; source: string; version: string; distinctId: string | (() => string | undefined); delivery?: (batch: Record[]) => void | Promise; flushThreshold?: number; flushIntervalMs?: number; maxQueueSize?: number; commonProperties?: Record; eventName?: (event: string) => string; enabled?: () => boolean; } ``` Sources: [integrations/agent-plugin-core/typescript/src/telemetry.ts:32-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/telemetry.ts#L32-L44) Events are processed through a bounded in-memory queue backed by automatic flushing thresholds, a configurable maximum queue size of 100 entries, and a delivery retry mechanism capped at 5 attempts with exponential backoff. ```typescript export function createTelemetry(config: TelemetryConfig) { let queue: Record[] = []; let timer: ReturnType | undefined; let consecutiveFailures = 0; let retryNotBefore = 0; let exitFlushAttempted = false; let flushing = false; const flushThreshold = config.flushThreshold ?? 10; const maxQueueSize = config.maxQueueSize ?? 100; ``` Sources: [integrations/agent-plugin-core/typescript/src/telemetry.ts:98-107](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/telemetry.ts#L98-L107) | Configuration Option | Default Value / Behavior | Purpose | | :--- | :--- | :--- | | `flushThreshold` | `10` | Event count required to trigger an immediate batch flush | | `maxQueueSize` | `100` | Maximum retention boundary; drops new events on overflow rather than evicting the retry backlog | | `flushIntervalMs` | `5000` | Periodic timer interval in milliseconds for background queue flushing | | `POSTHOG_API_KEY` | `phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX` | Hardcoded PostHog project API key used across plugin batches | | `POSTHOG_BATCH_URL` | `https://us.i.posthog.com/batch/` | Endpoint URL receiving batched telemetry payloads | ```typescript const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX"; const POSTHOG_BATCH_URL = "https://us.i.posthog.com/batch/"; ``` Sources: [integrations/agent-plugin-core/typescript/src/telemetry.ts:5-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/telemetry.ts#L5-L6), [integrations/agent-plugin-core/typescript/src/telemetry.ts:105-106](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/telemetry.ts#L105-L106) > [!WARNING] > Telemetry delivery is retried in memory rather than spooled to disk; a session that starts and ends without network connectivity will lose its queued telemetry, avoiding complex cross-process file lease machinery. Sources: [integrations/agent-plugin-core/typescript/src/telemetry.ts:79-85](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/telemetry.ts#L79-L85) ### Secret Redaction and Key Filtering To protect user confidentiality, property values and object payloads traverse a recursive sanitization pipeline before event construction. The `safeValue` utility function intercepts strings to apply `redactSecrets`, handles arrays recursively, and filters object keys against a comprehensive set of sensitive identifiers. ```typescript function safeValue(value: unknown): unknown { if (typeof value === "string") return redactSecrets(value); if (Array.isArray(value)) return value.map(safeValue); if (value && typeof value === "object") { return Object.fromEntries( Object.entries(value) .filter(([key]) => !PRIVATE_KEYS.has(key.toLowerCase().replace(/[^a-z]/g, ""))) .map(([key, nested]) => [key, safeValue(nested)]), ); } return value; } ``` Sources: [integrations/agent-plugin-core/typescript/src/telemetry.ts:51-62](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/telemetry.ts#L51-L62) The `PRIVATE_KEYS` set enumerates field names that are scrubbed from telemetry property dictionaries, normalized by lowercasing and stripping non-alphabetic characters. | Blocklisted Property Key | Blocklisted Property Key | Blocklisted Property Key | | :--- | :--- | :--- | | `apikey` | `authorization` | `password` | | `query` | `secret` | `prompt` | | `token` | `text` | `memory` | | `message` | `error` | `path` | | `cwd` | `userid` | `agentid` | | `runid` | `repoid` | `repositoryid` | | `projectid` | `appid` | `filters` | ```typescript const PRIVATE_KEYS = new Set([ "apikey", "authorization", "password", "query", "secret", "prompt", "token", "text", "memory", "message", "error", "path", "cwd", "userid", "agentid", "runid", "repoid", "repositoryid", "projectid", "appid", "filters", ]); ``` Sources: [integrations/agent-plugin-core/typescript/src/telemetry.ts:8-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/telemetry.ts#L8-L10) > [!NOTE] > Key filtering normalizes property keys by removing all non-alphabetic characters and converting to lowercase before checking membership in `PRIVATE_KEYS`, ensuring variants like `apiKey` or `repo_id` are caught. Sources: [integrations/agent-plugin-core/typescript/src/telemetry.ts:57-57](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/typescript/src/telemetry.ts#L57-L57) ### Key Hashing and Distinct ID Resolution Different runner integrations implement tailored distinct ID resolution strategies to balance anonymity with account continuity. The OpenClaw plugin supports asynchronous email resolution by pinging `/v1/ping/` with an API key token header, computing SHA-256 key fingerprints, and updating queued events when user identity is confirmed. ```typescript function keyFingerprint(apiKey?: string): string { return apiKey ? createHash("sha256").update(apiKey).digest("hex").slice(0, 16) : ""; } function distinctId(apiKey?: string): string { try { const auth = readPluginAuth(); if (auth.userEmail) { // Only when it belongs to the key in hand. Without this check a cached // email was used forever: switch to a different account and every event // kept reporting under the previous one, with nothing to notice it by. if (auth.keyFingerprint === keyFingerprint(apiKey)) { return createHash("sha256").update(auth.userEmail).digest("hex"); } // Only a REAL key that disagrees means the account changed. Without the // apiKey guard the comparison is `undefined === ""` for any call that // simply omits the key, so a capture with no context wiped a perfectly // good account out of openclaw.json. // // A row with an email and NO fingerprint is the legacy shape, from an // install predating this field. Clearing it here deleted a real account // before anything had replaced it, and if the re-resolve then failed // because the user was offline the email was gone from disk for good. The // Python core refuses the same trade: verify, and keep what you have until // the verification succeeds. resolveEmail below overwrites both fields // when it does, so there is nothing to clear first. if (apiKey && auth.keyFingerprint) clearResolvedAccount(); } } catch { // Fall through to the API key or anonymous identity. } return apiKey ? createHash("sha256").update(apiKey).digest("hex") : anonymousId(); } ``` Sources: [integrations/openclaw/telemetry.ts:43-75](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/telemetry.ts#L43-L75), [integrations/openclaw/telemetry.ts:99-129](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/telemetry.ts#L99-L129) By contrast, the OpenCode plugin hashes API keys to derive 32-character distinct identifiers and salts project identifiers with the API key to prevent enumeration attacks. ```typescript function distinctId(apiKey: string): string { return createHash("sha256").update(apiKey).digest("hex").slice(0, 32); } /** * Salted so the hash is not enumerable. * * An unsalted SHA-256 of a project id is reversible by anyone who can guess the * id, which for a project identifier is a small space. The API key is the salt: * it is already in play here (distinctId is a digest of it), it is high entropy, * and using it needs no per-install file and so no write race to get wrong. The * hash is therefore per account rather than per machine, which also keeps joins * working for one user across machines. It resets when the key rotates, which is * consistent, because distinctId resets with it. * * Both are omitted without a key. An event cannot be built without a distinctId * anyway, so this costs nothing. */ function projectHash(projectId?: string, apiKey?: string): Record { if (!projectId || !apiKey) return {}; return { project_hash: createHash("sha256").update(`${apiKey}:${projectId}`).digest("hex") }; } ``` Sources: [integrations/opencode-plugin/telemetry.ts:41-62](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/telemetry.ts#L41-L62) Pi Agent and DeepSeek plugins initialize anonymous IDs stored in local configuration files under `~/.mem0/` or plugin config directories, issuing `$identify` events when an API key eventually supersedes the anonymous session identifier. ```typescript function anonymousId(): string { if (cachedAnonymousId) return cachedAnonymousId; try { const stored = JSON.parse(fs.readFileSync(TELEMETRY_ID_PATH, "utf-8")).anonymousId; if (typeof stored === "string" && stored) return (cachedAnonymousId = stored); } catch { // First run or unreadable identity file. } const created = `pi-mem0-anon-${randomUUID().replace(/-/g, "")}`; try { fs.mkdirSync(CONFIG_DIR, { recursive: true }); fs.writeFileSync(TELEMETRY_ID_PATH, JSON.stringify({ anonymousId: created }), "utf-8"); } catch { // An unwritable config directory must not break the plugin. } return (cachedAnonymousId = created); } function distinctId(apiKey?: string): string { return apiKey ? createHash("sha256").update(apiKey).digest("hex") : anonymousId(); } function previousAnonymousId(id: string): string | undefined { if (identified || id.startsWith("pi-mem0-anon-")) return undefined; identified = true; try { const stored = JSON.parse(fs.readFileSync(TELEMETRY_ID_PATH, "utf-8")).anonymousId; fs.unlinkSync(TELEMETRY_ID_PATH); cachedAnonymousId = undefined; return typeof stored === "string" && stored ? stored : undefined; } catch { return undefined; } } ``` Sources: [integrations/pi-agent-plugin/src/telemetry.ts:21-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/telemetry.ts#L21-L54), [integrations/deepseek-plugin/src/telemetry.ts:34-65](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/telemetry.ts#L34-L65) > [!TIP] > OpenClaw latches email resolution attempts per key fingerprint rather than globally per process, allowing mid-session API key rotations to correctly trigger re-authentication checks. Sources: [integrations/openclaw/telemetry.ts:99-104](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/telemetry.ts#L99-L104) ## Related - [FastAPI Application](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application) - [Memory Lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle) --- ## Technical docs: Plugin Conformance Testing URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/telemetry-ops/plugin-conformance-testing
Relevant source files The following files were used as context for generating this wiki page: - [integrations/agent-plugin-core/conformance/run.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py) - [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py) - [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py) - [integrations/claude-code-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py) - [integrations/agent-plugin-core/python/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py) - [integrations/antigravity-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/hook_runner.py) - [integrations/codex-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/hook_runner.py) - [integrations/cursor-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py) - [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py) - [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py) - [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py) - [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py) - [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py) - [integrations/agent-plugin-core/conformance/artifacts.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/artifacts.py) - [integrations/claude-code-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_cli.py) - [integrations/mem0-agent-plugin/core/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py) - [integrations/agent-plugin-core/python/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py) - [integrations/deepseek-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [integrations/antigravity-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks/adapter.py) - [integrations/codex-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py) - [integrations/claude-code-plugin/core/_harness_id.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/_harness_id.py) - [integrations/antigravity-plugin/hooks.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks.json) - [integrations/cursor-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py) - [integrations/codex-plugin/core/_harness_id.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/_harness_id.py) - [integrations/mem0-agent-plugin/core/_harness_id.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/_harness_id.py) - [integrations/claude-code-plugin/adapters/claude/hook.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/hook.py) - [integrations/kimi-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py) - [integrations/antigravity-plugin/core/_harness_id.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/_harness_id.py)
## Overview Plugin conformance testing provides a unified verification framework to validate coding-agent memory plugins, ensuring bundle integrity, contract adherence, and cross-platform runtime stability across Python and TypeScript implementations. The suite systematically exercises both the shared core logic and individual plugin wrappers to guarantee that interactions with host environments remain predictable and consistent. Sources: [integrations/agent-plugin-core/conformance/run.py:1-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L1-L30), [integrations/agent-plugin-core/conformance/artifacts.py:1-24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/artifacts.py#L1-L24) The framework coordinates automated test suites, command execution checks, and artifact inspections for hosts such as [Claude Code Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin), Cursor, Codex, Kimi, and Antigravity, alongside portable plugins and platform integrations. It prevents subtle behavioral divergence across the diverse host environments by establishing strict runtime and build constraints that each implementation must satisfy. Sources: [integrations/agent-plugin-core/conformance/run.py:19-29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L19-L29), [integrations/agent-plugin-core/conformance/artifacts.py:15-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/artifacts.py#L15-L23) By centralizing test orchestration, artifact validation, hook normalization contracts, and harness segregation checks into a single runner architecture, the test suite catches regression issues and verifies distribution readiness prior to deployment. This guarantees that built plugins remain self-contained, free of monorepo path leaks, and fully capable of operating within host-specific sandbox limitations. Sources: [integrations/agent-plugin-core/conformance/run.py:38-133](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L38-L133), [integrations/agent-plugin-core/conformance/artifacts.py:29-44](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/artifacts.py#L29-L44) ## Conformance Suite Test Runner Architecture ### Suite Orchestration and Python Hosts The conformance runner uses Python path resolution via `CORE_ROOT` and `REPOSITORY_ROOT` to locate plugin directories, importing `redact` from `memory_core`, `build` from `build.build`, and artifact verifiers from `conformance.artifacts`. Python host integrations cover five coding-agent environments: `claude-code`, `cursor`, `codex`, `kimi`, and `antigravity`. ```python CORE_ROOT = Path(__file__).resolve().parents[1] REPOSITORY_ROOT = CORE_ROOT.parents[1] PYTHON_HOSTS = ("claude-code", "cursor", "codex", "kimi", "antigravity") GROUPS = ( "python-bundles", "python-tests", "typescript-core", "openclaw", "opencode", "pi-agent", "deepseek", ) LIVE_GROUP = "live-platform" sys.path.insert(0, str(CORE_ROOT)) sys.path.insert(0, str(CORE_ROOT / "python")) from memory_core import redact # noqa: E402 from build.build import build # noqa: E402 from conformance.artifacts import TYPESCRIPT_ARTIFACTS, verify_artifact as _typescript_artifact_check # noqa: E402 ``` Sources: [integrations/agent-plugin-core/conformance/run.py:17-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L17-L36) Group execution encompasses seven static validation groups alongside a live platform integration target. | Group Name | Target Environment / Tooling | Associated Commands / Artifacts | Sources | | :--- | :--- | :--- | :--- | | `python-bundles` | Python hosts & portable bundle | Native host builds & `mem0-agent-plugin` portable build | [integrations/agent-plugin-core/conformance/run.py:19-21](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L19-L21), [integrations/agent-plugin-core/conformance/run.py:101-110](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L101-L110) | | `python-tests` | Shared Python test suite | `pytest integrations/agent-plugin-core/tests` (plus host tests, ignoring conformance and integration tests) | [integrations/agent-plugin-core/conformance/run.py:22](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L22), [integrations/agent-plugin-core/conformance/run.py:50-65](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L50-L65) | | `typescript-core` | [TypeScript Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/typescript-agent-core) | `pnpm test`, `pnpm typecheck` | [integrations/agent-plugin-core/conformance/run.py:23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L23), [integrations/agent-plugin-core/conformance/run.py:66](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L66) | | `openclaw` | [OpenClaw integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/openclaw-integration) | `pnpm test`, `pnpm exec tsc --noEmit`, `pnpm build` + required artifacts | [integrations/agent-plugin-core/conformance/run.py:24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L24), [integrations/agent-plugin-core/conformance/run.py:67-71](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L67-L71) | | `opencode` | OpenCode plugin | `bun test`, `bun run type-check`, `bun run build` + required artifacts | [integrations/agent-plugin-core/conformance/run.py:25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L25), [integrations/agent-plugin-core/conformance/run.py:72-76](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L72-L76) | | `pi-agent` | Pi-Agent plugin | `pnpm test`, `pnpm typecheck`, `pnpm build` + required artifacts | [integrations/agent-plugin-core/conformance/run.py:26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L26), [integrations/agent-plugin-core/conformance/run.py:77-81](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L77-L81) | | `deepseek` | DeepSeek plugin | `pnpm test`, `pnpm typecheck`, `pnpm build` + required artifacts | [integrations/agent-plugin-core/conformance/run.py:27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L27), [integrations/agent-plugin-core/conformance/run.py:82-86](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L82-L86) | | `live-platform` | Claude Code integration tests | `pytest integrations/claude-code-plugin/tests/integration -q` | [integrations/agent-plugin-core/conformance/run.py:29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L29), [integrations/agent-plugin-core/conformance/run.py:87-95](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L87-L95) | Sources: [integrations/agent-plugin-core/conformance/run.py:19-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L19-L96) ### Check Planning and Execution The check planning phase dynamically aggregates tasks based on selected groups. For `python-bundles`, it schedules native and portable bundle targets across all defined python hosts. For runtime command groups, it maps each command entry to a numbered check identifier and appends artifact verification checks if the group exists in `TYPESCRIPT_ARTIFACTS`. ```python def _planned_checks(groups: set[str]) -> list[dict[str, Any]]: checks: list[dict[str, Any]] = [] if "python-bundles" in groups: checks.append( { "name": "python-bundles", "group": "python-bundles", "status": "planned", "hosts": list(PYTHON_HOSTS), "kinds": {"native": list(PYTHON_HOSTS), "portable": ["mem0-agent-plugin"]}, } ) for group, commands in _runtime_commands().items(): if group not in groups: continue for index, command in enumerate(commands, start=1): checks.append( { "name": f"{group}-{index}", "group": group, "status": "planned", "command": command, } ) if group in TYPESCRIPT_ARTIFACTS: _, required = TYPESCRIPT_ARTIFACTS[group] checks.append( { "name": f"{group}-artifact", "group": group, "status": "planned", "required": list(required), } ) return checks ``` Sources: [integrations/agent-plugin-core/conformance/run.py:99-133](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L99-L133) Command execution is handled by `_command_check`, which records monotonic duration, merges environment overrides, and redacts sensitive data from both the command arguments and captured standard output and error streams. Output capture truncates the response text to the final 8,000 characters when populated. ```python def _command_check( name: str, group: str, command: list[str], *, cwd: Path, environment_overrides: dict[str, str] | None = None, ) -> dict[str, Any]: started = time.monotonic() environment = dict(os.environ) environment.update(environment_overrides or {}) safe_command = [redact(part) for part in command] try: result = subprocess.run( command, cwd=cwd, env=environment, text=True, capture_output=True, check=False, ) output = redact((result.stdout + result.stderr).strip()) return { "name": name, "group": group, "status": "passed" if result.returncode == 0 else "failed", "duration_seconds": round(time.monotonic() - started, 3), "command": safe_command, "exit_code": result.returncode, **({"output": output[-8_000:]} if output else {}), } except OSError as exc: return { "name": name, "group": group, "status": "failed", "duration_seconds": round(time.monotonic() - started, 3), "command": safe_command, "exit_code": None, "output": redact(str(exc)), } ``` Sources: [integrations/agent-plugin-core/conformance/run.py:136-177](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L136-L177) > [!NOTE] > Command checks execute with `check=False` to capture non-zero exit codes as failed status outcomes without throwing exceptions prematurely during test suite execution. Sources: [integrations/agent-plugin-core/conformance/run.py:149-166](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/run.py#L149-L166) ## Artifact Validation and Build Inspection ### TypeScript Artifact Definitions The conformance framework verifies that built TypeScript packages contain all required distribution files and do not leak internal monorepo dependencies. The `TYPESCRIPT_ARTIFACTS` mapping defines the root paths and mandatory build outputs for each integration package. | Plugin Group | Package Path | Required Artifacts | | :--- | :--- | :--- | | `openclaw` | `integrations/openclaw` | `dist/index.js`, `dist/index.d.ts` | | `opencode` | `integrations/opencode-plugin` | `dist/index.js`, `index.d.ts` | | `pi-agent` | `integrations/pi-agent-plugin` | `dist/index.js`, `dist/index.d.ts`, `dist/entry.js`, `dist/entry.d.ts` | | `deepseek` | `integrations/deepseek-plugin` | `dist/index.js`, `dist/index.d.ts` | ```python TYPESCRIPT_ARTIFACTS = { "openclaw": (INTEGRATIONS_ROOT / "openclaw", ("dist/index.js", "dist/index.d.ts")), "opencode": (INTEGRATIONS_ROOT / "opencode-plugin", ("dist/index.js", "index.d.ts")), "pi-agent": ( INTEGRATIONS_ROOT / "pi-agent-plugin", ("dist/index.js", "dist/index.d.ts", "dist/entry.js", "dist/entry.d.ts"), ), "deepseek": (INTEGRATIONS_ROOT / "deepseek-plugin", ("dist/index.js", "dist/index.d.ts")), } ``` Sources: [integrations/agent-plugin-core/conformance/artifacts.py:15-23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/artifacts.py#L15-L23) ### Verification Logic and Monorepo Leak Detection The `verify_artifact` function checks for the physical presence of every required file within the package directory. It then recursively scans compiled JavaScript and declaration files (`*.js`, `*.mjs`, `*.cjs`, `*.d.ts`) inside the `dist` directory using a regular expression pattern that detects unbundled monorepo imports referencing `agent-plugin-core`. ```python MONOREPO_IMPORT = re.compile( r"(?:from\s+|import\s*\(|require\s*\()\s*['\"][^'\"]*agent-plugin-core" ) def verify_artifact(group: str, package: Path, required: tuple[str, ...]) -> dict[str, Any]: started = time.monotonic() errors = [f"missing package artifact: {name}" for name in required if not (package / name).is_file()] dist = package / "dist" for pattern in ("*.js", "*.mjs", "*.cjs", "*.d.ts"): for artifact in dist.rglob(pattern) if dist.is_dir() else (): if MONOREPO_IMPORT.search(artifact.read_text(encoding="utf-8")): errors.append(f"monorepo source import in package artifact: {artifact.relative_to(package)}") return { "name": f"{group}-artifact", "group": group, "status": "failed" if errors else "passed", "duration_seconds": round(time.monotonic() - started, 3), **({"output": "\n".join(errors)} if errors else {}), } ``` Sources: [integrations/agent-plugin-core/conformance/artifacts.py:24-43](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/artifacts.py#L24-L43) > [!WARNING] > Any matching `agent-plugin-core` import found inside built distribution files causes the artifact verification check to fail, ensuring that distributed packages remain entirely self-contained. Sources: [integrations/agent-plugin-core/conformance/artifacts.py:33-36](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/conformance/artifacts.py#L33-L36) ## Python Hook Runner Contract Conformance ### Hook Input Handling and Session Orchestration The shared Python hook runner implementations across agent variants orchestrate event hooks via standard input parsing and background worker handoffs (see [Python Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core)). The `read_hook_input` function parses JSON payloads from standard input, returning an empty dictionary upon encountering `json.JSONDecodeError` or `OSError`. ```python def read_hook_input() -> dict: try: value = json.load(sys.stdin) return value if isinstance(value, dict) else {} except (json.JSONDecodeError, OSError): return {} ``` Sources: [integrations/claude-code-plugin/core/hook_runner.py:44-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py#L44-L50) | Constant | Value | Purpose | | :--- | :--- | :--- | | `STALE_RUNNING_SECONDS` | `300` | Threshold in seconds before a running handoff file is considered stale and reset. | | `PENDING_EXPIRY_SECONDS` | `604800` (7 days) | Maximum age for pending handoff files before unlinking. | | `PENDING_LAUNCH_LIMIT` | `5` | Maximum number of pending handoffs launched per recovery cycle. | | `DEFAULT_IDLE_FLUSH_SECONDS` | `300` | Default idle duration triggering flush execution. | ```python STALE_RUNNING_SECONDS = 300 PENDING_EXPIRY_SECONDS = 7 * 24 * 60 * 60 PENDING_LAUNCH_LIMIT = 5 DEFAULT_IDLE_FLUSH_SECONDS = 300 ``` Sources: [integrations/claude-code-plugin/core/hook_runner.py:36-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py#L36-L40) > [!NOTE] > Automatic flushing relies on `MEM0_CODE_AUTO_FLUSH` environment variable settings, matching values `"1"`, `"true"`, `"yes"`, or `"on"` case-insensitively. Sources: [integrations/claude-code-plugin/core/hook_runner.py:193-196](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py#L193-L196) ### First Prompt Memory Injection and Handoff Lifecycle When evaluating initial user prompts, `first_prompt_memory_output` retrieves query parameters from environment variables and checks length bounds before querying vector stores. ```python def first_prompt_memory_output(store: EvidenceStore, hook_input: dict) -> dict: """Search once before the agent handles the first prompt in a session.""" repo, session_id, prompt, is_first_prompt = record_user_prompt(store, hook_input) if not is_first_prompt: return {} try: minimum_query_chars = int(os.environ.get("MEM0_CODE_MIN_QUERY_CHARS", "20")) except ValueError: minimum_query_chars = 20 if len(prompt.strip()) < max(minimum_query_chars, 1): return {} result = search_memories( store, repo, session_id, bounded(prompt, 6000), top_k=5, operation="first-prompt-search", timeout=2, ) ``` Sources: [integrations/claude-code-plugin/core/hook_runner.py:62-76](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py#L62-L76) ```mermaid sequenceDiagram participant Hook as hook_runner.py participant Store as EvidenceStore participant Worker as flush_worker.py Hook->>Store: record_user_prompt(hook_input) alt is_first_prompt and query length >= minimum_query_chars Hook->>Store: search_memories(prompt, top_k=5) Store-->>Hook: MemoryResult Hook->>Telemetry: record("context_injected") Hook-->>Agent: hookSpecificOutput (additionalContext) else subsequent prompt or short query Hook-->>Agent: {} end Note over Hook,Worker: Background Handoff Hook->>Worker: hand_off_flush() -> _launch_handoff() ``` Sources: [integrations/claude-code-plugin/core/hook_runner.py:64-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py#L64-L94), [integrations/claude-code-plugin/core/hook_runner.py:170-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py#L170-L191) > [!WARNING] > Handoff files are written atomically using a `.tmp` suffix before replacing the target `.json` file to prevent partial read states during concurrent worker launches. Sources: [integrations/claude-code-plugin/core/hook_runner.py:180-190](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.py#L180-L190) ## Plugin Adapter Hook Normalization Testing ### Native Event Mapping and Payload Translation Plugin adapters translate proprietary agent harness event streams into standardized arguments for the shared Mem0 runtime. Each adapter processes raw JSON inputs from standard input and maps harness-specific keys to common payload structures recognized by `hook_runner` and `memory_core`. | Adapter Plugin | Native Event / Trigger | Mapped Runtime Action | Target Source / File | | :--- | :--- | :--- | :--- | | `cursor-plugin` | `sessionStart` | `session-start` | [integrations/cursor-plugin/hooks/adapter.py:26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L26) | | `cursor-plugin` | `beforeSubmitPrompt` | `user-prompt` | [integrations/cursor-plugin/hooks/adapter.py:27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L27) | | `cursor-plugin` | `postToolUse` | `post-tool` | [integrations/cursor-plugin/hooks/adapter.py:28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L28) | | `cursor-plugin` | `postToolUseFailure` | `post-tool-failure` | [integrations/cursor-plugin/hooks/adapter.py:29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L29) | | `cursor-plugin` | `afterAgentResponse` | `assistant-stop` | [integrations/cursor-plugin/hooks/adapter.py:30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L30) | | `cursor-plugin` | `stop` | `stop` | [integrations/cursor-plugin/hooks/adapter.py:31](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L31) | | `cursor-plugin` | `sessionEnd` | `flush --reason session-end` | [integrations/cursor-plugin/hooks/adapter.py:32](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L32) | | `cursor-plugin` | `preCompact` | `flush --reason pre-compact` | [integrations/cursor-plugin/hooks/adapter.py:33](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L33) | | `kimi-plugin` | `SessionStart` | `session-start` | [integrations/kimi-plugin/hooks/adapter.py:23](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L23) | | `kimi-plugin` | `UserPromptSubmit` | `user-prompt` | [integrations/kimi-plugin/hooks/adapter.py:24](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L24) | | `kimi-plugin` | `PostToolUse` | `post-tool` | [integrations/kimi-plugin/hooks/adapter.py:25](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L25) | | `kimi-plugin` | `PostToolUseFailure` | `post-tool-failure` | [integrations/kimi-plugin/hooks/adapter.py:26](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L26) | | `kimi-plugin` | `Stop` | `stop` | [integrations/kimi-plugin/hooks/adapter.py:27](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L27) | | `kimi-plugin` | `PreCompact` | `flush --reason pre-compact` | [integrations/kimi-plugin/hooks/adapter.py:28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L28) | | `kimi-plugin` | `SessionEnd` | `flush --reason session-end` | [integrations/kimi-plugin/hooks/adapter.py:29](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L29) | Sources: [integrations/cursor-plugin/hooks/adapter.py:25-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L25-L34), [integrations/kimi-plugin/hooks/adapter.py:22-30](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L22-L30) > [!NOTE] > The `antigravity-plugin` adapter configures execution through `hooks.json` mapping `PreInvocation`, `PostToolUse`, and `Stop` commands directly to `adapter.py` entry points with strict timeout limits. Sources: [integrations/antigravity-plugin/hooks.json:3-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks.json#L3-L16) ### Transcript Stream Parsing and Session Reconstruction Adapters for harnesses like Antigravity and Kimi Code inspect local transcript file paths to extract structured conversation turns when direct message fields are missing from hook payloads. The Antigravity adapter reads newline-delimited JSON step objects, filtering for items where `status == "DONE"` and extracting redacted user requests enclosed within `` tags or planner response models. ```python def _read_transcript(path: str) -> list[dict[str, str]]: messages = [] try: with open(path, encoding="utf-8") as transcript: for line in transcript: try: step = json.loads(line) except json.JSONDecodeError: continue content = step.get("content") if not isinstance(content, str) or step.get("status") != "DONE": continue if step.get("type") == "USER_INPUT": match = re.search(r"\s*(.*?)\s*", content, re.DOTALL) messages.append({"role": "user", "content": redact(match.group(1) if match else content).strip()}) elif step.get("source") == "MODEL" and step.get("type") == "PLANNER_RESPONSE": messages.append({"role": "assistant", "content": redact(content).strip()}) except (OSError, TypeError): pass return messages ``` Sources: [integrations/antigravity-plugin/hooks/adapter.py:28-47](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks/adapter.py#L28-L47) Similarly, the Kimi Code adapter resolves session directories through `session_index.jsonl` lookups or globbing under `sessions/*/` to locate wire transcripts (`wire.jsonl`), aggregating text parts across loop events while skipping error or interrupted finish reasons. ```python def _session_dir(session_id: str) -> Path | None: home = Path(os.environ.get("KIMI_CODE_HOME", Path.home() / ".kimi-code")).expanduser() found = None try: with (home / "session_index.jsonl").open(encoding="utf-8") as index: for line in index: try: entry = json.loads(line) except json.JSONDecodeError: continue if entry.get("sessionId") == session_id and isinstance(entry.get("sessionDir"), str): found = Path(entry["sessionDir"]) except OSError: pass if found: return found if not session_id or Path(session_id).name != session_id: return None return next((path for path in (home / "sessions").glob(f"*/{session_id}") if path.is_dir()), None) def _last_assistant_message(transcript: Path) -> str: message = "" step_id = None parts = [] try: lines = transcript.read_text(encoding="utf-8").splitlines() except OSError: return "" for line in lines: try: record = json.loads(line) except json.JSONDecodeError: continue if record.get("type") != "context.append_loop_event" or not isinstance(record.get("event"), dict): continue event = record["event"] if event.get("type") == "step.begin": step_id = event.get("uuid") parts = [] elif event.get("type") == "content.part" and event.get("stepUuid") == step_id: part = event.get("part") if isinstance(part, dict) and part.get("type") == "text" and isinstance(part.get("text"), str): parts.append(part["text"]) elif event.get("type") == "step.end" and event.get("uuid") == step_id: if event.get("finishReason") not in {"error", "interrupted"} and parts: message = "".join(parts) step_id = None parts = [] return message ``` Sources: [integrations/kimi-plugin/hooks/adapter.py:33-82](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L33-L82) > [!WARNING] > Transcript parsing fails silently on `OSError`, `TypeError`, or `json.JSONDecodeError`, returning empty message lists or falling back to default runner implementations to prevent hook crashes. Sources: [integrations/antigravity-plugin/hooks/adapter.py:35-46](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks/adapter.py#L35-L46), [integrations/kimi-plugin/hooks/adapter.py:38-61](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L38-L61) ## Harness Identity and Multi-Agent Environments ### Harness Identity Configuration and Source Tagging The plugin core initializes runtime behavior using harness-specific identity parameters. Through `configure_harness`, plugins define their unique execution context including `name`, `env_prefix`, `data_dir_name`, and `source_tag`. When left unconfigured, defaults fallback to `generic` with standard `MEM0_PLUGIN` environment variables. | Harness Identifier | Platform Source | Platform Application | Source Reference | | :--- | :--- | :--- | :--- | | `claude-code` | `MEM0_PLUGIN` | `claude-code` | [integrations/claude-code-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/_harness_id.py#L3-L11) | | `codex` | `MEM0_PLUGIN` | `codex` | [integrations/codex-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/_harness_id.py#L3-L11) | | `coding-agent` | `MEM0_PLUGIN` | *None* | [integrations/mem0-agent-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/_harness_id.py#L3-L11) | | `antigravity` | `MEM0_PLUGIN` | `antigravity` | [integrations/antigravity-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/_harness_id.py#L3-L11) | Sources: [integrations/claude-code-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/_harness_id.py#L3-L11), [integrations/codex-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/_harness_id.py#L3-L11), [integrations/mem0-agent-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/_harness_id.py#L3-L11), [integrations/antigravity-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/_harness_id.py#L3-L11) > [!NOTE] > The platform-side vocabulary separates the overarching source family (`MEM0_PLUGIN`) from the specific editor application header (`X-Application`). If the application field is empty, `memory_core` omits the application header entirely rather than transmitting a placeholder string. Sources: [integrations/claude-code-plugin/core/_harness_id.py:6-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/_harness_id.py#L6-L11) ### Multi-Agent Environment Scoping and CLI Integration Multi-agent runtimes configure telemetry initialization (see [Telemetry and Metrics](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/telemetry-ops/telemetry-and-metrics)) and CLI parameter parsing dynamically based on command-line arguments. When executing diagnostics or control commands via `memory_cli.py`, operators can supply explicit `--harness` flags to override default source tags and bind the telemetry collector to the target environment. ```python def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--plugin-data-dir", default="") parser.add_argument("--harness", default="") subparsers = parser.add_subparsers(dest="command", required=True) status = subparsers.add_parser("status") status.add_argument("--json", action="store_true") doctor_parser = subparsers.add_parser("doctor") doctor_parser.add_argument("--json", action="store_true") subparsers.add_parser("pause") subparsers.add_parser("resume") forget = subparsers.add_parser("forget") forget.add_argument("--remote", action="store_true") forget.add_argument("--yes", action="store_true") forget.add_argument("--include-project-memory", action="store_true") args = parser.parse_args() if args.harness: source_tag = f"{args.harness.replace('-', '_')}_plugin" configure_harness(args.harness, source_tag=source_tag) telemetry.init(harness=args.harness, source_tag=source_tag.upper()) if args.plugin_data_dir: ``` Sources: [integrations/claude-code-plugin/core/memory_cli.py:61-86](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_cli.py#L61-L86), [integrations/mem0-agent-plugin/core/memory_cli.py:61-86](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L61-L86) > [!WARNING] > Failing to pass matching harness identifiers during concurrent multi-agent executions risks cross-contamination of local evidence stores when multiple plugins share an unsegmented root directory. Always set explicit `MEM0_CODE_DATA_DIR` overrides or plugin data path flags. Sources: [integrations/claude-code-plugin/core/memory_cli.py:86-88](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_cli.py#L86-L88), [integrations/mem0-agent-plugin/core/memory_cli.py:86-88](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_cli.py#L86-L88) ## Related - [Python Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core) - [TypeScript Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/typescript-agent-core) --- ## Technical docs: OSS to Platform Migration URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/telemetry-ops/oss-to-platform-migration
Relevant source files The following files were used as context for generating this wiki page: - [scripts/oss-to-platform-migrate.sh](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh) - [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts) - [integrations/hermes-plugin-mem0/_setup.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_setup.py) - [integrations/openclaw/cli/commands.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/cli/commands.ts) - [integrations/hermes-plugin-mem0/__init__.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/__init__.py) - [integrations/openclaw/providers.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/providers.ts) - [integrations/deepseek-plugin/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/deepseek-plugin/src/index.ts) - [integrations/vercel-ai-sdk/src/mem0-utils.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/vercel-ai-sdk/src/mem0-utils.ts) - [integrations/openclaw/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts) - [mem0-ts/src/community/src/integrations/langchain/mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/community/src/integrations/langchain/mem0.ts) - [mem0-ts/src/oss/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/index.ts) - [mem0-ts/src/oss/src/vector_stores/memory.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/memory.ts) - [mem0-ts/src/oss/src/vector_stores/opensearch.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/opensearch.ts) - [integrations/hermes-plugin-mem0/_backend.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/hermes-plugin-mem0/_backend.py) - [mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts) - [integrations/pi-agent-plugin/src/memory/tools.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/pi-agent-plugin/src/memory/tools.ts) - [mem0-ts/src/oss/src/prompts/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/prompts/index.ts) - [cli/node/src/backend/platform.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/cli/node/src/backend/platform.ts) - [integrations/zapier-mem0/src/index.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/zapier-mem0/src/index.ts)
## Overview The OSS to Platform Migration utility facilitates the transition of locally stored vector collections and memory artifacts from open-source Mem0 deployments to hosted platform backends. By automating collection extraction, schema normalization, and bulk API ingestion, it addresses the data fragmentation and operational overhead of moving between self-hosted vector engines and managed cloud infrastructure. Key design decisions include robust pagination, idempotent import key generation, and preflight validation checks that ensure metadata integrity and entity mapping are preserved across boundaries. Sources: [scripts/oss-to-platform-migrate.sh:711-729](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L711-L729), [scripts/oss-to-platform-migrate.sh:738-750](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L738-L750), [scripts/oss-to-platform-migrate.sh:825-835](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L825-L835) ## Migration script invocation and flags ### CLI Interface and Options The migration script exposes a command-line interface powered by standard argument parsing. It supports options for configuring Qdrant connections, setting export output paths, filtering memories by scope identifiers, and controlling pagination and import-only behaviors. | Flag / Option | Argument Type | Default Value | Description | | :--- | :--- | :--- | :--- | | `--qdrant-url` | string | `http://localhost:6333` | Base URL for the Qdrant instance. | | `--qdrant-api-key` | string | None | API key for authenticating with Qdrant. | | `--qdrant-collection` | string | `mem0` | Name of the Qdrant collection to export from. | | `--qdrant-page-size` | integer | 100 | Page size for scrolling through Qdrant points. Must be greater than 0. | | `--user-id` | string | None | Filter exported memories by specific user ID. | | `--agent-id` | string | None | Filter exported memories by specific agent ID. | | `--run-id` | string | None | Filter exported memories by specific run ID. | | `--all` | flag | False | Export all records without scope filtering. | | `--output` | path | Auto-generated | Target file path for the exported JSON artifact. | | `--import-only` | flag | False | Skip export and run platform ingestion on an existing input file. | | `--input` | path | None | Source JSON file path required when using `--import-only`. | Sources: [scripts/oss-to-platform-migrate.sh:739-746](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L739-L746), [scripts/oss-to-platform-migrate.sh:762-767](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L762-L767), [scripts/oss-to-platform-migrate.sh:796-799](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L796-L799) > [!WARNING] > Setting `--qdrant-page-size` to zero or a negative integer raises a `MigrationError` during initialization, halting the export process before any network requests are dispatched. Sources: [scripts/oss-to-platform-migrate.sh:743-745](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L743-L745) ### Environment Options and Configuration Authentication credentials and connection endpoints can be supplied via runtime environment variables or overridden explicitly through command-line flags. When platform requests return authentication failures (`401` or `403`), the script raises an `AuthError` directing operators to verify their `MEM0_API_KEY` environment variable or re-authenticate via email login. Sources: [scripts/oss-to-platform-migrate.sh:791-792](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L791-L792) > [!NOTE] > The import routine strictly requires the `--input` flag when `--import-only` is invoked; omitting it triggers an immediate validation failure. Sources: [scripts/oss-to-platform-migrate.sh:797-798](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L797-L798) ## Extracting open source vector collections ### Qdrant Collection Export and Normalization Dumping and reading memory payloads and vectors from open source vector store backends [Database Vector Stores](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/database-vector-stores) involves establishing collection connections, scrolling through stored points with pagination constraints, and normalizing payload structures into standardized export artifacts. The migration utility queries vector engines such as Qdrant to extract point records, isolating core fields and promoting identifiers into structured dictionaries. The export process reads points from a designated collection using configurable page sizes and optional scope filters. Each retrieved point is normalized by mapping core properties—such as the memory text, content hash, creation timestamp, and update timestamp—while promoting specific identifier keys and collecting unhandled properties into a separate metadata dictionary. ```bash promoted_payload_keys = ("user_id", "agent_id", "run_id", "actor_id", "role") core_and_promoted_keys = { "data", "hash", "created_at", "updated_at", "id", "text_lemmatized", "attributed_to", *promoted_payload_keys, } record: Dict[str, Any] = { "id": str(point.get("id")), "memory": payload.get("data", ""), "hash": payload.get("hash"), "created_at": payload.get("created_at"), "updated_at": payload.get("updated_at"), "source": "python_oss_qdrant", "source_payload": payload, } for key in promoted_payload_keys: if key in payload: record[key] = payload[key] metadata = {key: value for key, value in payload.items() if key not in core_and_promoted_keys} if metadata: record["metadata"] = metadata return record ``` Sources: [scripts/oss-to-platform-migrate.sh:699-729](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L699-L729) The resulting export artifact bundles metadata about the source SDK, vector store type, storage configuration, collection name, server URL, active filters, local anonymous identifiers, and the complete list of normalized records before writing the payload to disk with restricted file permissions. ```bash artifact = { "version": 1, "kind": "mem0_oss_qdrant_export", "exported_at": utc_now(), "source": { "sdk": "python", "vector_store": "qdrant", "storage": "hosted", "collection": collection_name, "url": base_url, "filters": { "user_id": args.user_id, "agent_id": args.agent_id, "run_id": args.run_id, "all": args.all, }, }, "local_anonymous_id": primary_anon_id(anon_ids), "anonymous_ids": { "oss": anon_ids.get("oss"), "cli": anon_ids.get("cli"), }, "record_count": len(records), "records": records, } write_export_file(output_path, artifact) return output_path, len(records) ``` Sources: [scripts/oss-to-platform-migrate.sh:752-778](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L752-L778) > [!CAUTION] > Export output files are explicitly written with read-write permissions restricted to the file owner (`stat.S_IRUSR | stat.S_IWUSR`) to protect sensitive memory payloads and metadata exposed during the dump. Sources: [scripts/oss-to-platform-migrate.sh:735](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L735), [scripts/oss-to-platform-migrate.sh:958](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L958) ### Vector Store Abstractions and Graph Backends Beyond Qdrant, TypeScript vector store implementations such as the `NeptuneAnalyticsVectorStore` interact with graph databases by structuring collections around specific labels, node identifiers, and vector execution commands. These stores initialize collection labels (`MEM0_VECTOR_${this.collectionName}`), user migration labels (`MEM0_VECTOR_memory_migrations`), and default node references (`mem0-user`) to execute batch insertions and top-K vector similarity searches. ```typescript constructor(config: NeptuneAnalyticsConfig) { this.graphIdentifier = this.resolveGraphIdentifier(config); this.collectionName = config.collectionName || "memories"; this.collectionLabel = `MEM0_VECTOR_${this.collectionName}`; this.collectionLabelExpr = this.escapeLabel(this.collectionLabel); this.userLabel = "MEM0_VECTOR_memory_migrations"; this.userLabelExpr = this.escapeLabel(this.userLabel); this.userNodeId = "mem0-user"; this.dimension = config.dimension || 1536; this.clientConfig = this.buildClientConfig(config); this.clientOverride = config.client; ``` Sources: [mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts:58-69](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts#L58-L69) > [!WARNING] > Neptune Analytics vector indices are non-transactional; combined updates that write payloads before vector embeddings employ best-effort compensation reads to capture prior node states, but concurrent same-vertex writes can interleave and desync stored payloads from their vectors. Sources: [mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts:184-189](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts#L184-L189) ## Platform ingestion and schema alignment ### Payload Parsing and Record Normalization Transforming open source records and streaming them into Mem0 platform API endpoints requires reading export files, validating record structures, and transforming local schema fields into hosted platform payloads. The migration script reads export artifacts containing either a raw list of records or a dictionary wrapper with a `records` array, validating that each entry conforms to an object structure before mapping. ```bash def read_import_records(input_path: Path) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]: try: parsed = json.loads(input_path.read_text(encoding="utf-8")) except FileNotFoundError as exc: raise MigrationError(f"Import file not found: {input_path}") from exc except json.JSONDecodeError as exc: raise MigrationError(f"Import file is not valid JSON: {input_path}") from exc if isinstance(parsed, list): records = parsed artifact: Dict[str, Any] = {} elif isinstance(parsed, dict): artifact = parsed raw_records = parsed.get("records") if not isinstance(raw_records, list): raise MigrationError("Import artifact must contain a records array.") records = raw_records else: raise MigrationError("Import file must be a JSON object or array.") return artifact, [record for record in records if isinstance(record, dict)] ``` Sources: [scripts/oss-to-platform-migrate.sh:802-823](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L802-L823) Each record undergoes schema alignment by extracting memory text, constructing platform-compatible messages, and injecting migration metadata. A deterministic migration import key is computed using a SHA-256 hash derived from the source SDK, vector store, collection name, and local record identifier. ```bash def migration_import_key(source: Dict[str, Any], record: Dict[str, Any]) -> str: raw = ":".join( [ str(source.get("sdk") or "unknown"), str(source.get("vector_store") or "unknown"), str(source.get("collection") or "unknown"), str(record.get("id") or ""), ] ) return sha256(raw.encode("utf-8")).hexdigest() ``` Sources: [scripts/oss-to-platform-migrate.sh:825-835](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L825-L835) > [!NOTE] > The import payload builder enforces that every record includes non-empty memory text and at least one scoping identifier (`user_id`, `agent_id`, or `run_id`), raising a `MigrationError` if these prerequisites are unmet. Sources: [scripts/oss-to-platform-migrate.sh:861-884](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L861-L884) ### Migration Metadata Mapping The metadata structure built for imported records embeds traceability fields into the hosted platform items. These properties link platform memories directly back to their open source origin collections, local IDs, and content hashes. | Metadata Key | Source / Logic | Description | | :--- | :--- | :--- | | `mem0_migration_source` | `record.get("source") or "python_oss_qdrant"` | Identifies the originating open source client driver or store. | | `mem0_migration_collection` | `source.get("collection")` | Captures the source vector collection name. | | `mem0_migration_local_id` | `record.get("id")` | Preserves the original vector point or record identifier. | | `mem0_migration_local_hash` | `record.get("hash")` | Stores the content hash from the local database. | | `mem0_migration_import_key` | `migration_import_key(source, record)` | Holds the SHA-256 deduplication hash for idempotency checking. | ```bash def build_import_metadata(source: Dict[str, Any], record: Dict[str, Any]) -> Dict[str, Any]: existing_metadata = record.get("metadata") if isinstance(record.get("metadata"), dict) else {} metadata = dict(existing_metadata) metadata.update( { "mem0_migration_source": record.get("source") or "python_oss_qdrant", "mem0_migration_collection": source.get("collection"), "mem0_migration_local_id": record.get("id"), "mem0_migration_local_hash": record.get("hash"), "mem0_migration_import_key": migration_import_key(source, record), } ) return {key: value for key, value in metadata.items() if value is not None} ``` Sources: [scripts/oss-to-platform-migrate.sh:846-859](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L846-L859) ### Streaming and Scope Pagination When streaming records to the platform, the script queries existing remote memories across affected scopes to prevent duplicate imports. Scopes are extracted from payloads and paginated against the platform API endpoint using designated page sizes and filter parameters. ```bash def platform_get_all_scope(base_url: str, api_key: str, scope_field: str, scope_value: str) -> List[Dict[str, Any]]: memories: List[Dict[str, Any]] = [] page = 1 while True: response = platform_request_json( "POST", f"{base_url}/v3/memories/?page={page}&page_size={DEFAULT_PLATFORM_PAGE_SIZE}", api_key=api_key, payload={"filters": {scope_field: scope_value}, "source": "migration"}, ) page_results = response.get("results") if not isinstance(page_results, list): raise MigrationError("Platform get_all response did not include a results list.") memories.extend(memory for memory in page_results if isinstance(memory, dict)) if not response.get("next"): return memories page += 1 ``` Sources: [scripts/oss-to-platform-migrate.sh:896-913](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L896-L913) > [!WARNING] > Platform request functions intercept HTTP status codes `401` and `403` to raise an `AuthError`, instructing operators to verify their `MEM0_API_KEY` or re-authenticate before retrying the data stream. Sources: [scripts/oss-to-platform-migrate.sh:791-792](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L791-L792) ## Integration provider transition ### Mode Selection and Initialization Switching active client provider implementations between open source engines and hosted platform backends is governed by the `createProvider` factory function and runtime configuration loading. The plugin inspects the configuration mode flag and instantiates either the self-hosted `OSSProvider` or the hosted `PlatformProvider`. ```typescript export function createProvider( cfg: Mem0Config, api: OpenClawPluginApi, ): Mem0Provider { if (cfg.mode === "open-source") { // v3.0.0: use customInstructions (was customPrompt) return new OSSProvider(cfg.oss, cfg.customInstructions, (p) => api.resolvePath(p), ); } return new PlatformProvider(cfg.apiKey!, cfg.baseUrl); } ``` Sources: [integrations/openclaw/providers.ts:530-542](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/providers.ts#L530-L542) ```typescript const provider = createProvider(cfg, api); const lifecycle = createMemoryLifecycle(); lifecycle.beginSession(); // Create Backend instance — PlatformBackend for platform mode, providerToBackend adapter for OSS let backend: Backend; if (cfg.mode === "platform") { backend = new PlatformBackend({ apiKey: cfg.apiKey!, baseUrl: cfg.baseUrl ?? "https://api.mem0.ai", }); } else { backend = providerToBackend(provider, cfg.userId); } ``` Sources: [integrations/openclaw/index.ts:170-183](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L170-L183) > [!IMPORTANT] > When `cfg.needsSetup` evaluates to true during plugin initialization, the plugin suppresses provider instantiation and enters a setup-waiting state, registering only the CLI and a placeholder service until an API key is configured via `openclaw mem0 init`. Sources: [integrations/openclaw/index.ts:137-168](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/index.ts#L137-L168) ### Backend Protocol Adapter Because open source mode uses client-side storage drivers while platform mode communicates directly with hosted API endpoints, the `providerToBackend` function wraps any `Mem0Provider` implementation into a uniform `Backend` interface. This adapter translates backend calls into provider methods while restricting platform-only capabilities like entity management and event tracking. | Backend Method | Underlying Provider Action | Restriction / Fallback | | :--- | :--- | :--- | | `add` | `provider.add(msgs, opts)` | Forces source to `"OPENCLAW"` and respects inference options. | | `search` | `provider.search(query, opts)` | Passes filters, `top_k`, and `threshold` with source scope. | | `get` | `provider.get(memoryId)` | Directly retrieves single memory items. | | `listMemories` | `provider.getAll(opts)` | Paginates local records via page size constraints. | | `update` | `provider.update(memoryId, content)` | Applies text changes; logs a warning if metadata-only updates are attempted. | | `delete` | `provider.delete` / `deleteAll` | Clears individual items or all records for a user ID. | | `deleteEntities` | — | Throws an error: `"Entity management is only available in platform mode."` | | `status` | — | Returns `{ connected: true, backend: "oss" }`. | | `entities` | — | Throws an error: `"Entity management is only available in platform mode."` | | `listEvents` / `getEvent` | — | Throws an error: `"Event management is only available in platform mode."` | ```typescript export function providerToBackend( provider: Mem0Provider, userId: string, ): Backend { return { async add(content, messages, opts = {}) { const msgs = messages ?? (content ? [{ role: "user", content }] : []); // v3.0.0: removed immutable, expiration_date const result = await provider.add( msgs as Array<{ role: string; content: string }>, { user_id: opts.userId ?? userId, source: "OPENCLAW", ...(opts.runId && { run_id: opts.runId }), ...(opts.metadata && { metadata: opts.metadata }), ...(opts.infer === false && { infer: false }), }, ); return result as unknown as Record; }, async search(query, opts = {}) { // v3.0.0: removed keyword_search, reranking const results = await provider.search(query, { user_id: opts.userId ?? userId, top_k: opts.topK, threshold: opts.threshold, filters: opts.filters, source: "OPENCLAW", }); return results as unknown as Record[]; }, async get(memoryId) { const item = await provider.get(memoryId); return item as unknown as Record; }, async listMemories(opts = {}) { const items = await provider.getAll({ user_id: opts.userId ?? userId, page_size: opts.pageSize, source: "OPENCLAW", }); return items as unknown as Record[]; }, async update(memoryId, content, metadata) { if (content) await provider.update(memoryId, content); if (metadata) { // OSS provider doesn't support metadata-only updates — log warning console.warn( "providerToBackend: metadata updates are not supported in OSS mode, only text updates are applied", ); } return { id: memoryId, updated: true }; }, async delete(memoryId, opts = {}) { if (opts.all) { await provider.deleteAll(opts.userId ?? userId); return { deleted: "all" }; } if (memoryId) { await provider.delete(memoryId); return { deleted: memoryId }; } throw new Error("Either memoryId or all is required"); }, async deleteEntities() { throw new Error("Entity management is only available in platform mode."); }, async status() { return { connected: true, backend: "oss" }; }, async entities() { throw new Error("Entity management is only available in platform mode."); }, async listEvents() { ``` Sources: [integrations/openclaw/providers.ts:555-641](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/providers.ts#L555-L641) > [!WARNING] > Attempting to execute entity management or event inspection through the provider-to-backend adapter in open-source mode will immediately throw execution errors, as those features require a hosted platform backend connection. Sources: [integrations/openclaw/providers.ts:625-640](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/openclaw/providers.ts#L625-L640) ## Validation and post-migration verification ### Review Artifact Generation Post-migration verification ensures that uploaded records match their source counterparts and maintain metadata integrity across the platform boundary. During the import execution loop, the migration routine tracks import outcomes, statuses, and validation failures by compiling review records. When the import run completes, failure states and skipped records are serialized into a timestamped review file to assist operators in auditing data parity. ```bash def review_file_path(input_path: Path) -> Path: timestamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ") return input_path.with_name(f"{input_path.stem}-import-review-{timestamp}.json") def write_import_review_file(input_path: Path, review_records: List[Dict[str, Any]], summary: Dict[str, Any]) -> Optional[Path]: if not review_records: return None path = review_file_path(input_path) payload = { "version": 1, "kind": "mem0_platform_import_review", "created_at": utc_now(), "summary": summary, "records": review_records, } path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8") os.chmod(path, stat.S_IRUSR | stat.S_IWUSR) return path ``` Sources: [scripts/oss-to-platform-migrate.sh:941-960](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L941-L960) > [!NOTE] > Review files are generated using the local input filename suffixed with `-import-review-` and an explicit UTC timestamp to prevent concurrent migration runs from overwriting audit logs. Sources: [scripts/oss-to-platform-migrate.sh:941-943](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L941-L943) ### Metadata and Entity Scope Validation To verify that entity relationships and local provenance survive migration intact, imported records preserve structural linkage through custom metadata fields. Each imported payload embeds migration tracking attributes derived from the source collection and point identifiers. | Metadata Field | Source Property | Purpose in Platform | | :--- | :--- | :--- | | `mem0_migration_source` | `record.get("source")` | Tracks the originating OSS store driver (default `"python_oss_qdrant"`). | | `mem0_migration_collection` | `source.get("collection")` | Records the source vector collection name. | | `mem0_migration_local_id` | `record.get("id")` | Preserves the original point ID from the open source database. | | `mem0_migration_local_hash` | `record.get("hash")` | Retains content hash for collision and duplication checks. | | `mem0_migration_import_key` | `migration_import_key(source, record)` | Stores a SHA-256 digest identifying the unique import stream key. | ```bash def build_import_metadata(source: Dict[str, Any], record: Dict[str, Any]) -> Dict[str, Any]: existing_metadata = record.get("metadata") if isinstance(record.get("metadata"), dict) else {} metadata = dict(existing_metadata) metadata.update( { "mem0_migration_source": record.get("source") or "python_oss_qdrant", "mem0_migration_collection": source.get("collection"), "mem0_migration_local_id": record.get("id"), "mem0_migration_local_hash": record.get("hash"), "mem0_migration_import_key": migration_import_key(source, record), } ) return {key: value for key, value in metadata.items() if value is not None} ``` Sources: [scripts/oss-to-platform-migrate.sh:846-859](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L846-L859) > [!WARNING] > Verification queries against the platform API must ensure that at least one entity scope (`user_id`, `agent_id`, or `run_id`) is correctly mapped; payloads lacking all three entity scopes will fail payload building and trigger an import error. Sources: [scripts/oss-to-platform-migrate.sh:882-884](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/scripts/oss-to-platform-migrate.sh#L882-L884) ## Related - [FastAPI Application](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/server-dashboard/fastapi-application) - [Vector Store Architecture](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/vector-store-architecture) --- ## User guide: Quickstart Overview URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/getting-started/quickstart-overview ## Overview Mem0 gives your AI agents and applications persistent, intelligent long-term memory. It automatically extracts, stores, updates, and searches memories across chat sessions so your AI can remember user preferences, facts, and past decisions over time. To get started, you can choose between three distinct ways to run Mem0 depending on your privacy, setup complexity, and infrastructure needs: - **Mem0 Platform (Cloud):** A fully managed cloud service hosted by Mem0. The easiest way to get started with zero infrastructure to maintain. - **Local Library:** Run Mem0 directly inside your application or local script, storing data locally on your machine. - **Self-Hosted Server:** Run your own private Mem0 server using Docker or local infrastructure for complete data control. --- ## Choosing Your Deployment Mode Review the comparison table below to decide which setup fits your workflow. | Mode | Best For | Setup Complexity | Infrastructure | | :--- | :--- | :--- | :--- | | **Platform** | Quick testing, cloud apps, zero maintenance | Low (Minutes) | Fully Managed Cloud | | **Local Library** | Local scripts, offline use, privacy-first prototyping | Low | Local Storage | | **Self-Hosted Server** | Production deployments requiring complete data privacy | Medium | Docker / Custom Server | --- ## Getting Started ### Installation and Setup Wizard Follow these steps to set up Mem0 using the interactive setup wizard or your preferred deployment approach. 1. **Install the CLI or Library:** Depending on your environment, install the Mem0 command-line tools or python/node libraries. For instance, run the interactive setup command: ```bash mem0 init ``` 2. **Choose Your Deployment Mode:** When you run the setup wizard, you will be prompted to select how you want to use Mem0: - **Platform (Recommended):** Hosted memory managed by Mem0. You can authenticate either by logging in with your email (receiving a verification code) or by entering your API key manually. - **Open Source / Self-Hosted:** Run your own open-source stack by selecting your preferred language model (LLM), embedder, and vector store providers. 3. **Configure Your Environment:** If you are using the cloud platform, you will need to make your API key available to your application by setting the environment variable: ```bash export MEM0_API_KEY="your-api-key-here" ``` If you are running a self-hosted server, point your application to your server URL using: ```bash export MEM0_HOST="http://localhost:8888" ``` > [!NOTE] > You can get your Mem0 Platform API key directly from your account dashboard on the web if you prefer manual entry over email authentication. --- ## Key Concepts To use Mem0 effectively, it helps to understand a few core concepts: - **Memory Search:** Semantic retrieval that finds relevant facts based on meaning rather than exact keyword matches. - **Auto-Capture & Extraction:** The ability to automatically scan conversation turns, distill lasting facts, and de-duplicate them on the storage backend. - **Namespaces & Scoping:** Organizing memories using identifiers (such as user IDs or agent IDs) so different users and AI agents have isolated memory spaces. --- ## Tips and Warnings > [!TIP] > For multi-part or complex questions, perform multiple searches with varying search queries or angles to ensure your agent gathers every relevant piece of background history. > [!WARNING] > If you configure a self-hosted server, make sure your environment variables (such as `MEM0_HOST`) do not conflict with your platform mode settings, or your application may route requests to the wrong endpoint. ## Related - [Command Line Setup](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/getting-started/command-line-setup) - [Managing Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/core-features/managing-memories) --- ## User guide: Command Line Setup URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/getting-started/command-line-setup ## Overview ### Introduction The Command Line Setup allows you to install the Mem0 command-line interface, initialize your user or agent account, and manage credentials directly from your terminal. Whether you are setting up a standard user account or bootstrapping an unattended agent environment, the initialization wizard guides you through authentication, configuration storage, and identity management. ### Interactive Setup and Authentication When you first run the setup wizard, you can choose between logging in with your email address or manually entering an API key. ```mermaid flowchart TD A[Run mem0 init] --> B{Choose Authentication} B -->|Option 1| C[Login with Email] B -->|Option 2| D[Enter API Key Manually] C --> E[Receive Verification Code] E --> F[Enter Code in Terminal] F --> G[Save Credentials] D --> G[Save Credentials] ``` ### Step-by-Step Initialization To configure your terminal environment and connect to Mem0, follow these steps: 1. **Launch the setup wizard:** Run the initialization command in your terminal. ```bash mem0 init ``` 2. **Select your authentication method:** When prompted, choose whether to sign in using your email address (recommended) or by providing an API key manually. 3. **Verify your identity:** If you chose email login, enter your email address and type the verification code sent to your inbox when prompted. 4. **Set your default user scope:** Define a default user identifier to scope your memories, or press Enter to accept the recommended default based on your system username. > [!NOTE] > Saved credentials and configuration settings are automatically stored in your user directory at `~/.mem0/config.json`. --- ## Non-Interactive and Automated Setup ### Introduction For automated scripts, continuous integration environments, or unattended terminal setups, you can pass command-line options directly to skip interactive prompts. ### Command Options | Option | Description | | :--- | :--- | | `--api-key ` | Provide an API key directly to skip the setup prompt. | | `-u, --user-id ` | Set a default user ID for scoping your memories. | | `--email ` | Start the email verification login flow. | | `--code ` | Provide the email verification code for non-interactive login. | | `--agent` | Bootstrap an unattended Agent Mode account without requiring an email. | | `--source ` | Provide channel attribution for signups (e.g., `github`, `hn`, `ph`). | | `--agent-caller ` | Self-declare your AI agent identity (e.g., `claude-code`, `cursor`). | | `--force` | Overwrite an existing configuration file without prompting for confirmation. | ### Quick Setup Examples * **Standard initialization:** ```bash mem0 init ``` * **Non-interactive API key setup:** ```bash mem0 init --api-key m0-xxx --user-id alice ``` * **Non-interactive email login:** ```bash mem0 init --email you@example.com --code 123456 ``` * **Unattended Agent Mode bootstrap:** ```bash mem0 init --agent --agent-caller claude-code ``` > [!TIP] > If you already have a valid API key configured in your environment variables via `MEM0_API_KEY`, the CLI will automatically detect and reuse it during initialization. --- ## Agent Mode and Account Management ### Introduction Mem0 supports Agent Mode for AI coding assistants and autonomous agents. This allows an agent to bootstrap a functional memory store instantly without requiring a manual email sign-up step. ### Managing Agent Identity * **Bootstrap Agent Mode:** Run the initialization command with the agent flag to create an unattended account: ```bash mem0 init --agent ``` * **Tag your active agent:** If you did not specify an agent caller during bootstrap, you can tag your active key later using the identify command: ```bash mem0 identify claude-code ``` * **Check your active identity:** Print your current agent identifier (AGENTRUSH identifier) using the whoami command: ```bash mem0 whoami ``` > [!IMPORTANT] > Supported self-declared agent identities include: `claude-code`, `cursor`, and custom bot names. ### Claiming an Agent Account If you started using Mem0 in Agent Mode and want to upgrade your account to a permanent human-owned profile linked to your email address, you can claim the account at any time: ```bash mem0 init --email you@example.com ``` This process uses a verification code sent to your inbox to link your existing agent API key to your email address without changing or invalidating your current key. ## Related - [Quickstart Overview](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/getting-started/quickstart-overview) - [Managing Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/core-features/managing-memories) --- ## User guide: Managing Memories URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/core-features/managing-memories ## Overview ### About Managing Memories Managing memories allows you to store, view, search, update, and delete facts and conversational history across different users, agents, and sessions. The memory system processes interactions to extract relevant preferences, details, and context, making future interactions personalized and informed. ### Core Concepts To effectively manage memories, it helps to understand how data is organized and targeted: * **Memory Identifier (`memory_id`)**: A unique identifier assigned to every individual saved memory item, used when retrieving history, updating, or deleting specific records. * **Identifiers & Scopes**: Memories can be isolated or grouped using parameters such as `user_id`, `agent_id`, `app_id` (or project ID), and `run_id` (session ID). * **Inference**: By default, text or messages passed to the memory system undergo smart extraction to pull out distinct, manageable facts. You can disable this to store entries verbatim. --- ## Saving Memories ### Storing Information You can save new information by providing raw text, individual facts, or full conversation turns. The system automatically processes the input to extract useful facts, preferences, or details. ### How to Save a Memory 1. Provide the content you want to store (either as a direct string, a list of facts, or conversation messages). 2. Specify optional scoping parameters such as a user ID, agent ID, or app/project ID to organize where the memory lives. 3. Submit the request to store the information. The system confirms creation or notes any updates made to existing overlapping facts. > [!TIP] > If you want to store a fact verbatim without the system attempting automatic fact extraction, set the inference option to false. --- ## Viewing and Searching Memories ### Retrieval Methods You can retrieve stored records either by searching for semantic relevance or by browsing through lists within a specific scope. ### Browsing All Memories To view stored memories without a specific query, you can list or paginate through records using your filters or scoping parameters (such as `user_id`, `agent_id`, or `run_id`). ### Searching Memories To find memories relevant to a specific topic or question: 1. Provide a search query string. 2. Apply optional filters (such as `user_id` or metadata filters) to narrow your search space. 3. Adjust the limit (`top_k`) to control how many results are returned. > [!NOTE] > Top-level entity parameters inside search requests are deprecated in favor of nested filter objects. Always scope searches using filters where supported. --- ## Updating Memories ### In-Place Modifications When a stored fact or preference changes, you can modify it in place rather than deleting and re-creating it. Updating in place preserves the unique memory identifier and its complete modification history. ### How to Update a Memory 1. Locate the exact `memory_id` of the record you wish to change. 2. Provide the updated text content, new metadata, or expiration date values. 3. Submit the update. The system re-indexes the new content and records an update event in the history log. > [!IMPORTANT] > At least one modifiable field (such as text, metadata, timestamp, or expiration date) must be provided when updating a memory. --- ## Deleting Memories ### Removal and Cleanup You can remove individual memories that are obsolete or incorrect, or clear out entire groups of memories when requested. ### Deleting a Single Memory 1. Identify the unique `memory_id` of the record you want to remove. 2. Issue a deletion request for that ID. The memory and its associated tracking links are permanently removed. ### Deleting Bulk Memories You can delete all memories associated with a specific user, agent, app, or session identifier. > [!CAUTION] > Bulk deletions and project-wide memory wipes are irreversible. Ensure you have explicit confirmation before clearing all data for a user or project scope. ## Related - [Searching Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/core-features/searching-memories) - [Organizing User Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/how-to-guides/organizing-user-memories) --- ## User guide: Searching Memories URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/core-features/searching-memories ## Overview ### Introduction Searching memories allows you to query, rank, and retrieve relevant stored facts and context using natural language queries, keyword matching, and semantic filters. The search engine combines semantic vector similarity with keyword ranking (BM25) and entity boosting to surface the most contextually relevant information for users or AI agents. > [!NOTE] > Effective searches require at least one entity identifier (`user_id`, `agent_id`, or `run_id`) to scope the results and ensure data isolation. --- ### Running a Basic Search You can search your memory store using the command-line interface or programmatically. To search memories using natural language via the command line: 1. Open your terminal. 2. Run the search command with your query and a user identifier: ```bash mem0 search "preferences" --user-id alice ``` 3. (Optional) Customize the number of results returned using the `--top-k` flag and control the match strictness using `--threshold`: ```bash mem0 search "tools" --user-id alice --top-k 5 --threshold 0.2 ``` --- ### Refining Search Results ### Filtering and Scoping Options When querying memories, you can restrict results using entity filters or custom JSON filter criteria. | Option / Flag | Description | | :--- | :--- | | `--user-id` | Scopes the search to a specific user. | | `--agent-id` | Scopes the search to a specific AI agent. | | `--run-id` | Scopes the search to a specific execution run. | | `--top-k` | Maximum number of results to return (default varies by client, typically 10 to 20). | | `--threshold` | Minimum similarity score threshold between 0.0 and 1.0. | | `--rerank` | Opt-in flag to invoke a secondary reranking model for higher precision. | | `--keyword` | Enables hybrid keyword search alongside semantic search. | | `--filter` | Applies structured metadata filters using JSON format. | > [!TIP] > You can combine semantic search with structured filters to query specific categories or metadata tags. For example: > ```bash > mem0 search "invoices" --user-id alice --filter '{"AND": [{"categories": {"in": ["work"]}}]}' > ``` --- ### Search Workflow The following diagram illustrates how search queries are processed, scored, and returned to the user or agent: ```mermaid flowchart TD A[User enters natural language query] --> B[Sanitize and extract query entities] B --> C[Run Semantic & Keyword Search] C --> D[Apply Filters & Entity Boosts] D --> E[Score and Rank Candidates] E --> F[Optional Reranking] F --> G[Return Formatted Results] ``` > [!WARNING] > If a search query is submitted without an accompanying entity identifier (`user_id`, `agent_id`, or `run_id`), the system will reject the request with an error to maintain proper data scoping. ## Related - [Managing Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/core-features/managing-memories) - [Organizing User Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/how-to-guides/organizing-user-memories) --- ## User guide: Advanced Memory Capabilities URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/core-features/advanced-memory-capabilities ## Overview The software includes advanced memory capabilities designed to help AI agents maintain a coherent, persistent understanding of users and projects over time. Rather than treating every chat interaction as completely brand new, these features allow the system to extract meaningful facts from everyday conversations, map out relationships between different pieces of information, anchor events to precise points in time, and automatically compile structured user profiles. This guide explains how these advanced capabilities work together to create a smarter, more personalized AI experience. --- ## Core Concepts Understanding how the system processes and organizes information requires familiarity with a few key concepts: * **Memory Extraction**: The automated process of reading through user and assistant conversations to pull out durable facts, preferences, plans, and professional details while discarding transient filler or greetings. * **Memory Linking**: The ability to connect related pieces of information together (such as linking a vet checkup record to an existing profile about a user's pet) so the system can build a contextual web of knowledge. * **Temporal Grounding**: The translation of relative time references (like "yesterday" or "last week") into concrete, absolute dates based on when the conversation actually took place, ensuring memories remain accurate long into the future. * **Profile Generation**: The automated creation and maintenance of structured summary profiles for users based on accumulated interactions and configured data schemas. --- ## How Features Work ### Graph Relationships and Memory Linking When new memories are extracted, the system checks whether they relate to any existing memories in storage. If a connection is found—such as a new project milestone for a known job, or a follow-up event about a known entity—the system links them together using reference identifiers. This allows the AI to retrieve not just isolated facts, but entire clusters of related context during future conversations. ### Temporal Reasoning and Time Grounding Conversations often contain relative time phrases that lose meaning over time. The system uses the observation date of a conversation to anchor relative statements into absolute dates: * "yesterday" becomes the exact calendar day prior to the observation date. * "last week" becomes the specific preceding week. * "recently" is tied directly to the historical timeframe of the discussion rather than the current system clock. > [!NOTE] > This grounding ensures that statements like "User went to Paris last week" remain permanently meaningful even years later, rather than becoming ambiguous. ### Automatic Profile Generation As users interact with the system over time, accumulated facts and preferences feed into automated profile generation. Rather than requiring manual updates, profiles compile key user attributes based on configured project schemas and custom extraction instructions. * **Asynchronous Processing**: Profiles build automatically once a user's interaction history crosses internal message thresholds. * **Instant Refresh**: You can also trigger profile generation immediately when needed using dedicated client actions. * **Status Tracking**: Profile creation jobs run asynchronously, allowing you to check progress statuses (`succeeded`, `pending`, `failed`, `not_enabled`, or `insufficient_data`) as they process. --- ## Managing Profiles ### Checking User Profiles To retrieve the current memory profile for a specific user: 1. Identify the unique user identifier supplied when memories were added (for example, `"alice"`). 2. Request the profile data using the user identifier. 3. Check the returned status field to see whether the profile is fully generated or still processing. ### Customizing Profile Settings You can view and update project-wide profile configurations to tailor what information gets captured: * **Enabled State**: Turn automated profile generation on or off across your project. * **Schema Definition**: Provide a structured JSON schema where every property includes a clear description to guide extraction. * **Custom Instructions**: Supply extra rules or behavioral guidance for the extraction step. > [!TIP] > Before applying a new profile schema to all users, use the sampling feature to test extraction against a small group of real users first. This lets you verify your schema design against live memory data before rolling it out broadly. ## Related - [Searching Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/core-features/searching-memories) - [Organizing User Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/how-to-guides/organizing-user-memories) --- ## User guide: Organizing User Memories URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/how-to-guides/organizing-user-memories ## Overview ### Introduction Organizing and partitioning your memories ensures that your AI assistants and coding tools recall relevant context without mixing up distinct projects, personal preferences, or sub-agent tasks. By leveraging identifiers such as user scopes, project tags, and custom categories, you can precisely control where memories are stored and retrieved. > [!NOTE] > Proper organization prevents unrelated project details from polluting your global profile while allowing shared preferences to remain accessible across different coding environments. --- ### Core Concepts Understanding how memories are categorized and partitioned helps you structure your data effectively: * **Scopes**: Define the boundary of a memory. You can store information at the project level (specific to the current repository), session level (ephemeral to a single run), or global level (user-wide across all projects). * **Identifiers (`user_id`, `agent_id`, `run_id`)**: Parameters used to namespace your data. Entity identifiers partition memories so that different users, specific sub-agents, or automated runs do not overwrite each other. * **Coding Categories**: Predefined classifications for technical knowledge, architectural decisions, workflows, problems, and fixes that help structure what the agent learns about a codebase. --- ### Tagging and Partitioning Across Users and Sub-Agents Memories are automatically partitioned based on the active session context, but you can explicitly target specific namespaces using tool parameters or configuration settings. 1. **Main User Namespace**: By default, memories attach to your primary user identifier or system username. 2. **Sub-Agent Isolation**: Sessions running under sub-agents automatically namespace their data using session routing keys, ensuring that ephemeral sub-agent tasks do not clutter your main history. 3. **Explicit Scoping**: When adding or searching for memories via tools, you can specify a write scope: * `"project"` (default): Scoped to the current repository or application. * `"session"`: Scoped strictly to the active run. * `"global"`: User-wide across all projects and repositories. --- ### Customizing Memory Categories Projects can automatically bootstrap custom categories to organize technical knowledge effectively. The standard coding memory categories include: | Category Name | Description | | :--- | :--- | | `project_knowledge` | What the project is and how its code, APIs, data, files, and components work. | | `decisions_and_constraints` | Why an approach was chosen, what must remain true, and rules future work must follow. | | `workflows` | How to run, test, debug, deploy, configure, or otherwise work on the project. | | `problems_and_fixes` | Bugs, failures, known pitfalls, their causes, and how to fix or avoid them. | | `results` | Outcomes and measurements from tests, benchmarks, experiments, or investigations. | --- ### Example: Adding a Scoped Memory When adding a memory through an agent tool, you can pass specific parameters to control its scope and categorization: ```json { "text": "User switched from almond milk to oat milk lattes after developing an almond sensitivity", "scope": "project", "metadata": { "category": "preference", "confidence": 0.9 } } ``` > [!TIP] > Use the `"global"` scope sparingly and only when saving universal user preferences (such as preferred coding languages or communication styles) that apply across every repository. ## Related - [Managing Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/core-features/managing-memories) - [Searching Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/core-features/searching-memories) --- ## User guide: Exporting and Sharing URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/how-to-guides/exporting-and-sharing ## Overview This guide explains how to back up and export your memory archives, migrate data between platforms, and set up notification webhooks. These features help you maintain data portability, integrate with external systems, and ensure your stored memories can be easily backed up or transferred. ## Exporting Memory Archives ### Export Overview You can generate and retrieve archives of your stored memories using export tools and API methods. Exports allow you to back up your data or format memories into structured schemas. ### Creating a Memory Export 1. Define the filters (such as specific users, runs, or categories) and the JSON schema or format requirements for your data. 2. Initiate the export request via the API or dashboard to generate a new export archive. 3. Once processing is complete, retrieve the export package using the returned export identifier. > [!NOTE] > Exports may process asynchronously depending on the volume of data. Use the status checking tools or event trackers to confirm when your export file is ready for download. ## Migrating Data Between Platforms ### Migration Overview If you are transitioning memory data from a self-hosted vector store or community setup to a managed platform account, you can use migration and import utilities. ### Migration Steps 1. **Export existing data**: Run the export script or utility targeting your current storage collection (such as a local database or Qdrant collection) to output a structured JSON archive. 2. **Review the export file**: Inspect the generated JSON file to verify that record counts and metadata attributes match your expectations. 3. **Import to the target platform**: Run the import utility referencing your export file and destination platform credentials. > [!WARNING] > Ensure your API keys and environment variables are correctly configured before running import or migration commands to prevent partial data transfers or authentication failures. ## Setting up Notification Webhooks ### Webhook Overview Webhooks enable your projects to receive automated real-time HTTP notifications whenever specific events occur within your project. ### Managing Webhooks You can list, create, update, and delete webhooks programmatically or via your project settings. When creating a webhook, you must provide a valid target URL, a descriptive name, and a list of event types that will trigger notifications. | Action | Description | | :--- | :--- | | **List Webhooks** | Retrieve all configured webhooks associated with your project ID. | | **Create Webhook** | Register a new endpoint URL and subscribe to specific event types. | | **Update Webhook** | Modify an existing webhook's name, target URL, or event subscriptions. | | **Delete Webhook** | Remove a webhook configuration permanently to stop notifications. | > [!TIP] > Always verify that your target webhook endpoint can successfully receive and respond to incoming HTTP POST requests with a 2xx status code to avoid missed event deliveries. ## Related - [Managing Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/core-features/managing-memories) - [Storage Provider Settings](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/configuration/storage-provider-settings) --- ## User guide: Building AI Companions URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/how-to-guides/building-ai-companions ## Overview Building AI companions requires more than a simple chat window; an effective companion needs to retain your preferences, personality traits, and history across multiple sessions. By integrating **Mem0** into your conversational assistant or framework, your assistant gains persistent long-term memory, semantic recall, and automatic context updates. This guide covers how to wire memory management into interactive frameworks so that your AI companions remember important details, respect custom memory scopes, and seamlessly adapt to users over time. > [!NOTE] > Persistent memory allows your AI companion to recall past decisions, personal details, and workflows without requiring you to re-explain yourself in every new conversation. --- ## Setting Up Persistent Memory for Assistants ### Understanding Authentication and Configuration To wire memory into an interactive conversational assistant, you typically choose a framework integration or plugin and configure your authentication and scoping preferences. Every memory integration requires authentication to connect your assistant to the Mem0 platform or your self-hosted backend. Set your API key in your environment variables: ```bash export MEM0_API_KEY="your-api-key-here" ``` If you are running a self-hosted instance rather than the cloud platform, you can configure a custom host URL instead: ```bash export MEM0_HOST="https://your-self-hosted-mem0-instance.com" ``` ### Configuring Memory Scopes Memories are organized using namespaces to keep data cleanly separated between users, projects, and active runs. The available scope identifiers include: | Scope Identifier | What It Controls | Example Use Case | | :--- | :--- | :--- | | `user_id` | Identifies the unique human user owning the memory. | Separating preferences for different users. | | `agent_id` | Partitions memories by a specific AI agent's identity. | Giving different companions separate personalities. | | `run_id` | Limits memories to a specific active run or conversation session. | Storing temporary state for a single working session. | | `app_id` | Groups memories under a specific application or project. | Isolating project knowledge in a coding workspace. | --- ## Step-by-Step: Wiring Memory into Frameworks ### Framework Integration Workflow Depending on your conversational framework, you can hook Mem0 directly into the message loop for automatic context injection and conversation extraction. ### Step 1: Initialize the Memory Store Initialize the memory store with your credentials and default scope settings. For example, when using memory stores in backend pipelines, specify your user namespace and enable extraction: ```python from mem0_strands import Mem0MemoryStore # Configure semantic memory with automatic server-side extraction store = Mem0MemoryStore(user_id="alex", writable=True, extraction=True) ``` ### Step 2: Enable Auto-Recall and Auto-Capture Most companion integrations support automated hooks that perform two key functions: 1. **Auto-Recall:** Before generating a response, the system searches stored memories for relevant facts based on your latest prompt and injects them into the system prompt. 2. **Auto-Capture:** After a conversation turn or session ends, the system passes the conversation turns to Mem0 to extract durable facts, preferences, and decisions. ```typescript // Example configuration snippet for auto-recall and auto-capture settings const mem0Config = { autoRecall: true, autoCapture: true, topK: 5, searchThreshold: 0.7, }; ``` ### Step 3: Use Explicit Memory Tools If your companion framework supports agent-callable tools, the assistant can proactively search, add, update, or delete memories during conversations. ```markdown User does X (e.g., states a preference) → System extracts durable fact → Memory stored via add_memory tool → Future queries retrieve context via search_memory tool ``` Available core tool actions typically include: - **Search:** Recalls facts ranked by semantic relevance. - **Add:** Stores a durable fact verbatim or via extraction. - **Update:** Replaces the text of an outdated memory by its unique ID. - **Delete:** Removes obsolete or forgotten memories. --- ## Examples & Configuration Options Below is an example of configuring a chat completion wrapper with Mem0 to automatically manage memory injection and background saving: ```python from mem0 import MemoryClient # Initialize the client client = MemoryClient(api_key="your-api-key") # Add a conversation turn to memory client.add( [ {"role": "user", "content": "I prefer vegetarian recipes and live in Seattle."}, {"role": "assistant", "content": "Got it! I'll keep your dietary preference in mind."} ], user_id="alex-user" ) # Search relevant memories for future turns memories = client.search( query="What does the user like to eat?", filters={"user_id": "alex-user"} ) ``` > [!TIP] > When building multi-agent companions, use distinct `agent_id` parameters to ensure each companion maintains its own distinct conversational history and persona traits. --- ## Tips and Warnings > [!WARNING] > Extraction runs asynchronously on the server side in some configurations. Do not immediately search for a newly added memory in the exact same turn expecting it to be indexed instantly. > [!CAUTION] > Avoid passing sensitive credentials, raw secrets, or private keys into conversational prompts. Ensure your extraction filters redact sensitive tokens before writing them to long-term storage. > [!TIP] > For chat models using framework plugins (such as LangChain or custom orchestrators), utilize helper functions like `condenseMem0MemoryIntoHumanMessage` or system prompt injection utilities to cleanly separate reference memories from active user questions. ## Related - [Managing Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/core-features/managing-memories) - [Organizing User Memories](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/how-to-guides/organizing-user-memories) --- ## User guide: AI Model Selection URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/configuration/ai-model-selection ## Overview The AI Model Selection feature allows you to configure, switch, and customize the underlying Large Language Models (LLMs), embedding providers, and vector stores that power your memory engine. Whether you want to leverage cloud-powered intelligence using hosted APIs or maintain complete data privacy with fully local execution, you can easily select the providers that best fit your infrastructure and workflow requirements. --- ## Supported Providers ### LLM Providers Supported LLM providers: `openai`, `openai_structured`, `anthropic`, `groq`, `ollama`, `lmstudio`, `google` (or `gemini`), `azure_openai`, `mistral`, `langchain`, `deepseek`, `xai`, `sarvam`, `aws_bedrock`, `litellm`, `minimax`, `together`, `vllm`, `azure_openai_structured`. ### Embedder Providers Supported embedder providers: `openai`, `aws_bedrock`, `ollama`, `lmstudio`, `together`, `google` (or `gemini`), `azure_openai`, `fastembed`, `langchain`, `vertexai`, `huggingface`. ### Vector Store Providers Supported vector store providers: `qdrant`, `chroma`, `pgvector`, `pinecone`, `mongodb`, `milvus`, `baidu`, `cassandra`, `neptune`, `upstash_vector`, `azure_ai_search`, `azure_mysql`, `redis`, `valkey`, `databricks`, `elasticsearch`, `vertex_ai_vector_search`, `opensearch`, `supabase`, `weaviate`, `faiss`, `langchain`, `s3_vectors`, `turbopuffer`, `oracledb`, `memory`. > [!NOTE] > When switching embedding providers or changing model dimensions, ensure your vector store collection matches the new dimension size to avoid compatibility errors. --- ## Configuring Providers via Dashboard You can update your LLM and embedding providers directly from the web dashboard. 1. Open your self-hosted dashboard and navigate to the **Configuration** page. 2. Locate the **LLM Provider** section. 3. Select your desired provider from the **Provider** dropdown menu. 4. Enter your preferred **Model** identifier (e.g., `gpt-5-mini`) and input your corresponding **API Key** if required. 5. Scroll down to the **Embedding Model** section and select your preferred embedding provider and model. 6. Click **Save Configuration** to apply your changes. > [!TIP] > If you are using an OpenAI-compatible local provider or Ollama, you can often reuse the base URL and authentication settings across both LLM and embedding configurations. --- ## Configuring Providers via Interactive CLI Wizard ### Wizard Steps If you are setting up your environment via the command line, the interactive configuration wizard will guide you through provider selection step-by-step. 1. **LLM Provider Selection:** The wizard displays a list of available LLM options (such as OpenAI, Anthropic, or Ollama). Type the number corresponding to your choice and press Enter. If prompted, supply your API key or custom server URL. 2. **Embedding Provider Selection:** Choose your embedding provider. If you select the same provider as your LLM, the wizard will automatically reuse your API key and connection URL from the previous step. 3. **Vector Store Selection:** Choose a vector database backend (such as Qdrant or PGVector). Follow the on-screen prompts to input connection details such as host, port, and credentials. --- ## Configuration Reference The following table summarizes the primary configuration options available when setting up providers manually in your configuration files: | Option Key | Description | Example Value | | :--- | :--- | :--- | | `llm.provider` | The active LLM provider identifier | `"openai"`, `"ollama"` | | `llm.config.model` | The specific model name to generate responses | `"gpt-5-mini"`, `"llama3.1:8b"` | | `llm.config.apiKey` | Authentication API key for cloud providers | `"sk-..."` | | `llm.config.baseURL` or `ollama_base_url` | Custom API endpoint or local server URL | `"http://localhost:11434"` | | `embedder.provider` | The active embedding provider identifier | `"openai"`, `"ollama"` | | `embedder.config.model` | The specific embedding model name | `"text-embedding-3-small"` | | `vectorStore.provider` | The active vector database provider | `"qdrant"`, `"pgvector"` | > [!WARNING] > Storing API keys in plaintext configuration files is discouraged. Always use environment variables or secure secret reference objects (such as `${OPENAI_API_KEY}` or `SecretRef`) where supported. ## Related - [Storage Provider Settings](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/configuration/storage-provider-settings) - [Quickstart Overview](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/getting-started/quickstart-overview) --- ## User guide: Storage Provider Settings URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/configuration/storage-provider-settings ## Overview Mem0 relies on vector stores to organize, store, and retrieve long-term memories efficiently. By default, Mem0 uses built-in vector storage options, but you can easily connect custom vector databases like Qdrant, Chroma, PGVector, or Pinecone to match your infrastructure requirements. Supported storage providers: `qdrant`, `chroma`, `pgvector`, `pinecone`, `mongodb`, `milvus`, `baidu`, `cassandra`, `neptune`, `upstash_vector`, `azure_ai_search`, `azure_mysql`, `redis`, `valkey`, `databricks`, `elasticsearch`, `vertex_ai_vector_search`, `opensearch`, `supabase`, `weaviate`, `faiss`, `langchain`, `s3_vectors`, `turbopuffer`, `oracledb`. Sources: `mem0/vector_stores/configs.py`, `mem0/utils/factory.py`, `mem0-ts/src/oss/src/utils/factory.ts`. --- ## Configuring a Vector Store Provider ### Supported Providers and Requirements When setting up your storage provider, you need to supply the appropriate connection settings depending on whether you are running locally or connecting to a managed cloud service. | Provider | Key Configuration Options | | :--- | :--- | | **Qdrant** | `host`, `port`, `path`, `url`, `api_key`, `on_disk`, `collection_name` | | **Chroma** | `path`, `host`, `port`, `api_key`, `tenant`, `collection_name` | | **PGVector** | `dbname`, `user`, `password`, `host`, `port`, `connection_string`, `hnsw` | | **Pinecone** | `api_key`, `collection_name`, `embedding_model_dims`, `serverless_config`, `pod_config` | | **Upstash Vector** | `url`, `token`, `collection_name`, `enable_embeddings` | > [!NOTE] > For cloud-managed providers like Pinecone or Upstash, make sure your API keys or REST tokens are correctly specified either inside the provider configuration block or through corresponding environment variables (such as `PINECONE_API_KEY` or `UPSTASH_VECTOR_REST_URL`). --- ## Step-by-Step Configuration ### 1. Install Required Dependencies Before connecting a custom vector database, ensure you have installed the necessary Python or TypeScript package bindings for your chosen provider. For example, if you are using Pinecone or Chroma in Python, install the respective packages: ```bash pip install pinecone pinecone-text chromadb ``` ### 2. Define the Vector Store Settings Configure your memory instance by specifying the `provider` name alongside its corresponding `config` parameters. ```python from mem0 import Memory config = { "vector_store": { "provider": "pinecone", "config": { "collection_name": "my-mem0-index", "embedding_model_dims": 1536, "metric": "cosine", "serverless_config": { "cloud": "aws", "region": "us-east-1" } } } } memory = Memory.from_config(config) ``` > [!WARNING] > Do not specify both `pod_config` and `serverless_config` simultaneously when configuring Pinecone. Choose one deployment method to avoid initialization errors. --- ## Best Practices and Tips > [!TIP] > **Dimension Matching:** Always ensure that the `embedding_model_dims` (or `dimension`) defined in your vector store configuration matches the dimension size output by your active embedding model (e.g., `1536` for OpenAI's `text-embedding-3-small`). > [!CAUTION] > **Extra Fields Validation:** Mem0's configuration validators strictly reject unrecognized parameter keys. Ensure you only include parameters supported by that specific provider's configuration schema. ## Related - [AI Model Selection](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/configuration/ai-model-selection) - [Quickstart Overview](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/getting-started/quickstart-overview) --- ## User guide: Agent IDE Plugins URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/integrations/agent-ide-plugins ## Overview ### What are Agent IDE Plugins Agent IDE plugins connect Mem0's persistent memory and cross-session context directly to your favorite AI coding environments. By integrating memory capabilities into editors and coding assistants, these plugins allow your tools to remember project knowledge, decisions, constraints, workflows, and bug fixes across different coding sessions and devices. Supported coding environments and platforms include: - Claude Code - Cursor - Codex - Kimi - OpenCode - Pi - OpenClaw - Hermes - Antigravity > [!NOTE] > Most plugins require a Mem0 Platform API key to authenticate and sync memories securely via cloud storage. Alternatively, self-hosted open-source backends are supported by specific plugins where configured. --- ### Key Concepts Understanding how memory operates within your coding environment helps you get the most out of your assistant: - **Auto-Recall:** Automatically fetches relevant memories based on your current prompt or workspace, injecting them into the agent's context window before it answers. - **Auto-Capture:** Automatically stores useful conversation context, decisions, and outcomes after each agent turn or when a session ends. - **Scoping:** Organizes memories by context level so your agent searches the right information. Scopes include project-specific knowledge (`project`), single session state (`session`), or workspace-wide preferences (`global`). - **Coding Categories:** Tags and organizes memories into specific buckets such as project knowledge, decisions and constraints, workflows, problems and fixes, and results. --- ### Connecting Your Coding Environment #### Setup and Configuration Follow the steps below to configure and use Mem0 within supported AI coding environments. #### 1. Obtain Your API Key 1. Go to the Mem0 Dashboard to create or retrieve your API key. 2. Copy your unique API key string (typically starting with `m0-` or `sk-`). #### 2. Configure Environment Variables Set your API key as an environment variable so your IDE plugin can automatically detect it. Supported environment variables for authentication keys: - `MEM0_API_KEY` - `OPENAI_API_KEY` - `ANTHROPIC_API_KEY` - `MEM0_HOST` - `MEM0_PLUGIN_API_KEY` #### 3. Install and Enable the Plugin Depending on your chosen coding environment, install the Mem0 integration package or extension from your editor's extension marketplace or plugin manager. Once installed, the plugin initializes automatically when you start a coding session or open a project workspace. --- ### Usage and Commands #### Memory Tools and Commands Once connected, coding agent plugins provide specialized tools and interactive commands to help you manage your long-term memories. #### Built-in Memory Tools Your coding agent has direct access to memory tools to search and store facts dynamically: - **Search:** Queries past repository decisions, bug fixes, and workflows using natural language. - **Add:** Saves durable facts, user preferences, or completed changes. - **Update / Delete:** Modifies or removes outdated memories by ID. #### Interactive CLI and Slash Commands Many plugins provide interactive commands that you can type directly into your coding assistant's terminal or chat interface: | Command | Description | | :--- | :--- | | `/mem0-remember ` | Store a memory verbatim without running inference. | | `/mem0-search ` | Perform a semantic search across your stored memories. | | `/mem0-forget ` | Find and delete memories matching a natural language query. | | `/mem0-tour [scope]` | Browse all memories grouped by category for a given scope. | --- ### Tips and Warnings > [!TIP] > **Be Proactive:** You do not need to wait to be asked to remember things. When you establish a project constraint, prefer a specific tool, or make a major architectural decision, tell your agent to save it. > [!WARNING] > **Extraction Timing:** When you store a fact using a tool, server-side extraction may run asynchronously. Do not immediately search for a freshly written fact expecting it to appear in milliseconds. > [!CAUTION] > **Credential Safety:** Never paste plaintext API keys directly into shared configuration files or public repositories. Use environment variables or designated secure storage references for all sensitive keys. ## Related - [Workflow Tool Connectors](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/integrations/workflow-tool-connectors) - [Command Line Setup](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/getting-started/command-line-setup) --- ## User guide: Workflow Tool Connectors URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/integrations/workflow-tool-connectors ## Overview ### Introduction to Workflow Connectors Workflow Tool Connectors allow you to easily integrate Mem0's long-term memory layer into popular multi-agent frameworks, automation tools, and developer environments. By connecting Mem0 to tools like LangChain, n8n, OpenClaw, and Pi-Agent, your AI agents gain the ability to recall user preferences, store durable facts across sessions, and maintain context over time without starting from scratch. > [!NOTE] > Depending on the framework you choose, you can use either the cloud-based Mem0 Platform or a self-hosted open-source backend. ### Supported Frameworks and Connectors Mem0 provides dedicated modules and plugins for various platforms and workflow engines. | Integration / Framework | Connector Type | Supported Capabilities | | :--- | :--- | :--- | | **LangChain** | Community Memory Class (`Mem0Memory`) | Chat history management, semantic memory search, and automatic context injection. | | **n8n** | Community Node (`@mem0/n8n-nodes-mem0`) | Add, search, get, update, and delete memories within automated workflows and AI Agent nodes. | | **OpenClaw** | Plugin (`openclaw-mem0`) | Auto-recall, auto-capture, multi-agent isolation, CLI controls, and platform/OSS dual mode. | | **Pi-Agent / Other Coding Agents** | Extension / Plugin | Tool registration (`mem0_memory`), project/session/global scoping, and telemetry tracking. | Sources: LangChain integration (`mem0-ts/src/community/src/integrations/langchain/mem0.ts`), n8n node description (`integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts`), OpenClaw plugin (`integrations/openclaw/index.ts`), Pi-Agent extension (`integrations/pi-agent-plugin/src/memory/tools.ts`). --- ## Connecting Mem0 to LangChain ### LangChain Memory Integration Overview The `Mem0Memory` class integrates Mem0 directly into LangChain chat models and conversation chains, managing memory storage and retrieval automatically. ### Configuration and Setup 1. Install the required Mem0 package in your TypeScript or JavaScript project. 2. Initialize the `Mem0Memory` class by providing your Mem0 API key and a session or user identifier. 3. Attach the memory instance to your LangChain conversation chain or chat model configuration. ```typescript import { Mem0Memory } from "@mem0/community"; import { ChatOpenAI } from "@langchain/openai"; import { ConversationChain } from "langchain/chains"; const memory = new Mem0Memory({ sessionId: "user123", apiKey: "your-api-key", memoryOptions: { user_id: "user123", run_id: "run123" }, }); const model = new ChatOpenAI({ modelName: "gpt-4o-mini", temperature: 0, }); const chain = new ConversationChain({ llm: model, memory }); ``` > [!TIP] > You can toggle `separateMessages` in `Mem0MemoryInput` if you prefer receiving chat history as a list of separate messages containing a system message, rather than a single condensed human message. --- ## Automating Workflows with n8n ### n8n Community Node Overview The Mem0 community node for n8n (`@mem0/n8n-nodes-mem0`) allows you to build automated workflows that interact with your long-term memory layer, making it fully compatible with n8n's AI Agent (Tools Agent) node. ### Setting Up the Mem0 Node 1. Add the **Mem0** node to your n8n workflow canvas. 2. Create new **Mem0 API** credentials by providing your API key and base URL (defaults to `https://api.mem0.ai`). 3. Choose the **Resource** (Memory) and the desired **Operation**: - **Add**: Extract and store memories from messages. - **Search**: Search stored facts by meaning and semantic relevance. - **Get All**: List or page through stored memories. - **Get / Update / Delete**: Manage specific memories by their unique Memory ID. > [!IMPORTANT] > When using the Add operation, ensure you configure at least one entity identifier (such as User ID, Agent ID, Run ID, or App ID) so the system knows who owns the stored memory. --- ## Integrating with OpenClaw ### OpenClaw Plugin Overview The OpenClaw Mem0 plugin brings persistent memory to OpenClaw agents, supporting both the Mem0 cloud platform and self-hosted open-source backends. ### Key Features and CLI Initialization - **Auto-Recall & Auto-Capture:** Automatically injects relevant memories before each agent turn and captures key conversation facts afterward. - **Per-Agent Isolation:** Multi-agent setups automatically read and write from separate user identifier namespaces using session routing. - **Dual Mode:** Configure for either cloud platform usage or self-hosted open-source deployments. You can initialize and log into the OpenClaw Mem0 plugin using the command-line interface: ```bash openclaw mem0 init --api-key ``` Supported authentication and provider configuration choices include: - **Mem0 Platform API Key:** Authenticated via the `MEM0_API_KEY` environment variable or `--mem0-api-key`. - **Open Source with OpenAI:** Self-hosted mode using OpenAI for language models and embeddings. - **OpenSource with Ollama:** Fully local mode with no external API keys required. Sources: OpenClaw plugin configuration (`integrations/openclaw/openclaw.plugin.json`). --- ## Managing Scopes and Entities ### Entity Scoping Overview When working across multi-agent frameworks, proper scoping ensures that memories are retrieved by the correct user, agent, or project. - **User ID (`user_id`):** Identifies the human user who owns the memory. Essential for cross-session recall. - **Agent ID (`agent_id`):** Partitions memories by specific AI agents in multi-agent environments. - **Run ID / Session ID (`run_id`):** Isolates memories to a specific workflow execution run or chat session. - **App ID (`app_id`):** Groups memories by project, repository, or application namespace. > [!WARNING] > Destructive operations like deleting all memories (`delete_all_memories`) will permanently erase data within the targeted scope. Always verify your scope parameters before executing wipe commands. ## Related - [Agent IDE Plugins](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/integrations/agent-ide-plugins) - [Building AI Companions](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/how-to-guides/building-ai-companions) --- ## User guide: Troubleshooting and FAQ URL: https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/troubleshooting/troubleshooting-and-faq ## Overview This guide helps you resolve common setup challenges, missing or invalid API keys, search mismatches, and upgrade or configuration issues when working with Mem0. Follow the troubleshooting steps and frequently asked questions below to get your memory integration running smoothly. > [!NOTE] > If you encounter unexpected behavior, running your built-in environment diagnostics or doctor check is the fastest way to identify missing credentials, incompatible package versions, or connection failures. --- ## Setup and Installation Issues ### Version Incompatibilities and Missing Dependencies If you encounter errors during startup or package initialization, your installed client version may not meet the minimum requirement. > [!IMPORTANT] > Ensure you are running `mem0ai>=2.0.10`. To update your Python package to the required version, run: > ```bash > uv pip install --python $(which python) "mem0ai>=2.0.10" > ``` ### Local Infrastructure Checks for Open Source Mode When running in open source mode with local vector stores or language models, connectivity issues are common if underlying services are not running or reachable. > [!TIP] > Use the configuration wizard or manually verify that your local service ports are active: > - **Ollama:** Ensure it is running at `http://localhost:11434` (run `ollama serve` if stopped). > - **Qdrant:** Ensure your vector database container is running and reachable at `http://localhost:6333`. > - **PGVector:** Verify that PostgreSQL with the `pgvector` extension is active on port `5432`. --- ## API Key Troubleshooting ### Missing or Unsynchronized Keys If your memory operations fail with authentication errors, your API key may be missing from your environment variables, configuration files, or ecosystem touchpoints. 1. **Verify your environment variable:** Ensure `MEM0_API_KEY` is exported in your shell or configuration file. 2. **Sync your key across tools:** Propagate your active API key into supported ecosystem configuration files and shell profiles (`~/.zshrc`, `~/.bashrc`, `~/.bash_profile`, or Claude settings) to prevent mismatch errors between your command-line setup and background plugins. | Error Type | Common Cause | Resolution | | :--- | :--- | :--- | | `401 Unauthorized` | Expired or malformed API key | Re-authenticate or update your `MEM0_API_KEY` variable. | | `API key missing` | Variable not exported in current shell | Set `MEM0_API_KEY` or run setup again. | | `Wildcard Scope Refusal` | Configured user ID evaluates to a wildcard | Provide a specific, non-wildcard user identifier. | --- ## Search and Filtering Mismatches ### Entity Scoping and Metadata Filters Mem0 requires entity parameters (such as `user_id`, `agent_id`, `app_id`, and `run_id`) to be passed inside structured filters rather than as top-level parameters in newer versions. > [!WARNING] > Passing scoping parameters at the top level during search or listing operations can result in empty result sets or search mismatches. Always nest scoping identifiers inside your query filters: > ```json > { > "filters": { > "user_id": "your-user-id", > "app_id": "your-repo-app-id" > } > } > ``` ### Hybrid and Keyword Search Fallbacks If hybrid or keyword searches fail or return unexpected results, check your database capabilities: - **Qdrant:** Server-side sparse vectors and BM25 hybrid search require Qdrant `>= 1.15.2` with inference enabled. If an incompatible server version rejects the payload, Mem0 automatically falls back to dense-only vector search to prevent data write failures. --- ## Frequently Asked Questions ### How do I check the health of my installation? You can execute diagnostic checks to inspect your Python version, data directory permissions, API key presence, and remote authentication status in real time. A successful check verifies that all connection parameters and environment variables are properly aligned. ### What should I do if my memory deletion or forget request fails? Ensure that your user and repository scopes are explicitly defined and are not wildcards. The system refuses wildcard deletion requests to prevent accidental data loss across shared environments. ### How do I switch between Platform and Open Source modes? You can re-run your setup configuration command with your preferred mode flag to re-configure your storage backends, API endpoints, and provider credentials. ## Related - [Quickstart Overview](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/getting-started/quickstart-overview) - [Command Line Setup](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/guide/getting-started/command-line-setup) ---