Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb224083e4 | ||
|
|
30469aec17 | ||
|
|
50db9e428d | ||
|
|
fb87349664 | ||
|
|
8827553576 | ||
|
|
c8e20a9bb5 | ||
|
|
93a51f4763 | ||
|
|
86fe275f53 | ||
|
|
e6d6276bb9 | ||
|
|
9692726db4 | ||
|
|
d8d776636f | ||
|
|
a5a688295e | ||
|
|
5d40592e42 | ||
|
|
a488e19044 | ||
|
|
57f944e18a | ||
|
|
fe3f7ae618 | ||
|
|
4a7e166f9a | ||
|
|
85768e78e7 | ||
|
|
7b395f3bf7 | ||
|
|
4180409b09 | ||
|
|
649e719ce6 | ||
|
|
1a53852d93 | ||
|
|
ac9cdd4840 | ||
|
|
cf530c4bec | ||
|
|
92b958c1cc | ||
|
|
c239d8a483 | ||
|
|
9d6b79a14e | ||
|
|
e44b46ef2e | ||
|
|
3882af7450 | ||
|
|
d39ebad09f |
@@ -266,7 +266,7 @@ config = MemoryConfig(
|
||||
graph_store=GraphStoreConfig(provider="neo4j", config={...}), # optional
|
||||
history_db_path="~/.mem0/history.db",
|
||||
version="v1.1",
|
||||
custom_fact_extraction_prompt="Custom prompt...",
|
||||
custom_instructions="Custom prompt...",
|
||||
custom_update_memory_prompt="Custom prompt..."
|
||||
)
|
||||
```
|
||||
@@ -684,7 +684,7 @@ Conversation: {messages}
|
||||
"""
|
||||
|
||||
config = MemoryConfig(
|
||||
custom_fact_extraction_prompt=custom_extraction_prompt
|
||||
custom_instructions=custom_extraction_prompt
|
||||
)
|
||||
memory = Memory(config)
|
||||
```
|
||||
|
||||
@@ -41,16 +41,30 @@
|
||||
<p align="center">
|
||||
<a href="https://mem0.ai/research"><strong>📄 Building Production-Ready AI Agents with Scalable Long-Term Memory →</strong></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<strong>⚡ +26% Accuracy vs. OpenAI Memory • 🚀 91% Faster • 💰 90% Fewer Tokens</strong>
|
||||
</p>
|
||||
|
||||
> **🎉 mem0ai v1.0.0 is now available!** This major release includes API modernization, improved vector store support, and enhanced GCP integration. [See migration guide →](MIGRATION_GUIDE_v1.0.md)
|
||||
## New Memory Algorithm (April 2026)
|
||||
|
||||
## 🔥 Research Highlights
|
||||
- **+26% Accuracy** over OpenAI Memory on the LOCOMO benchmark
|
||||
- **91% Faster Responses** than full-context, ensuring low-latency at scale
|
||||
- **90% Lower Token Usage** than full-context, cutting costs without compromise
|
||||
| Benchmark | Old | New | Tokens | Latency p50 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **LoCoMo** | 71.4 | **91.6** | 7.0K | 0.88s |
|
||||
| **LongMemEval** | 67.8 | **93.4** | 6.8K | 1.09s |
|
||||
| **BEAM (1M)** | — | **64.1** | 6.7K | 1.00s |
|
||||
| **BEAM (10M)** | — | **48.6** | 6.9K | 1.05s |
|
||||
|
||||
All benchmarks run on the same production-representative model stack. Single-pass retrieval (one call, no agentic loops).
|
||||
|
||||
**What changed:**
|
||||
- **Single-pass ADD-only extraction** -- one LLM call, no UPDATE/DELETE. Memories accumulate; nothing is overwritten.
|
||||
- **Agent-generated facts are first-class** -- when an agent confirms an action, that information is now stored with equal weight.
|
||||
- **Entity linking** -- entities are extracted, embedded, and linked across memories for retrieval boosting.
|
||||
- **Multi-signal retrieval** -- semantic, BM25 keyword, and entity matching scored in parallel and fused.
|
||||
|
||||
See the [migration guide](https://docs.mem0.ai/migration/oss-v2-to-v3) for upgrade instructions. The [evaluation framework](https://github.com/mem0ai/memory-benchmarks) is open-sourced so anyone can reproduce the numbers.
|
||||
|
||||
## Research Highlights
|
||||
- **91.6 on LoCoMo** -- +20 points over the previous algorithm
|
||||
- **93.4 on LongMemEval** -- +26 points, with +53.6 on assistant memory recall
|
||||
- **64.1 on BEAM (1M)** -- production-scale memory evaluation at 1M tokens
|
||||
- [Read the full paper](https://mem0.ai/research)
|
||||
|
||||
# Introduction
|
||||
@@ -88,6 +102,13 @@ Install the sdk via pip:
|
||||
pip install mem0ai
|
||||
```
|
||||
|
||||
For enhanced hybrid search with BM25 keyword matching and entity extraction, install with NLP support:
|
||||
|
||||
```bash
|
||||
pip install mem0ai[nlp]
|
||||
python -m spacy download en_core_web_sm
|
||||
```
|
||||
|
||||
Install sdk via npm:
|
||||
```bash
|
||||
npm install mem0ai
|
||||
@@ -109,7 +130,9 @@ See the [CLI documentation](https://docs.mem0.ai/platform/cli) for the full comm
|
||||
|
||||
### Basic Usage
|
||||
|
||||
Mem0 requires an LLM to function, with `gpt-4.1-nano-2025-04-14 from OpenAI as the default. However, it supports a variety of LLMs; for details, refer to our [Supported LLMs documentation](https://docs.mem0.ai/components/llms/overview).
|
||||
Mem0 requires an LLM to function, with `gpt-5-mini` from OpenAI as the default. However, it supports a variety of LLMs; for details, refer to our [Supported LLMs documentation](https://docs.mem0.ai/components/llms/overview).
|
||||
|
||||
Mem0 uses `text-embedding-3-small` from OpenAI as the default embedding model. For best results with hybrid search (semantic + keyword + entity boosting), we recommend using at least [Qwen 600M](https://huggingface.co/Alibaba-NLP/gte-Qwen2-1.5B-instruct) or a comparable embedding model. See [Supported Embeddings](https://docs.mem0.ai/components/embedders/overview) for configuration details.
|
||||
|
||||
First step is to instantiate the memory:
|
||||
|
||||
@@ -122,13 +145,13 @@ memory = Memory()
|
||||
|
||||
def chat_with_memories(message: str, user_id: str = "default_user") -> str:
|
||||
# Retrieve relevant memories
|
||||
relevant_memories = memory.search(query=message, user_id=user_id, limit=3)
|
||||
relevant_memories = memory.search(query=message, filters={"user_id": user_id}, top_k=3)
|
||||
memories_str = "\n".join(f"- {entry['memory']}" for entry in relevant_memories["results"])
|
||||
|
||||
# Generate Assistant response
|
||||
system_prompt = f"You are a helpful AI. Answer the question based on query and memories.\nUser Memories:\n{memories_str}"
|
||||
messages = [{"role": "system", "content": system_prompt}, {"role": "user", "content": message}]
|
||||
response = openai_client.chat.completions.create(model="gpt-4.1-nano-2025-04-14", messages=messages)
|
||||
response = openai_client.chat.completions.create(model="gpt-5-mini", messages=messages)
|
||||
assistant_response = response.choices[0].message.content
|
||||
|
||||
# Create new memories from the conversation
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mem0/cli",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"description": "The official CLI for mem0 — the memory layer for AI agents",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
||||
@@ -116,6 +116,7 @@ export class PlatformBackend implements Backend {
|
||||
if (opts.expires) payload.expiration_date = opts.expires;
|
||||
if (opts.categories) payload.categories = opts.categories;
|
||||
if (opts.enableGraph) payload.enable_graph = true;
|
||||
payload.source = "CLI";
|
||||
|
||||
return (await this._request("POST", "/v1/memories/", {
|
||||
json: payload,
|
||||
@@ -176,6 +177,7 @@ export class PlatformBackend implements Backend {
|
||||
if (opts.keyword) payload.keyword_search = true;
|
||||
if (opts.fields) payload.fields = opts.fields;
|
||||
if (opts.enableGraph) payload.enable_graph = true;
|
||||
payload.source = "CLI";
|
||||
|
||||
const result = (await this._request("POST", "/v2/memories/search/", {
|
||||
json: payload,
|
||||
@@ -186,10 +188,9 @@ export class PlatformBackend implements Backend {
|
||||
}
|
||||
|
||||
async get(memoryId: string): Promise<Record<string, unknown>> {
|
||||
return (await this._request("GET", `/v1/memories/${memoryId}/`)) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
return (await this._request("GET", `/v1/memories/${memoryId}/`, {
|
||||
params: { source: "CLI" },
|
||||
})) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
async listMemories(
|
||||
@@ -227,6 +228,7 @@ export class PlatformBackend implements Backend {
|
||||
});
|
||||
if (apiFilters) payload.filters = apiFilters;
|
||||
if (opts.enableGraph) payload.enable_graph = true;
|
||||
payload.source = "CLI";
|
||||
|
||||
const result = (await this._request("POST", "/v2/memories/", {
|
||||
json: payload,
|
||||
@@ -245,6 +247,7 @@ export class PlatformBackend implements Backend {
|
||||
const payload: Record<string, unknown> = {};
|
||||
if (content) payload.text = content;
|
||||
if (metadata) payload.metadata = metadata;
|
||||
payload.source = "CLI";
|
||||
return (await this._request("PUT", `/v1/memories/${memoryId}/`, {
|
||||
json: payload,
|
||||
})) as Record<string, unknown>;
|
||||
@@ -255,7 +258,7 @@ export class PlatformBackend implements Backend {
|
||||
opts: DeleteOptions = {},
|
||||
): Promise<Record<string, unknown>> {
|
||||
if (opts.all) {
|
||||
const params: Record<string, string> = {};
|
||||
const params: Record<string, string> = { source: "CLI" };
|
||||
if (opts.userId) params.user_id = opts.userId;
|
||||
if (opts.agentId) params.agent_id = opts.agentId;
|
||||
if (opts.appId) params.app_id = opts.appId;
|
||||
@@ -265,10 +268,9 @@ export class PlatformBackend implements Backend {
|
||||
})) as Record<string, unknown>;
|
||||
}
|
||||
if (memoryId) {
|
||||
return (await this._request(
|
||||
"DELETE",
|
||||
`/v1/memories/${memoryId}/`,
|
||||
)) as Record<string, unknown>;
|
||||
return (await this._request("DELETE", `/v1/memories/${memoryId}/`, {
|
||||
params: { source: "CLI" },
|
||||
})) as Record<string, unknown>;
|
||||
}
|
||||
throw new Error("Either memoryId or --all is required");
|
||||
}
|
||||
@@ -291,6 +293,7 @@ export class PlatformBackend implements Backend {
|
||||
result = (await this._request(
|
||||
"DELETE",
|
||||
`/v2/entities/${entityType}/${entityId}/`,
|
||||
{ params: { source: "CLI" } },
|
||||
)) as Record<string, unknown>;
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -31,10 +31,15 @@ export interface DefaultsConfig {
|
||||
enableGraph: boolean;
|
||||
}
|
||||
|
||||
export interface TelemetryConfig {
|
||||
anonymousId: string;
|
||||
}
|
||||
|
||||
export interface Mem0Config {
|
||||
version: number;
|
||||
defaults: DefaultsConfig;
|
||||
platform: PlatformConfig;
|
||||
telemetry: TelemetryConfig;
|
||||
}
|
||||
|
||||
export function createDefaultConfig(): Mem0Config {
|
||||
@@ -52,6 +57,9 @@ export function createDefaultConfig(): Mem0Config {
|
||||
baseUrl: DEFAULT_BASE_URL,
|
||||
userEmail: "",
|
||||
},
|
||||
telemetry: {
|
||||
anonymousId: "",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -80,6 +88,9 @@ export function loadConfig(): Mem0Config {
|
||||
config.defaults.appId = defaults.app_id ?? "";
|
||||
config.defaults.runId = defaults.run_id ?? "";
|
||||
config.defaults.enableGraph = defaults.enable_graph ?? false;
|
||||
|
||||
const telemetry = data.telemetry ?? {};
|
||||
config.telemetry.anonymousId = telemetry.anonymous_id ?? "";
|
||||
}
|
||||
|
||||
// Environment variable overrides
|
||||
@@ -119,6 +130,9 @@ export function saveConfig(config: Mem0Config): void {
|
||||
base_url: config.platform.baseUrl,
|
||||
user_email: config.platform.userEmail,
|
||||
},
|
||||
telemetry: {
|
||||
anonymous_id: config.telemetry.anonymousId,
|
||||
},
|
||||
};
|
||||
|
||||
fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2));
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
*/
|
||||
|
||||
import { spawn } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { CONFIG_FILE, loadConfig } from "./config.js";
|
||||
import { CONFIG_FILE, loadConfig, saveConfig } from "./config.js";
|
||||
import { CLI_VERSION } from "./version.js";
|
||||
|
||||
const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX";
|
||||
@@ -29,11 +29,34 @@ function isTelemetryEnabled(): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a persistent per-machine anonymous ID, generating one if needed.
|
||||
*
|
||||
* Stored in ~/.mem0/config.json under `telemetry.anonymous_id` so that
|
||||
* repeat runs on the same machine share one PostHog identity instead of
|
||||
* collapsing into a single shared fallback string.
|
||||
*/
|
||||
function getOrCreateAnonymousId(): string {
|
||||
const config = loadConfig();
|
||||
if (config.telemetry.anonymousId) {
|
||||
return config.telemetry.anonymousId;
|
||||
}
|
||||
|
||||
const newId = `cli-anon-${randomUUID().replace(/-/g, "")}`;
|
||||
config.telemetry.anonymousId = newId;
|
||||
try {
|
||||
saveConfig(config);
|
||||
} catch {
|
||||
/* ignore persistence failure — still return the generated ID */
|
||||
}
|
||||
return newId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a stable anonymous identifier for the current user.
|
||||
*
|
||||
* Priority: cached user_email (from /v1/ping/) > MD5(api_key) > fallback.
|
||||
* Matches the SDK pattern in mem0-ts/src/client/mem0.ts.
|
||||
* Priority: cached user_email (from /v1/ping/) > MD5(api_key) >
|
||||
* persistent per-machine anonymous ID.
|
||||
*/
|
||||
function getDistinctId(): string {
|
||||
try {
|
||||
@@ -47,7 +70,11 @@ function getDistinctId(): string {
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
return "anonymous-cli";
|
||||
try {
|
||||
return getOrCreateAnonymousId();
|
||||
} catch {
|
||||
return `cli-anon-${randomUUID().replace(/-/g, "")}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,6 +96,25 @@ export function captureEvent(
|
||||
const config = loadConfig();
|
||||
const distinctId = preResolvedEmail || getDistinctId();
|
||||
|
||||
// Detect anonymous → identified transition. If a stored anonymous_id
|
||||
// exists and we just resolved to a real identity, fire a one-shot
|
||||
// $identify event so PostHog stitches the pre-signup history onto
|
||||
// the authenticated profile. Clear the stored id so we don't re-alias.
|
||||
let anonIdToAlias: string | null = null;
|
||||
if (
|
||||
distinctId &&
|
||||
!distinctId.startsWith("cli-anon-") &&
|
||||
config.telemetry.anonymousId
|
||||
) {
|
||||
anonIdToAlias = config.telemetry.anonymousId;
|
||||
config.telemetry.anonymousId = "";
|
||||
try {
|
||||
saveConfig(config);
|
||||
} catch {
|
||||
/* ignore — alias may double-fire next run, harmless */
|
||||
}
|
||||
}
|
||||
|
||||
const payload = {
|
||||
api_key: POSTHOG_API_KEY,
|
||||
distinct_id: distinctId,
|
||||
@@ -92,6 +138,7 @@ export function captureEvent(
|
||||
mem0ApiKey: config.platform.apiKey || "",
|
||||
mem0BaseUrl: config.platform.baseUrl || "https://api.mem0.ai",
|
||||
configPath: CONFIG_FILE,
|
||||
anonDistinctIdToAlias: anonIdToAlias,
|
||||
};
|
||||
|
||||
const child = spawn(
|
||||
|
||||
@@ -94,6 +94,19 @@ async function sendPosthogEvent(posthogHost, payload) {
|
||||
}
|
||||
}
|
||||
|
||||
async function sendIdentifyEvent(ctx, payload, anonId) {
|
||||
const identifyPayload = {
|
||||
api_key: payload.api_key,
|
||||
event: "$identify",
|
||||
distinct_id: payload.distinct_id,
|
||||
properties: {
|
||||
$anon_distinct_id: anonId,
|
||||
$lib: (payload.properties && payload.properties.$lib) || "posthog-node",
|
||||
},
|
||||
};
|
||||
await sendPosthogEvent(ctx.posthogHost, identifyPayload);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const ctx = JSON.parse(process.argv[2]);
|
||||
const payload = ctx.payload;
|
||||
@@ -102,6 +115,14 @@ async function main() {
|
||||
await resolveAndCacheEmail(ctx, payload);
|
||||
}
|
||||
|
||||
// Fire $identify *after* email resolution so PostHog links the stored
|
||||
// anonymous id directly to the final identity (email, not the api-key
|
||||
// hash). The regular event is sent next so it lands under the merged
|
||||
// profile.
|
||||
if (ctx.anonDistinctIdToAlias) {
|
||||
await sendIdentifyEvent(ctx, payload, ctx.anonDistinctIdToAlias);
|
||||
}
|
||||
|
||||
await sendPosthogEvent(ctx.posthogHost, payload);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "mem0-cli"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
description = "The official CLI for mem0 — the memory layer for AI agents"
|
||||
readme = "README.md"
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""mem0 CLI — the command-line interface for the mem0 memory layer."""
|
||||
|
||||
__version__ = "0.2.2"
|
||||
__version__ = "0.2.3"
|
||||
|
||||
@@ -93,6 +93,7 @@ class PlatformBackend(Backend):
|
||||
payload["categories"] = categories
|
||||
if enable_graph:
|
||||
payload["enable_graph"] = True
|
||||
payload["source"] = "CLI"
|
||||
|
||||
return self._request("POST", "/v1/memories/", json=payload)
|
||||
|
||||
@@ -172,6 +173,7 @@ class PlatformBackend(Backend):
|
||||
payload["fields"] = fields
|
||||
if enable_graph:
|
||||
payload["enable_graph"] = True
|
||||
payload["source"] = "CLI"
|
||||
|
||||
result = self._request("POST", "/v2/memories/search/", json=payload)
|
||||
return (
|
||||
@@ -181,7 +183,7 @@ class PlatformBackend(Backend):
|
||||
)
|
||||
|
||||
def get(self, memory_id: str) -> dict:
|
||||
return self._request("GET", f"/v1/memories/{memory_id}/")
|
||||
return self._request("GET", f"/v1/memories/{memory_id}/", params={"source": "CLI"})
|
||||
|
||||
def list_memories(
|
||||
self,
|
||||
@@ -220,6 +222,7 @@ class PlatformBackend(Backend):
|
||||
payload["filters"] = api_filters
|
||||
if enable_graph:
|
||||
payload["enable_graph"] = True
|
||||
payload["source"] = "CLI"
|
||||
|
||||
result = self._request("POST", "/v2/memories/", json=payload, params=params)
|
||||
return (
|
||||
@@ -236,6 +239,7 @@ class PlatformBackend(Backend):
|
||||
payload["text"] = content
|
||||
if metadata:
|
||||
payload["metadata"] = metadata
|
||||
payload["source"] = "CLI"
|
||||
return self._request("PUT", f"/v1/memories/{memory_id}/", json=payload)
|
||||
|
||||
def delete(
|
||||
@@ -249,7 +253,7 @@ class PlatformBackend(Backend):
|
||||
run_id: str | None = None,
|
||||
) -> dict:
|
||||
if all:
|
||||
params: dict[str, str] = {}
|
||||
params: dict[str, str] = {"source": "CLI"}
|
||||
if user_id:
|
||||
params["user_id"] = user_id
|
||||
if agent_id:
|
||||
@@ -260,7 +264,7 @@ class PlatformBackend(Backend):
|
||||
params["run_id"] = run_id
|
||||
return self._request("DELETE", "/v1/memories/", params=params)
|
||||
elif memory_id:
|
||||
return self._request("DELETE", f"/v1/memories/{memory_id}/")
|
||||
return self._request("DELETE", f"/v1/memories/{memory_id}/", params={"source": "CLI"})
|
||||
else:
|
||||
raise ValueError("Either memory_id or --all is required")
|
||||
|
||||
@@ -285,7 +289,9 @@ class PlatformBackend(Backend):
|
||||
# Delete each provided entity via the v2 path-based endpoint
|
||||
result: dict = {}
|
||||
for entity_type, entity_id in entities.items():
|
||||
result = self._request("DELETE", f"/v2/entities/{entity_type}/{entity_id}/")
|
||||
result = self._request(
|
||||
"DELETE", f"/v2/entities/{entity_type}/{entity_id}/", params={"source": "CLI"}
|
||||
)
|
||||
return result
|
||||
|
||||
def ping(self, timeout: float | None = None) -> dict:
|
||||
|
||||
@@ -39,11 +39,17 @@ class DefaultsConfig:
|
||||
enable_graph: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class TelemetryConfig:
|
||||
anonymous_id: 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)
|
||||
|
||||
|
||||
SHORT_KEY_ALIASES: dict[str, str] = {
|
||||
@@ -87,6 +93,9 @@ def load_config() -> Mem0Config:
|
||||
config.defaults.run_id = defaults.get("run_id", "")
|
||||
config.defaults.enable_graph = defaults.get("enable_graph", False)
|
||||
|
||||
telemetry = data.get("telemetry", {})
|
||||
config.telemetry.anonymous_id = telemetry.get("anonymous_id", "")
|
||||
|
||||
# Environment variable overrides
|
||||
env_key = os.environ.get("MEM0_API_KEY")
|
||||
if env_key:
|
||||
@@ -137,6 +146,9 @@ def save_config(config: Mem0Config) -> None:
|
||||
"base_url": config.platform.base_url,
|
||||
"user_email": config.platform.user_email,
|
||||
},
|
||||
"telemetry": {
|
||||
"anonymous_id": config.telemetry.anonymous_id,
|
||||
},
|
||||
}
|
||||
|
||||
with open(CONFIG_FILE, "w") as f:
|
||||
|
||||
@@ -9,12 +9,14 @@ Disable with: MEM0_TELEMETRY=false
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX"
|
||||
@@ -26,11 +28,31 @@ def _is_telemetry_enabled() -> bool:
|
||||
return val not in ("false", "0", "no")
|
||||
|
||||
|
||||
def _get_or_create_anonymous_id() -> str:
|
||||
"""Return a persistent per-machine anonymous ID, generating one if needed.
|
||||
|
||||
Stored in ~/.mem0/config.json under `telemetry.anonymous_id` so that
|
||||
repeat runs on the same machine share one PostHog identity instead of
|
||||
collapsing into a single shared fallback string.
|
||||
"""
|
||||
from mem0_cli.config import load_config, save_config
|
||||
|
||||
config = load_config()
|
||||
if config.telemetry.anonymous_id:
|
||||
return config.telemetry.anonymous_id
|
||||
|
||||
new_id = f"cli-anon-{uuid.uuid4().hex}"
|
||||
config.telemetry.anonymous_id = new_id
|
||||
with contextlib.suppress(Exception):
|
||||
save_config(config)
|
||||
return new_id
|
||||
|
||||
|
||||
def _get_distinct_id() -> str:
|
||||
"""Return a stable anonymous identifier for the current user.
|
||||
|
||||
Priority: cached user_email (from /v1/ping/) > MD5(api_key) > fallback.
|
||||
Matches the SDK pattern in mem0/client/main.py.
|
||||
Priority: cached user_email (from /v1/ping/) > MD5(api_key) >
|
||||
persistent per-machine anonymous ID.
|
||||
"""
|
||||
try:
|
||||
from mem0_cli.config import load_config
|
||||
@@ -42,7 +64,10 @@ def _get_distinct_id() -> str:
|
||||
return hashlib.md5(config.platform.api_key.encode()).hexdigest()
|
||||
except Exception:
|
||||
pass
|
||||
return "anonymous-cli"
|
||||
try:
|
||||
return _get_or_create_anonymous_id()
|
||||
except Exception:
|
||||
return f"cli-anon-{uuid.uuid4().hex}"
|
||||
|
||||
|
||||
def capture_event(
|
||||
@@ -61,12 +86,27 @@ def capture_event(
|
||||
|
||||
try:
|
||||
from mem0_cli import __version__
|
||||
from mem0_cli.config import CONFIG_FILE, load_config
|
||||
from mem0_cli.config import CONFIG_FILE, load_config, save_config
|
||||
from mem0_cli.state import is_agent_mode
|
||||
|
||||
config = load_config()
|
||||
distinct_id = pre_resolved_email or _get_distinct_id()
|
||||
|
||||
# Detect anonymous → identified transition. If a stored anonymous_id
|
||||
# exists and we just resolved to a real identity, fire a one-shot
|
||||
# $identify event so PostHog stitches the pre-signup history onto
|
||||
# the authenticated profile. Clear the stored id so we don't re-alias.
|
||||
anon_id_to_alias: str | None = None
|
||||
if (
|
||||
distinct_id
|
||||
and not distinct_id.startswith("cli-anon-")
|
||||
and config.telemetry.anonymous_id
|
||||
):
|
||||
anon_id_to_alias = config.telemetry.anonymous_id
|
||||
config.telemetry.anonymous_id = ""
|
||||
with contextlib.suppress(Exception):
|
||||
save_config(config)
|
||||
|
||||
payload = {
|
||||
"api_key": POSTHOG_API_KEY,
|
||||
"distinct_id": distinct_id,
|
||||
@@ -92,6 +132,7 @@ def capture_event(
|
||||
"mem0_api_key": config.platform.api_key or "",
|
||||
"mem0_base_url": config.platform.base_url or "https://api.mem0.ai",
|
||||
"config_path": str(CONFIG_FILE),
|
||||
"anon_distinct_id_to_alias": anon_id_to_alias,
|
||||
}
|
||||
|
||||
subprocess.Popen(
|
||||
|
||||
@@ -27,9 +27,31 @@ def main() -> None:
|
||||
if ctx.get("needs_email") and ctx.get("mem0_api_key"):
|
||||
_resolve_and_cache_email(ctx, payload)
|
||||
|
||||
# Fire $identify *after* email resolution so PostHog links the stored
|
||||
# anonymous id directly to the final identity (email, not the api-key
|
||||
# hash). The regular event is sent next so it lands under the merged
|
||||
# profile.
|
||||
anon_id = ctx.get("anon_distinct_id_to_alias")
|
||||
if anon_id:
|
||||
_send_identify_event(ctx, payload, anon_id)
|
||||
|
||||
_send_posthog_event(ctx["posthog_host"], payload)
|
||||
|
||||
|
||||
def _send_identify_event(ctx: dict, payload: dict, anon_id: str) -> None:
|
||||
"""Send a PostHog $identify event aliasing anon_id → payload['distinct_id']."""
|
||||
identify_payload = {
|
||||
"api_key": payload["api_key"],
|
||||
"event": "$identify",
|
||||
"distinct_id": payload["distinct_id"],
|
||||
"properties": {
|
||||
"$anon_distinct_id": anon_id,
|
||||
"$lib": payload.get("properties", {}).get("$lib", "posthog-python"),
|
||||
},
|
||||
}
|
||||
_send_posthog_event(ctx["posthog_host"], identify_payload)
|
||||
|
||||
|
||||
def _resolve_and_cache_email(ctx: dict, payload: dict) -> None:
|
||||
"""Call /v1/ping/ to get the user's email, update the payload, and cache it."""
|
||||
try:
|
||||
|
||||
@@ -47,8 +47,6 @@ Provide at least one message or direct memory string. Most callers supply `messa
|
||||
| `messages` | array | No* | Conversation turns for Mem0 to infer memories from. Each object should include `role` and `content`. |
|
||||
| `metadata` | object | Optional | Custom key/value metadata (e.g., `{"topic": "preferences"}`). |
|
||||
| `infer` | boolean (default `true`) | Optional | Set to `false` to skip inference and store the provided text as-is. |
|
||||
| `async_mode` | boolean (default `true`) | Optional | Controls asynchronous processing. Most clients leave this enabled. |
|
||||
| `output_format` | string (default `v1.1`) | Optional | Response format. `v1.1` wraps results in a `results` array. |
|
||||
|
||||
> \* Provide at least one `messages` entry to describe what you are storing. For scoped memories, include `user_id`. You can also attach `agent_id`, `app_id`, `run_id`, `project_id`, or `org_id` to refine ownership.
|
||||
|
||||
@@ -83,20 +81,3 @@ Successful requests return an array of events queued for processing. Each event
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Graph relationships
|
||||
|
||||
Add Memories can enrich the knowledge graph on write. Set `enable_graph: true` to create entity nodes and relationships for the stored memory. Use this when you want downstream `get_all` or search calls to traverse connected entities.
|
||||
|
||||
<CodeGroup>
|
||||
```json Graph-aware request
|
||||
{
|
||||
"user_id": "alice",
|
||||
"messages": [
|
||||
{ "role": "user", "content": "I met with Dr. Lee at General Hospital." }
|
||||
],
|
||||
"enable_graph": true
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
The response follows the same format, and related entities become available in [Graph Memory](/platform/features/graph-memory) queries.
|
||||
|
||||
@@ -62,8 +62,7 @@ To retrieve graph memory relationships between entities, pass `output_format="v1
|
||||
memories = client.get_all(
|
||||
filters={
|
||||
"user_id": "alex"
|
||||
},
|
||||
output_format="v1.1"
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ Example with the mem0 Python package:
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
client = MemoryClient(org_id='YOUR_ORG_ID', project_id='YOUR_PROJECT_ID')
|
||||
client = MemoryClient(api_key="your-api-key")
|
||||
```
|
||||
|
||||
</Tab>
|
||||
@@ -41,10 +41,7 @@ client = MemoryClient(org_id='YOUR_ORG_ID', project_id='YOUR_PROJECT_ID')
|
||||
|
||||
```javascript
|
||||
import { MemoryClient } from "mem0ai";
|
||||
const client = new MemoryClient({
|
||||
organizationId: "YOUR_ORG_ID",
|
||||
projectId: "YOUR_PROJECT_ID"
|
||||
});
|
||||
const client = new MemoryClient({ apiKey: "your-api-key" });
|
||||
```
|
||||
|
||||
</Tab>
|
||||
@@ -98,9 +95,6 @@ client.project.update(
|
||||
custom_instructions="..."
|
||||
)
|
||||
|
||||
# Enable graph memory for the project
|
||||
client.project.update(enable_graph=True)
|
||||
|
||||
# Use the input language for memory storage and retrieval
|
||||
client.project.update(multilingual=True)
|
||||
|
||||
@@ -111,7 +105,6 @@ client.project.update(
|
||||
{"personal_info": "User personal information and preferences"},
|
||||
{"work_context": "Professional context and work-related information"}
|
||||
],
|
||||
enable_graph=True,
|
||||
multilingual=True
|
||||
)
|
||||
```
|
||||
@@ -172,11 +165,11 @@ All project methods are available in async mode:
|
||||
from mem0 import AsyncMemoryClient
|
||||
|
||||
async def manage_project():
|
||||
client = AsyncMemoryClient(org_id='YOUR_ORG_ID', project_id='YOUR_PROJECT_ID')
|
||||
client = AsyncMemoryClient(api_key="your-api-key")
|
||||
|
||||
# All methods support async/await
|
||||
project_info = await client.project.get()
|
||||
await client.project.update(enable_graph=True)
|
||||
await client.project.update(multilingual=True)
|
||||
members = await client.project.get_members()
|
||||
|
||||
# To call the async function properly
|
||||
|
||||
@@ -4,6 +4,26 @@ description: "Major product launches, headline features, and milestones for Mem0
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
<Update label="2026-04-14" description="Mem0 SDK v2.0.0 / v3.0.0">
|
||||
|
||||
**New Memory Algorithm — State-of-the-Art Accuracy at 90% Lower Cost**
|
||||
|
||||
Ground-up rewrite of the memory pipeline with 20+ point benchmark improvements:
|
||||
|
||||
- **LoCoMo:** 71.4 → **91.6** (+20) — multi-turn conversation recall
|
||||
- **LongMemEval:** 67.8 → **93.4** (+26) — long-term memory across sessions
|
||||
- **BEAM (1M tokens):** **64.1** — production-scale memory evaluation
|
||||
- **Agent memories are first-class** — Previous algorithm: 46% on assistant recall. New: **100%**
|
||||
- **Temporal reasoning works** — "Where did I live before SF?" Previous: 51%. New: **93%**
|
||||
- **90% fewer tokens** — Under 7K tokens per retrieval vs 25K+ for full-context approaches
|
||||
- **ADD-only extraction** — Memories accumulate; nothing is overwritten or deleted
|
||||
- **Hybrid retrieval** — Semantic + BM25 keyword + entity boost, scored in parallel
|
||||
- **Entity linking** — Entities extracted, embedded, and linked across memories
|
||||
|
||||
Breaking changes: Graph memory removed from OSS, `search()` defaults changed, deprecated params removed. See [migration guide](/migration/oss-v2-to-v3).
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-04-06" description="Mem0 Skill Graph">
|
||||
|
||||
**Mem0 Skill Graph — In-Context Documentation for AI Agents**
|
||||
@@ -31,7 +51,7 @@ A full-featured command-line interface for Mem0, available in both Python and No
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-04-04" description="OpenClaw v1.0.4">
|
||||
<Update label="2026-04-06" description="OpenClaw v1.0.4">
|
||||
|
||||
**OpenClaw Plugin — Production-Ready**
|
||||
|
||||
|
||||
@@ -4,6 +4,20 @@ description: "Release notes for the OpenClaw plugin and agent harness."
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
<Update label="2026-04-11" description="v1.0.6">
|
||||
|
||||
**Bug Fixes:**
|
||||
- **Telemetry:** Replaced shared `"anonymous-openclaw"` fallback with a persistent per-machine random hash (`openclaw-anon-<uuid>`), so anonymous plugin users are counted individually in PostHog ([#4790](https://github.com/mem0ai/mem0/pull/4790))
|
||||
- **Telemetry:** Added PostHog `$identify` event on first authenticated run to stitch anonymous history onto the authenticated profile ([#4790](https://github.com/mem0ai/mem0/pull/4790))
|
||||
- **Telemetry:** Fixed event loss on short-lived CLI invocations — added `beforeExit` handler to flush queued events before the process exits ([#4790](https://github.com/mem0ai/mem0/pull/4790))
|
||||
- **Telemetry:** Added lazy `/v1/ping/` email resolution so users who configure API key outside `mem0 init` show as their email in PostHog, not an md5 hash ([#4790](https://github.com/mem0ai/mem0/pull/4790))
|
||||
- **Telemetry:** Unified CLI event prefix from `openclaw.<cmd>` to `openclaw.cli.<cmd>` on the needsSetup branch to match the authenticated branch ([#4790](https://github.com/mem0ai/mem0/pull/4790))
|
||||
|
||||
**Improvements:**
|
||||
- **API:** Added `source: "OPENCLAW"` to all provider calls (`add`, `search`, `getAll`) across tools, CLI commands, recall, and the OSS backend adapter ([#4790](https://github.com/mem0ai/mem0/pull/4790))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-04-07" description="v1.0.5">
|
||||
|
||||
**Bug Fixes:**
|
||||
|
||||
+115
-2
@@ -7,7 +7,57 @@ mode: "wide"
|
||||
<Tabs>
|
||||
<Tab title="Python">
|
||||
|
||||
<Update label="2026-04-04" description="v1.0.11">
|
||||
<Update label="2026-04-14" description="v2.0.0">
|
||||
|
||||
**Major Release** — Python SDK with V3 memory pipeline, ADD-only extraction, and cleaned-up API surface.
|
||||
|
||||
**New Features:**
|
||||
- **Single-Pass Extraction:** Replaced 2-LLM-call pipeline with additive extraction using `ADDITIVE_EXTRACTION_PROMPT`. Memories accumulate via `linked_memory_ids` — no more UPDATE/DELETE events ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **Hybrid Search:** Combined semantic + BM25 keyword matching + entity boost with additive scoring. Native `keyword_search()` added to 15 vector store adapters (Qdrant, Elasticsearch, OpenSearch, Azure AI Search, Weaviate, Redis, PGVector, Pinecone, Databricks, MongoDB, Milvus, Baidu, Upstash, Azure MySQL, Vertex AI) ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **Entity Extraction & Linking:** spaCy-based entity extraction with second vector collection (`{collection}_entities`) for cross-memory relationship retrieval. Optional dependency: `pip install mem0ai[nlp]` ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **Batch Operations:** Batch embedding, batch persist, and batch entity linking (8-phase pipeline) for both sync `Memory` and async `AsyncMemory` at full parity ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **Message Persistence:** SQLite-based rolling window (10 messages per session scope) for LLM context ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **Valkey Cluster Mode:** Added `cluster_mode` parameter for Valkey Cluster Mode Enabled (CME) deployments ([#4759](https://github.com/mem0ai/mem0/pull/4759))
|
||||
- **V3 API Endpoints:** `MemoryClient.add()` now posts to `/v3/memories/add/`; `MemoryClient.get_all()` posts to `/v3/memories/` and returns a paginated envelope `{"count": int, "next": str | None, "previous": str | None, "results": [...]}` ([#4856](https://github.com/mem0ai/mem0/pull/4856))
|
||||
- **Default model:** `gpt-5-mini` is now the default across `OpenAILLM`, `OpenAIStructuredLLM`, `AzureOpenAILLM`, `AzureOpenAIStructuredLLM`, and `LiteLLM` fallback ([#4829](https://github.com/mem0ai/mem0/pull/4829))
|
||||
|
||||
**Breaking Changes:**
|
||||
- **`add()` returns ADD-only events** — No more `"UPDATE"` or `"DELETE"` events. Memories accumulate; nothing is overwritten ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **`search()` default `threshold` is now `0.1`** — Pass `threshold=0.0` for previous behavior ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **`search()` `score` is now a combined multi-signal score** — The top-level `score` fuses semantic similarity, BM25 keyword match, and entity boost into one value. Absolute numbers shift versus the old raw cosine score; retune any hard thresholds against representative queries. Per-signal scores are not exposed on the response ([#4805](https://github.com/mem0ai/mem0/pull/4805), [#4836](https://github.com/mem0ai/mem0/pull/4836))
|
||||
- **`search()` default `rerank` is now `False`** — Pass `rerank=True` for previous behavior ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **`top_k` default changed 100 → 20** in `Memory.get_all()` and `Memory.search()` (sync + async). Pass `top_k=100` explicitly to restore the old behavior ([#4843](https://github.com/mem0ai/mem0/pull/4843))
|
||||
- **Entity ID validation:** `user_id` / `agent_id` / `run_id` are trimmed; empty-string and whitespace-only values now raise `ValueError` ([#4843](https://github.com/mem0ai/mem0/pull/4843))
|
||||
- **Search params validation:** `threshold` must be a number in `[0, 1]`; `top_k` must be a non-negative integer — invalid inputs raise `ValueError` ([#4843](https://github.com/mem0ai/mem0/pull/4843))
|
||||
- **`messages` in `Memory.add()` rejects invalid types:** Passing `None` or non-`(str | dict | list)` values raises `Mem0ValidationError` (`error_code="VALIDATION_003"`) ([#4843](https://github.com/mem0ai/mem0/pull/4843))
|
||||
- **`qdrant-client>=1.12.0` required** — Upgrade from `>=1.9.1` ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **`org_id` and `project_id` removed** — Removed from `MemoryClient` constructor and all method signatures ([#4740](https://github.com/mem0ai/mem0/pull/4740))
|
||||
- **Graph Memory Removed (OSS):** `mem0/memory/graph_memory.py`, `memgraph_memory.py`, `kuzu_memory.py`, `apache_age_memory.py`, and `mem0/graphs/` (Neo4j / Memgraph / Kuzu / Apache AGE / Neptune drivers) deleted — ~4,000 lines. Graph memory is no longer supported in the OSS SDK; graph drivers (neo4j, memgraph, kuzu, etc.) can be uninstalled. Use the Platform API for graph features. Remove `enable_graph` and `graph_store` from your config ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **`enable_graph` removed from Client SDK** — Graph memory is now a project-level setting on the Platform. Remove `enable_graph` from `MemoryClient.add()` / `search()` / `get_all()` / `update_project()` calls ([#4776](https://github.com/mem0ai/mem0/pull/4776))
|
||||
- **`custom_fact_extraction_prompt` renamed to `custom_instructions`** — Update config and memory module references ([#4740](https://github.com/mem0ai/mem0/pull/4740))
|
||||
- **Typed option classes** — Added Pydantic v2 typed classes: `AddMemoryOptions`, `SearchMemoryOptions`, `GetAllMemoryOptions`, `DeleteAllMemoryOptions`, `UpdateMemoryOptions`, `ProjectUpdateOptions` ([#4740](https://github.com/mem0ai/mem0/pull/4740))
|
||||
|
||||
**Security:**
|
||||
- **FAISS:** Prevent arbitrary code execution via pickle deserialization in `FAISS` vector store ([#4833](https://github.com/mem0ai/mem0/pull/4833))
|
||||
|
||||
**Bug Fixes:**
|
||||
- **V3 migration crashes:** Fixed crashes in the v3 migration path; entity linking on OSS is now functional across Qdrant and Milvus backends ([#4836](https://github.com/mem0ai/mem0/pull/4836))
|
||||
- **Qdrant entity store:** Entity store now shares the existing Qdrant client when using embedded mode (`path=...`), eliminating RocksDB lock contention between the main and entity collections ([#4836](https://github.com/mem0ai/mem0/pull/4836))
|
||||
- **Reranker:** Fixed incorrect use of SentenceTransformer for cross-encoder reranker models — switched to CrossEncoder API for proper scoring ([#4806](https://github.com/mem0ai/mem0/pull/4806))
|
||||
- **S3 Vectors:** Handle `vector=None` in `update()` to prevent boto3 validation error when `event=NONE` ([#4594](https://github.com/mem0ai/mem0/pull/4594))
|
||||
- **LLMs:** Made OpenAI `store` parameter opt-in to prevent leaking to non-OpenAI backends like Google Gemini ([#4757](https://github.com/mem0ai/mem0/pull/4757))
|
||||
- **LLMs:** Forward `response_format` to Azure OpenAI API to prevent JSON parsing failures ([#4689](https://github.com/mem0ai/mem0/pull/4689))
|
||||
- **Core:** Guard `temp_uuid_mapping` lookups against LLM-hallucinated IDs with safe `.get()` and warnings ([#4674](https://github.com/mem0ai/mem0/pull/4674))
|
||||
- **Client:** Prevent `MemoryClient.feedback()` telemetry TypeError by merging feedback data into single payload ([#4795](https://github.com/mem0ai/mem0/pull/4795))
|
||||
|
||||
**Improvements:**
|
||||
- **Telemetry:** Sample OSS hot-path events at 10% via PostHog `before_send` hook to reduce event volume ([#4771](https://github.com/mem0ai/mem0/pull/4771))
|
||||
|
||||
See the [OSS v1 to v2 migration guide](https://docs.mem0.ai/migration/oss-v1-to-v2) and [Platform migration guide](https://docs.mem0.ai/migration/platform-v2-to-v3) for upgrade instructions.
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-04-06" description="v1.0.11">
|
||||
|
||||
**New Features & Updates:**
|
||||
- **SDK:** Added `multilingual` parameter to project update ([#4314](https://github.com/mem0ai/mem0/pull/4314))
|
||||
@@ -843,7 +893,59 @@ mode: "wide"
|
||||
</Tab>
|
||||
|
||||
<Tab title="TypeScript">
|
||||
<Update label="2026-04-04" description="v2.4.6">
|
||||
<Update label="2026-04-14" description="v3.0.0">
|
||||
|
||||
**Major Release** — TypeScript SDK with V3 memory pipeline, camelCase parameters, and cleaned-up API surface.
|
||||
|
||||
**V3 Memory Pipeline (OSS):**
|
||||
- **Single-Pass Extraction:** Additive extraction pipeline aligned with Python SDK — memories accumulate, no UPDATE/DELETE events ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **Entity Extraction & Linking:** New `entity_extraction.ts` module (720+ lines) with cross-memory relationship retrieval ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **Message Persistence:** SQLite-based message history via new `SQLiteManager.ts` with rolling window for LLM context ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **Batch Embeddings:** `embedBatch()` support in OpenAI and Azure embedding providers ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **Scoring & Lemmatization:** New `scoring.ts` and `lemmatization.ts` utilities for hybrid search ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **New Prompts:** `prompts/index.ts` (592+ lines) with additive extraction prompt aligned with Python SDK ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **V3 API Endpoints:** `MemoryClient.add()` now posts to `/v3/memories/add/`; `MemoryClient.getAll()` posts to `/v3/memories/` with paginated envelope `{ count, next, previous, results }` ([#4856](https://github.com/mem0ai/mem0/pull/4856))
|
||||
- **Default model:** `gpt-5-mini` is now the default in `OpenAI`, `OpenAIStructured`, and `Azure` LLM providers ([#4829](https://github.com/mem0ai/mem0/pull/4829))
|
||||
|
||||
**Breaking Changes:**
|
||||
- **Graph Memory Removed (OSS):** `graph_memory.ts` (675 lines), `graphs/tools.ts` (267 lines), `graphs/utils.ts` (116 lines), `graphs/configs.ts` (30 lines) deleted. Graph memory is no longer supported in the OSS SDK — use Platform API for graph features ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **camelCase Parameters (Client SDK):** All user-facing parameters converted from snake_case to camelCase. Mapping is transparent at API boundary via `camelToSnakeKeys()` / `snakeToCamelKeys()` ([#4776](https://github.com/mem0ai/mem0/pull/4776))
|
||||
```typescript
|
||||
// Before
|
||||
client.add(messages, { user_id: "alice", top_k: 5 });
|
||||
// After
|
||||
client.add(messages, { userId: "alice", topK: 5 });
|
||||
```
|
||||
- **Per-Method Option Types:** Replaced monolithic `MemoryOptions` with typed interfaces: `AddMemoryOptions`, `SearchMemoryOptions`, `GetAllMemoryOptions`, `DeleteAllMemoryOptions` ([#4740](https://github.com/mem0ai/mem0/pull/4740))
|
||||
- **Removed Deprecated Parameters:** `org_id`, `project_id`, `api_version`, `output_format`, `async_mode`, `enable_graph`, `limit` removed from client method signatures. `ClientOptions` reduced to `{ apiKey, host }` only ([#4740](https://github.com/mem0ai/mem0/pull/4740))
|
||||
- **`limit` renamed to `topK` (OSS):** Update all search calls ([#4740](https://github.com/mem0ai/mem0/pull/4740))
|
||||
- **`topK` default changed 100 → 20** in `Memory.getAll()` and `Memory.search()`. Pass `topK: 100` explicitly to restore the old behavior ([#4843](https://github.com/mem0ai/mem0/pull/4843))
|
||||
- **Entity ID validation:** `userId` / `agentId` / `runId` are trimmed; empty-string and whitespace-only values now throw ([#4843](https://github.com/mem0ai/mem0/pull/4843))
|
||||
- **Search params validation:** `threshold` must be in `[0, 1]`; `topK` must be a non-negative integer — invalid inputs throw ([#4843](https://github.com/mem0ai/mem0/pull/4843))
|
||||
- **`messages` in `Memory.add()` is required:** Passing `undefined` or `null` now throws ([#4843](https://github.com/mem0ai/mem0/pull/4843))
|
||||
- **`customPrompt` renamed to `customInstructions` (OSS):** Update memory and vector store configurations ([#4740](https://github.com/mem0ai/mem0/pull/4740))
|
||||
- **`enableGraph` removed (OSS):** Config option removed — graph memory no longer available in OSS ([#4776](https://github.com/mem0ai/mem0/pull/4776))
|
||||
|
||||
**New Features:**
|
||||
- **LLMs:** Added DeepSeek LLM provider with OpenAI-compatible integration using custom baseURL to `api.deepseek.com` ([#4613](https://github.com/mem0ai/mem0/pull/4613))
|
||||
- **Entity store isolation:** `MemoryVectorStore` now uses a dedicated `_entities.db` file, preventing entity/memory store collisions ([#4829](https://github.com/mem0ai/mem0/pull/4829), [#4841](https://github.com/mem0ai/mem0/pull/4841))
|
||||
- **Payload backward compatibility:** Legacy camelCase payload keys normalized to snake_case on read ([#4841](https://github.com/mem0ai/mem0/pull/4841))
|
||||
|
||||
**Bug Fixes:**
|
||||
- **V3 migration:** Fixed crashes in the OSS migration path; entity linking works end-to-end ([#4836](https://github.com/mem0ai/mem0/pull/4836))
|
||||
- **PGVector init race:** `PGVector.initialize()` now memoises the in-flight init promise ([#4841](https://github.com/mem0ai/mem0/pull/4841))
|
||||
- **Redis module detection:** Handles both node-redis v4+ and legacy `moduleList` response shapes ([#4841](https://github.com/mem0ai/mem0/pull/4841))
|
||||
- **Config:** Fixed `ConfigManager.mergeConfig()` to only include `graphStore` when explicitly provided by user, preventing default Neo4j connection attempts ([#4776](https://github.com/mem0ai/mem0/pull/4776))
|
||||
- **LLMs:** Config manager now falls back to `userConf.url` for `baseURL` — prevents custom LLM providers (Ollama, LMStudio) from silently connecting to OpenAI ([#4761](https://github.com/mem0ai/mem0/pull/4761))
|
||||
|
||||
**Improvements:**
|
||||
- **Telemetry:** Sample OSS hot-path events at 10% to reduce PostHog event volume ([#4771](https://github.com/mem0ai/mem0/pull/4771))
|
||||
|
||||
See the [TypeScript SDK migration guide](https://docs.mem0.ai/migration/ts-v2-to-v3) for upgrade instructions.
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-04-06" description="v2.4.6">
|
||||
|
||||
**New Features & Updates:**
|
||||
- **Client:** Added `multilingual` parameter to project update types ([#4314](https://github.com/mem0ai/mem0/pull/4314))
|
||||
@@ -1160,6 +1262,17 @@ mode: "wide"
|
||||
|
||||
<Tab title="CLI">
|
||||
|
||||
<Update label="2026-04-11" description="Python v0.2.3 / Node v0.2.3">
|
||||
|
||||
**Bug Fixes:**
|
||||
- **Telemetry:** Replaced shared `"anonymous-cli"` fallback with a persistent per-machine random hash (`cli-anon-<uuid>`), so anonymous CLI users are counted individually in PostHog instead of collapsing into one identity ([#4789](https://github.com/mem0ai/mem0/pull/4789))
|
||||
- **Telemetry:** Added PostHog `$identify` event on first authenticated run to stitch pre-signup anonymous history onto the authenticated user profile ([#4789](https://github.com/mem0ai/mem0/pull/4789))
|
||||
|
||||
**Improvements:**
|
||||
- **API:** All API calls now include `source=CLI` in request bodies (POST/PUT) and query params (GET/DELETE) for server-side attribution ([#4789](https://github.com/mem0ai/mem0/pull/4789))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-04-06" description="Python v0.2.2 / Node v0.2.2">
|
||||
|
||||
**New Features:**
|
||||
|
||||
@@ -21,7 +21,7 @@ os.environ["OPENAI_API_KEY"] = "your-api-key"
|
||||
|
||||
# Initialize a LangChain model directly
|
||||
openai_model = ChatOpenAI(
|
||||
model="gpt-4.1-nano-2025-04-14",
|
||||
model="gpt-5-mini",
|
||||
temperature=0.2,
|
||||
max_tokens=2000
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ config = {
|
||||
"llm": {
|
||||
"provider": "litellm",
|
||||
"config": {
|
||||
"model": "gpt-4.1-nano-2025-04-14",
|
||||
"model": "gpt-5-mini",
|
||||
"temperature": 0.2,
|
||||
"max_tokens": 2000,
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ config = {
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4.1-nano-2025-04-14",
|
||||
"model": "gpt-5-mini",
|
||||
"temperature": 0.2,
|
||||
"max_tokens": 2000,
|
||||
}
|
||||
@@ -86,7 +86,7 @@ config = {
|
||||
"llm": {
|
||||
"provider": "openai_structured",
|
||||
"config": {
|
||||
"model": "gpt-4.1-nano-2025-04-14",
|
||||
"model": "gpt-5-mini",
|
||||
"temperature": 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ config = {
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4.1-nano-2025-04-14"
|
||||
"model": "gpt-5-mini"
|
||||
}
|
||||
},
|
||||
"reranker": {
|
||||
|
||||
@@ -189,7 +189,7 @@ for i, prompt in enumerate(prompts):
|
||||
config["reranker"]["config"]["scoring_prompt"] = prompt
|
||||
memory = Memory.from_config(config)
|
||||
|
||||
results = memory.search("test query", user_id="test_user")
|
||||
results = memory.search("test query", filters={"user_id": "test_user"})
|
||||
print(f"Prompt {i+1} results: {results}")
|
||||
```
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ config = {
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4.1-nano-2025-04-14"
|
||||
"model": "gpt-5-mini"
|
||||
}
|
||||
},
|
||||
"reranker": {
|
||||
@@ -95,7 +95,7 @@ messages = [
|
||||
memory.add(messages, user_id="bob")
|
||||
|
||||
# Search with reranking
|
||||
results = memory.search("What is the user's profession?", user_id="bob")
|
||||
results = memory.search("What is the user's profession?", filters={"user_id": "bob"})
|
||||
|
||||
for result in results['results']:
|
||||
print(f"Memory: {result['memory']}")
|
||||
|
||||
@@ -175,7 +175,7 @@ queries = [
|
||||
|
||||
results = []
|
||||
for query in queries:
|
||||
result = m.search(query, user_id="alice", rerank=True)
|
||||
result = m.search(query, filters={"user_id": "alice"}, rerank=True)
|
||||
results.append(result)
|
||||
```
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ messages = [
|
||||
memory.add(messages, user_id="david")
|
||||
|
||||
# Search with LLM reranking
|
||||
results = memory.search("What programming topics is the user studying?", user_id="david")
|
||||
results = memory.search("What programming topics is the user studying?", filters={"user_id": "david"})
|
||||
|
||||
for result in results['results']:
|
||||
print(f"Memory: {result['memory']}")
|
||||
|
||||
@@ -283,12 +283,12 @@ for result in results["results"]:
|
||||
def safe_llm_rerank_search(query, user_id, max_retries=3):
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
return m.search(query, user_id=user_id, rerank=True)
|
||||
return m.search(query, filters={"user_id": user_id}, rerank=True)
|
||||
except Exception as e:
|
||||
print(f"Attempt {attempt + 1} failed: {e}")
|
||||
if attempt == max_retries - 1:
|
||||
# Fall back to vector search
|
||||
return m.search(query, user_id=user_id, rerank=False)
|
||||
return m.search(query, filters={"user_id": user_id}, rerank=False)
|
||||
|
||||
# Use the safe function
|
||||
results = safe_llm_rerank_search("What are my preferences?", "alice")
|
||||
@@ -376,19 +376,19 @@ class RobustLLMReranker:
|
||||
# Try primary LLM reranker
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
return self.primary.search(query, user_id=user_id, rerank=True)
|
||||
return self.primary.search(query, filters={"user_id": user_id}, rerank=True)
|
||||
except Exception as e:
|
||||
print(f"Primary reranker attempt {attempt + 1} failed: {e}")
|
||||
|
||||
# Try fallback reranker
|
||||
if self.fallback:
|
||||
try:
|
||||
return self.fallback.search(query, user_id=user_id, rerank=True)
|
||||
return self.fallback.search(query, filters={"user_id": user_id}, rerank=True)
|
||||
except Exception as e:
|
||||
print(f"Fallback reranker failed: {e}")
|
||||
|
||||
# Final fallback: vector search only
|
||||
return self.primary.search(query, user_id=user_id, rerank=False)
|
||||
return self.primary.search(query, filters={"user_id": user_id}, rerank=False)
|
||||
|
||||
# Usage
|
||||
primary_config = {
|
||||
|
||||
@@ -101,7 +101,7 @@ messages = [
|
||||
memory.add(messages, user_id="charlie")
|
||||
|
||||
# Search with local reranking
|
||||
results = memory.search("What books does the user like?", user_id="charlie")
|
||||
results = memory.search("What books does the user like?", filters={"user_id": "charlie"})
|
||||
|
||||
for result in results['results']:
|
||||
print(f"Memory: {result['memory']}")
|
||||
|
||||
@@ -86,7 +86,7 @@ messages = [
|
||||
memory.add(messages, user_id="alice")
|
||||
|
||||
# Search with reranking
|
||||
results = memory.search("What Italian food does the user like?", user_id="alice")
|
||||
results = memory.search("What Italian food does the user like?", filters={"user_id": "alice"})
|
||||
|
||||
for result in results['results']:
|
||||
print(f"Memory: {result['memory']}")
|
||||
|
||||
@@ -153,7 +153,7 @@ def measure_reranker_performance(config, queries, user_id):
|
||||
latencies = []
|
||||
for query in queries:
|
||||
start_time = time.time()
|
||||
results = memory.search(query, user_id=user_id)
|
||||
results = memory.search(query, filters={"user_id": user_id})
|
||||
latency = time.time() - start_time
|
||||
latencies.append(latency)
|
||||
|
||||
@@ -191,7 +191,7 @@ class CachedReranker:
|
||||
|
||||
@lru_cache(maxsize=1000)
|
||||
def search_cached(self, query_hash, user_id):
|
||||
return self.memory.search(query, user_id=user_id)
|
||||
return self.memory.search(query, filters={"user_id": user_id})
|
||||
|
||||
def search(self, query, user_id):
|
||||
query_hash = hashlib.md5(f"{query}_{user_id}".encode()).hexdigest()
|
||||
|
||||
@@ -15,7 +15,7 @@ Mem0 supports LangChain as a provider for vector store integration. LangChain pr
|
||||
```python Python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
from langchain_community.vectorstores import Chroma
|
||||
from langchain_chroma import Chroma
|
||||
from langchain_openai import OpenAIEmbeddings
|
||||
|
||||
# Initialize a LangChain vector store
|
||||
|
||||
@@ -72,7 +72,7 @@ m.add(messages, user_id="alice", metadata={"category": "movies"})
|
||||
### Search Memories
|
||||
|
||||
```python
|
||||
results = m.search("What kind of movies does Alice like?", user_id="alice")
|
||||
results = m.search("What kind of movies does Alice like?", filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
@@ -36,7 +36,7 @@ messages = [
|
||||
m.add(messages, user_id="alice", metadata={"category": "movies"})
|
||||
|
||||
# Search memories
|
||||
results = m.search(query="sci-fi recommendations", user_id="alice")
|
||||
results = m.search(query="sci-fi recommendations", filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
### Config
|
||||
|
||||
@@ -50,4 +50,25 @@ Here are the parameters available for configuring Valkey:
|
||||
| `hnsw_m` | Number of bi-directional links for HNSW | `16` |
|
||||
| `hnsw_ef_construction` | Size of dynamic candidate list for HNSW | `200` |
|
||||
| `hnsw_ef_runtime` | Size of dynamic candidate list for search | `10` |
|
||||
| `cluster_mode` | Enable cluster mode for Valkey cluster (CME) deployments | `false` |
|
||||
| `distance_metric` | Distance metric for vector similarity | `cosine` |
|
||||
|
||||
## Cluster Mode
|
||||
|
||||
To use Valkey with cluster mode enabled (CME), set `cluster_mode` to `true`:
|
||||
|
||||
```python
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "valkey",
|
||||
"config": {
|
||||
"collection_name": "memories",
|
||||
"valkey_url": "valkey://cluster-endpoint:6379",
|
||||
"embedding_model_dims": 1536,
|
||||
"cluster_mode": True
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When cluster mode is enabled, the connector uses `ValkeyCluster` instead of the standalone client, which handles `MOVED`/`ASK` redirections automatically. Search queries are coordinated across all shards by the valkey-search module's built-in coordinator. See the [valkey-search documentation](https://github.com/valkey-io/valkey-search) for details on cluster mode behavior.
|
||||
|
||||
@@ -60,7 +60,7 @@ class PersonalAITutor:
|
||||
"""
|
||||
# Start a streaming response request to the AI
|
||||
response = self.client.responses.create(
|
||||
model="gpt-4.1-nano-2025-04-14",
|
||||
model="gpt-5-mini",
|
||||
instructions="You are a personal AI Tutor.",
|
||||
input=question,
|
||||
stream=True
|
||||
@@ -81,7 +81,7 @@ class PersonalAITutor:
|
||||
:param user_id: Optional user ID to filter memories.
|
||||
:return: List of memories.
|
||||
"""
|
||||
return self.memory.get_all(user_id=user_id)
|
||||
return self.memory.get_all(filters={"user_id": user_id})
|
||||
|
||||
# Instantiate the PersonalAITutor
|
||||
ai_tutor = PersonalAITutor()
|
||||
|
||||
@@ -57,7 +57,7 @@ m = Memory.from_config(config)
|
||||
m.add("I'm visiting Paris", user_id="john")
|
||||
|
||||
# Retrieve memories
|
||||
memories = m.get_all(user_id="john")
|
||||
memories = m.get_all(filters={"user_id": "john"})
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
@@ -47,7 +47,7 @@ ${memoriesStr}`;
|
||||
];
|
||||
|
||||
const response = await openaiClient.chat.completions.create({
|
||||
model: "gpt-4.1-nano-2025-04-14",
|
||||
model: "gpt-5-mini",
|
||||
messages: messages
|
||||
});
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ config = {
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4.1-nano-2025-04-14",
|
||||
"model": "gpt-5-mini",
|
||||
"temperature": 0.1,
|
||||
"max_tokens": 2000,
|
||||
}
|
||||
@@ -77,7 +77,7 @@ class PersonalTravelAssistant:
|
||||
|
||||
# Generate response using Responses API
|
||||
response = self.client.responses.create(
|
||||
model="gpt-4.1-nano-2025-04-14",
|
||||
model="gpt-5-mini",
|
||||
input=prompt
|
||||
)
|
||||
|
||||
@@ -89,11 +89,11 @@ class PersonalTravelAssistant:
|
||||
return answer
|
||||
|
||||
def get_memories(self, user_id):
|
||||
memories = self.memory.get_all(user_id=user_id)
|
||||
memories = self.memory.get_all(filters={"user_id": user_id})
|
||||
return [m['memory'] for m in memories['results']]
|
||||
|
||||
def search_memories(self, query, user_id):
|
||||
memories = self.memory.search(query, user_id=user_id)
|
||||
memories = self.memory.search(query, filters={"user_id": user_id})
|
||||
return [m['memory'] for m in memories['results']]
|
||||
|
||||
# Usage example
|
||||
@@ -143,7 +143,7 @@ class PersonalTravelAssistant:
|
||||
|
||||
# Generate response using gpt-4.1-nano
|
||||
response = self.client.chat.completions.create(
|
||||
model="gpt-4.1-nano-2025-04-14"2025-04-14",
|
||||
model="gpt-5-mini",
|
||||
messages=self.messages
|
||||
)
|
||||
answer = response.choices[0].message.content
|
||||
|
||||
@@ -126,16 +126,15 @@ async def search_memories(
|
||||
print(f"Finding memories related to: {query}")
|
||||
results = await mem0_client.search(
|
||||
query,
|
||||
user_id=USER_ID,
|
||||
limit=5,
|
||||
filters={"user_id": USER_ID},
|
||||
top_k=5,
|
||||
threshold=0.7, # Higher threshold for more relevant results
|
||||
|
||||
)
|
||||
|
||||
|
||||
# Format and return the results
|
||||
if not results.get('results', []):
|
||||
return "I don't have any relevant memories about this topic."
|
||||
|
||||
|
||||
memories = [f"• {result['memory']}" for result in results.get('results', [])]
|
||||
return "Here's what I remember that might be relevant:\n" + "\n".join(memories)
|
||||
```
|
||||
@@ -161,7 +160,7 @@ def create_memory_voice_agent():
|
||||
Use the search_memories tool when you need context from past conversations or user asks you to recall something.
|
||||
""",
|
||||
),
|
||||
model="gpt-4.1-nano-2025-04-14",
|
||||
model="gpt-5-mini",
|
||||
tools=[save_memories, search_memories],
|
||||
)
|
||||
|
||||
@@ -342,16 +341,15 @@ async def search_memories(
|
||||
print(f"Finding memories related to: {query}")
|
||||
results = await mem0_client.search(
|
||||
query,
|
||||
user_id=USER_ID,
|
||||
limit=5,
|
||||
filters={"user_id": USER_ID},
|
||||
top_k=5,
|
||||
threshold=0.7, # Higher threshold for more relevant results
|
||||
|
||||
)
|
||||
|
||||
|
||||
# Format and return the results
|
||||
if not results.get('results', []):
|
||||
return "I don't have any relevant memories about this topic."
|
||||
|
||||
|
||||
memories = [f"• {result['memory']}" for result in results.get('results', [])]
|
||||
return "Here's what I remember that might be relevant:\n" + "\n".join(memories)
|
||||
|
||||
@@ -368,7 +366,7 @@ def create_memory_voice_agent():
|
||||
Use the search_memories tool when you need context from past conversations or user asks you to recall something.
|
||||
""",
|
||||
),
|
||||
model="gpt-4.1-nano-2025-04-14",
|
||||
model="gpt-5-mini",
|
||||
tools=[save_memories, search_memories],
|
||||
)
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ mem0_client = MemoryClient(api_key="your-mem0-key")
|
||||
|
||||
def chat(user_input, user_id):
|
||||
# Retrieve relevant memories
|
||||
memories = mem0_client.search(user_input, user_id=user_id, limit=5)
|
||||
memories = mem0_client.search(user_input, filters={"user_id": user_id}, top_k=5)
|
||||
context = "\\n".join(m["memory"] for m in memories["results"])
|
||||
|
||||
# Call LLM with memory context
|
||||
@@ -123,7 +123,7 @@ ollama_chat = OpenAI(base_url=f"{OLLAMA_URL}/v1", api_key="ollama")
|
||||
|
||||
def chat(user_input, user_id):
|
||||
# Retrieve relevant memories
|
||||
memories = memory.search(user_input, user_id=user_id, limit=5)
|
||||
memories = memory.search(user_input, filters={"user_id": user_id}, top_k=5)
|
||||
context = "\n".join(m["memory"] for m in memories["results"])
|
||||
|
||||
# Call LLM with memory context (Ollama via OpenAI-compatible API)
|
||||
@@ -319,7 +319,7 @@ print([m["memory"] for m in memories["results"]])
|
||||
</Tab>
|
||||
<Tab title="Open Source">
|
||||
```python
|
||||
memories = memory.get_all(user_id="max")
|
||||
memories = memory.get_all(filters={"user_id": "max"})
|
||||
print([m["memory"] for m in memories["results"]])
|
||||
# Output: ["Max wants to run marathon under 4 hours", "hey", "lol ok", "cool thanks", "gtg bye"]
|
||||
```
|
||||
@@ -397,7 +397,7 @@ print([m["memory"] for m in memories["results"]])
|
||||
chat("hey how's it going", user_id="max")
|
||||
chat("I prefer trail running over roads", user_id="max")
|
||||
|
||||
memories = memory.get_all(user_id="max")
|
||||
memories = memory.get_all(filters={"user_id": "max"})
|
||||
print([m["memory"] for m in memories["results"]])
|
||||
# Output: ["Max wants to run marathon under 4 hours", "Max prefers trail running over roads"]
|
||||
```
|
||||
@@ -446,7 +446,7 @@ Retrieve agent style alongside user memories:
|
||||
<Tab title="Platform">
|
||||
```python
|
||||
# Get coach personality
|
||||
agent_memories = mem0_client.search("coaching style", agent_id="ray_coach")
|
||||
agent_memories = mem0_client.search("coaching style", filters={"agent_id": "ray_coach"})
|
||||
# Output: ["Max wants direct, data-driven feedback. Skip motivational language."]
|
||||
|
||||
# Store conversations with agent_id
|
||||
@@ -459,7 +459,7 @@ mem0_client.add([
|
||||
<Tab title="Open Source">
|
||||
```python
|
||||
# Get coach personality
|
||||
agent_memories = memory.search("coaching style", agent_id="ray_coach")
|
||||
agent_memories = memory.search("coaching style", filters={"agent_id": "ray_coach"})
|
||||
# Output: ["Max wants direct, data-driven feedback. Skip motivational language."]
|
||||
|
||||
# Store conversations with agent_id
|
||||
@@ -706,13 +706,13 @@ memory.add(
|
||||
<Tabs>
|
||||
<Tab title="Platform">
|
||||
```python
|
||||
memories = mem0_client.search("training plan", user_id="max", limit=5)
|
||||
memories = mem0_client.search("training plan", filters={"user_id": "max"}, top_k=5)
|
||||
# Gets: marathon goal, trail preference, ankle injury (if still valid)
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Open Source">
|
||||
```python
|
||||
memories = memory.search("training plan", user_id="max", limit=5)
|
||||
memories = memory.search("training plan", filters={"user_id": "max"}, top_k=5)
|
||||
# Gets: marathon goal, trail preference, ankle injury (if still valid / not pruned)
|
||||
```
|
||||
</Tab>
|
||||
@@ -806,7 +806,7 @@ mem0_client.update(goal_memory["id"], "Max wants to run sub-3:45 marathon")
|
||||
<Tab title="Open Source">
|
||||
```python
|
||||
# Find the old memory
|
||||
memories = memory.get_all(user_id="max")
|
||||
memories = memory.get_all(filters={"user_id": "max"})
|
||||
goal_memory = [m for m in memories["results"] if "sub-4" in m["memory"]][0]
|
||||
|
||||
# Update it
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
---
|
||||
title: Browser Extension Memory
|
||||
description: "Add Mem0's universal memory layer to Chrome chat surfaces."
|
||||
---
|
||||
|
||||
|
||||
Enhance your AI interactions with Mem0, a Chrome extension that introduces a universal memory layer across platforms like ChatGPT, Claude, and Perplexity. Mem0 ensures seamless context sharing, making your AI experiences more personalized and efficient.
|
||||
|
||||
<Note>
|
||||
We now support Grok! The Mem0 Chrome Extension has been updated to work with Grok, bringing the same powerful memory capabilities to your Grok conversations.
|
||||
</Note>
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **Universal Memory Layer**: Share context seamlessly across ChatGPT, Claude, Perplexity, and Grok.
|
||||
- **Smart Context Detection**: Automatically captures relevant information from your conversations.
|
||||
- **Intelligent Memory Retrieval**: Surfaces pertinent memories at the right time.
|
||||
- **One-Click Sync**: Easily synchronize with existing ChatGPT memories.
|
||||
- **Memory Dashboard**: Manage all your memories in one centralized location.
|
||||
|
||||
## Installation
|
||||
|
||||
You can install the Mem0 Chrome Extension using one of the following methods:
|
||||
|
||||
### Method 1: Chrome Web Store Installation
|
||||
|
||||
1. **Download the Extension**: Open Google Chrome and navigate to the [Mem0 Chrome Extension page](https://chromewebstore.google.com/detail/mem0/onihkkbipkfeijkadecaafbgagkhglop?hl=en).
|
||||
2. **Add to Chrome**: Click on the "Add to Chrome" button.
|
||||
3. **Confirm Installation**: In the pop-up dialog, click "Add extension" to confirm. The Mem0 icon should now appear in your Chrome toolbar.
|
||||
|
||||
### Method 2: Manual Installation
|
||||
|
||||
1. **Download the Extension**: Clone or download the extension files from the [Mem0 Chrome Extension GitHub repository](https://github.com/mem0ai/mem0-chrome-extension).
|
||||
2. **Access Chrome Extensions**: Open Google Chrome and navigate to `chrome://extensions`.
|
||||
3. **Enable Developer Mode**: Toggle the "Developer mode" switch in the top right corner.
|
||||
4. **Load Unpacked Extension**: Click "Load unpacked" and select the directory containing the extension files.
|
||||
5. **Confirm Installation**: The Mem0 Chrome Extension should now appear in your Chrome toolbar.
|
||||
|
||||
## Usage
|
||||
|
||||
1. **Locate the Mem0 Icon**: After installation, find the Mem0 icon in your Chrome toolbar.
|
||||
2. **Sign In**: Click the icon and sign in with your Google account.
|
||||
3. **Interact with AI Assistants**:
|
||||
- **ChatGPT and Perplexity**: Continue your conversations as usual; Mem0 operates seamlessly in the background.
|
||||
- **Claude**: Click the Mem0 button or use the shortcut `Ctrl + M` to activate memory functions.
|
||||
|
||||
## Configuration
|
||||
|
||||
- **API Key**: Obtain your API key from the Mem0 Dashboard to connect the extension to the Mem0 API.
|
||||
- **User ID**: This is your unique identifier in the Mem0 system. If not provided, it defaults to `chrome-extension-user`.
|
||||
|
||||
## Demo Video
|
||||
|
||||
<iframe width="700" height="400" src="https://www.youtube.com/embed/dqenCMMlfwQ?si=zhGVrkq6IS_0Jwyj" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||
|
||||
## Privacy and Data Security
|
||||
|
||||
Your messages are sent to the Mem0 API for extracting and retrieving memories. Mem0 is committed to ensuring your data's privacy and security.
|
||||
|
||||
---
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Build a Mem0 Companion" icon="users" href="/cookbooks/essentials/building-ai-companion">
|
||||
Learn the foundations of memory-powered assistants that work across platforms.
|
||||
</Card>
|
||||
<Card title="Multimodal Support" icon="image" href="/platform/features/multimodal-support">
|
||||
Extend your browser interactions with vision and audio memory.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -83,7 +83,7 @@ class MultiAgentLearningSystem:
|
||||
|
||||
def __init__(self, student_id: str):
|
||||
self.student_id = student_id
|
||||
self.llm = OpenAI(model="gpt-4.1-nano-2025-04-14", temperature=0.2)
|
||||
self.llm = OpenAI(model="gpt-5-mini", temperature=0.2)
|
||||
|
||||
# Memory context for this student
|
||||
self.memory_context = {"user_id": student_id, "app": "learning_assistant"}
|
||||
|
||||
@@ -22,7 +22,7 @@ import os
|
||||
from llama_index.llms.openai import OpenAI
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "<your-openai-api-key>"
|
||||
llm = OpenAI(model="gpt-4.1-nano-2025-04-14")
|
||||
llm = OpenAI(model="gpt-5-mini")
|
||||
```
|
||||
|
||||
Initialize the Mem0 client. You can find your API key <a href="https://app.mem0.ai/dashboard/api-keys" rel="nofollow">here</a>. Read about Mem0 [Open Source](https://docs.mem0.ai/open-source/overview).
|
||||
|
||||
@@ -1,766 +0,0 @@
|
||||
---
|
||||
title: MiroFish Swarm Memory
|
||||
description: "Build a multi-agent swarm simulation with graph-powered memory using Mem0 and MiroFish patterns."
|
||||
---
|
||||
|
||||
<Snippet file="blank-notif.mdx" />
|
||||
|
||||
Build a multi-agent swarm simulation with graph-powered memory using Mem0 OSS and [MiroFish](https://github.com/666ghj/MiroFish) patterns. MiroFish is a graph-centric system — it extracts entities and relationships from documents, builds a knowledge graph, and queries it throughout its pipeline. Mem0's Graph Memory is a natural replacement for its Zep Cloud integration.
|
||||
|
||||
<Note>
|
||||
This cookbook demonstrates the **core memory patterns** using a simplified simulation. MiroFish's actual architecture uses a factory pattern (`memory_factory.py`) with abstract providers, batch buffering with retries in `ZepGraphMemoryUpdater`, and IPC-based agent interviews. This cookbook focuses on the Mem0 API integration points — wrap these calls in your own retry/batch logic for production use.
|
||||
</Note>
|
||||
|
||||
## Overview
|
||||
|
||||
This cookbook implements a **Housing Policy Prediction Simulation** following MiroFish's five-stage workflow:
|
||||
|
||||
1. **Graph Building** — Ingest seed documents, extract entities and relationships
|
||||
2. **Environment Setup** — Query the knowledge graph to enrich agent profiles
|
||||
3. **Simulation** — Track agent interactions with per-agent memory isolation
|
||||
4. **Report Generation** — Semantic search + graph traversal for analysis
|
||||
5. **Deep Interaction** — Query post-simulation memory and relationships (MiroFish also supports live agent interviews via IPC — not covered here)
|
||||
|
||||
Three agents debate a housing policy reform:
|
||||
- **Mayor Chen** — Policy advocate pushing for zoning reform
|
||||
- **Wang (Homeowner)** — Opposition leader organizing resistance
|
||||
- **Professor Li** — Academic providing data-driven analysis
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
pip install "mem0ai[graph]"
|
||||
```
|
||||
|
||||
You need a graph backend. Choose one:
|
||||
|
||||
| Backend | Setup | Best for |
|
||||
|---|---|---|
|
||||
| **Neo4j Aura** (free tier) | [Sign up](https://neo4j.com/product/auradb/), get Bolt URI | Production, closest to Zep |
|
||||
| **Neo4j Docker** | `docker run -p 7687:7687 -e NEO4J_AUTH=neo4j/password neo4j:5` | Local development |
|
||||
| **Kuzu** (embedded) | No setup needed — runs in-process | Quick testing, zero dependencies |
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY="sk-..."
|
||||
|
||||
# Option A: Neo4j Docker (local development)
|
||||
docker run -p 7687:7687 -e NEO4J_AUTH=neo4j/password neo4j:5
|
||||
export NEO4J_URL="neo4j://localhost:7687"
|
||||
export NEO4J_USERNAME="neo4j"
|
||||
export NEO4J_PASSWORD="password"
|
||||
|
||||
# Option B: Neo4j Aura (production — free tier available)
|
||||
export NEO4J_URL="neo4j+s://<your-instance>.databases.neo4j.io"
|
||||
export NEO4J_USERNAME="neo4j"
|
||||
export NEO4J_PASSWORD="your-aura-password"
|
||||
|
||||
# Option C: Kuzu (zero setup — auto-detected when NEO4J_URL is not set)
|
||||
# No exports needed
|
||||
```
|
||||
|
||||
## Complete Implementation
|
||||
|
||||
```python
|
||||
"""
|
||||
MiroFish Swarm Prediction Simulation with Mem0 Graph Memory
|
||||
|
||||
MiroFish uses Zep Cloud as its knowledge graph backend. This implementation
|
||||
replaces Zep with Mem0 OSS Graph Memory, which provides:
|
||||
- Automatic entity extraction from text
|
||||
- Relationship mining (source → relationship → destination triples)
|
||||
- Combined vector + graph search returning memories AND relations
|
||||
- Per-agent isolation via run_id
|
||||
- Self-hosted with no node caps
|
||||
|
||||
Follows MiroFish's 5-stage pipeline:
|
||||
1. Graph Building - Ingest seed documents, extract entities
|
||||
2. Environment Setup - Query graph to enrich agent profiles
|
||||
3. Simulation - Track agent actions with per-agent isolation
|
||||
4. Report Generation - Semantic + graph search for analysis
|
||||
5. Deep Interaction - Query post-simulation knowledge graph
|
||||
|
||||
Run:
|
||||
export OPENAI_API_KEY="sk-..."
|
||||
export NEO4J_URL="neo4j://localhost:7687"
|
||||
export NEO4J_USERNAME="neo4j"
|
||||
export NEO4J_PASSWORD="password"
|
||||
python mirofish_swarm_memory.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
from mem0 import Memory
|
||||
|
||||
|
||||
# ======================================================================
|
||||
# MiroFish Agent Action Types (matches OASIS simulation output)
|
||||
# ======================================================================
|
||||
|
||||
# Twitter actions
|
||||
TWITTER_ACTIONS = [
|
||||
"CREATE_POST", "LIKE_POST", "REPOST", "FOLLOW",
|
||||
"DO_NOTHING", "QUOTE_POST",
|
||||
]
|
||||
|
||||
# Reddit actions (superset — includes moderation + discovery)
|
||||
REDDIT_ACTIONS = [
|
||||
"LIKE_POST", "DISLIKE_POST", "CREATE_POST", "CREATE_COMMENT",
|
||||
"LIKE_COMMENT", "DISLIKE_COMMENT", "SEARCH_POSTS", "SEARCH_USER",
|
||||
"TREND", "REFRESH", "DO_NOTHING", "FOLLOW", "MUTE",
|
||||
]
|
||||
|
||||
# Combined (DO_NOTHING is skipped during memory storage)
|
||||
MIROFISH_ACTIONS = list(set(TWITTER_ACTIONS + REDDIT_ACTIONS) - {"DO_NOTHING"})
|
||||
|
||||
|
||||
# ======================================================================
|
||||
# Graph Memory Configuration
|
||||
# ======================================================================
|
||||
|
||||
def build_config():
|
||||
"""Build Mem0 config with Graph Memory.
|
||||
|
||||
Uses Neo4j if credentials are set, otherwise falls back to Kuzu (embedded).
|
||||
"""
|
||||
neo4j_url = os.environ.get("NEO4J_URL")
|
||||
|
||||
# Shared config for LLM, embedder, and vector store
|
||||
base = {
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {"model": "gpt-4o-mini", "temperature": 0.1}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {"model": "text-embedding-3-small", "embedding_dims": 1536}
|
||||
},
|
||||
"vector_store": {
|
||||
"provider": "qdrant",
|
||||
"config": {
|
||||
"collection_name": "mirofish",
|
||||
"embedding_model_dims": 1536,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
custom_prompt = (
|
||||
"Extract all people, organizations, policies, locations, "
|
||||
"and their relationships. Capture support/opposition stances, "
|
||||
"affiliations, and quantitative claims."
|
||||
)
|
||||
|
||||
if neo4j_url:
|
||||
base["graph_store"] = {
|
||||
"provider": "neo4j",
|
||||
"config": {
|
||||
"url": neo4j_url,
|
||||
"username": os.environ.get("NEO4J_USERNAME", "neo4j"),
|
||||
"password": os.environ.get("NEO4J_PASSWORD", "password"),
|
||||
},
|
||||
"custom_prompt": custom_prompt,
|
||||
}
|
||||
else:
|
||||
# Fallback: Kuzu embedded (no external services needed)
|
||||
print(" NEO4J_URL not set — using Kuzu (embedded) graph store")
|
||||
base["graph_store"] = {
|
||||
"provider": "kuzu",
|
||||
"config": {"db": "/tmp/mirofish_graph.kuzu"},
|
||||
"custom_prompt": custom_prompt,
|
||||
}
|
||||
|
||||
return base
|
||||
|
||||
|
||||
# ======================================================================
|
||||
# Simulation Engine
|
||||
# ======================================================================
|
||||
|
||||
class MiroFishSimulation:
|
||||
"""
|
||||
Multi-agent simulation with graph-powered memory.
|
||||
|
||||
Uses Mem0 Graph Memory to replace MiroFish's Zep Cloud integration:
|
||||
- Entities and relationships are extracted automatically from text
|
||||
- search() returns both semantic memories AND graph relations
|
||||
- Per-agent isolation via run_id
|
||||
- Project isolation via user_id
|
||||
"""
|
||||
|
||||
def __init__(self, project_id: str, config: dict):
|
||||
self.project_id = project_id
|
||||
self.memory = Memory.from_config(config)
|
||||
self.stats = {
|
||||
"documents_ingested": 0,
|
||||
"activities_recorded": 0,
|
||||
"rounds_completed": 0,
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Stage 1: Graph Building — Seed Document Ingestion
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def ingest_documents(self, documents: list[str]):
|
||||
"""Ingest seed documents and extract entities + relationships.
|
||||
|
||||
MiroFish equivalent: GraphBuilderService.build_graph()
|
||||
Zep equivalent: graph.add_batch() with episode polling
|
||||
|
||||
With Mem0 Graph Memory, each document is processed by the LLM
|
||||
to extract entities (people, orgs, policies) and relationships
|
||||
(supports, opposes, filed). These become nodes and edges in the
|
||||
graph store, alongside vector embeddings for semantic search.
|
||||
"""
|
||||
print(" Ingesting documents and building knowledge graph...")
|
||||
for i, doc in enumerate(documents):
|
||||
result = self.memory.add(
|
||||
[{"role": "user", "content": doc}],
|
||||
user_id=self.project_id,
|
||||
metadata={"stage": "graph_building", "source": "seed_document", "chunk_index": i}
|
||||
)
|
||||
# Graph Memory returns extracted relations
|
||||
relations = result.get("relations", {})
|
||||
added = relations.get("added_entities", [])
|
||||
if added:
|
||||
print(f" Doc {i}: extracted {len(added)} entities/relations")
|
||||
|
||||
self.stats["documents_ingested"] = len(documents)
|
||||
print(f" Ingested {len(documents)} documents")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Stage 2: Environment Setup — Agent Profile Enrichment
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def enrich_agent_profile(self, agent_name: str, persona_query: str) -> dict:
|
||||
"""Search memory + graph for context relevant to an agent's persona.
|
||||
|
||||
MiroFish equivalent: OasisProfileGenerator using graph.search()
|
||||
|
||||
Returns both semantic memories and graph relations that can be
|
||||
injected into the agent's system prompt.
|
||||
"""
|
||||
results = self.memory.search(
|
||||
persona_query,
|
||||
user_id=self.project_id,
|
||||
limit=10
|
||||
)
|
||||
facts = [r["memory"] for r in results.get("results", [])]
|
||||
relations = results.get("relations", [])
|
||||
|
||||
print(f" {agent_name}: {len(facts)} facts, {len(relations)} relations")
|
||||
return {"facts": facts, "relations": relations}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Stage 3: Simulation — Agent Activity Tracking
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def record_action(self, agent_id: str, agent_name: str,
|
||||
action_type: str, content: str,
|
||||
platform: str, round_num: int):
|
||||
"""Record a single agent action as a memory with graph extraction.
|
||||
|
||||
MiroFish equivalent: ZepGraphMemoryUpdater.add_activity()
|
||||
Zep equivalent: graph.add(type="text", data=episode_text)
|
||||
|
||||
Agent memories use run_id to group by agent (no assistant
|
||||
memories involved). Graph Memory extracts entities/relationships
|
||||
from the action content automatically.
|
||||
"""
|
||||
formatted = f"{agent_name} [{action_type}]: {content}"
|
||||
|
||||
self.memory.add(
|
||||
[{"role": "user", "content": formatted}],
|
||||
run_id=agent_id,
|
||||
metadata={
|
||||
"action_type": action_type,
|
||||
"platform": platform,
|
||||
"round": round_num,
|
||||
"agent_name": agent_name,
|
||||
}
|
||||
)
|
||||
self.stats["activities_recorded"] += 1
|
||||
|
||||
def run_round(self, round_num: int, activities: list[tuple]):
|
||||
"""Execute one simulation round."""
|
||||
print(f" Round {round_num}: {len(activities)} actions")
|
||||
for agent_id, agent_name, action_type, content, platform in activities:
|
||||
self.record_action(agent_id, agent_name, action_type, content, platform, round_num)
|
||||
self.stats["rounds_completed"] = max(self.stats["rounds_completed"], round_num)
|
||||
|
||||
def recall_agent_memory(self, agent_id: str, query: str) -> dict:
|
||||
"""Agent recalls its own memories mid-simulation.
|
||||
|
||||
Searches by run_id to match the scope used during add().
|
||||
"""
|
||||
results = self.memory.search(
|
||||
query,
|
||||
run_id=agent_id,
|
||||
limit=5
|
||||
)
|
||||
return {
|
||||
"memories": [r["memory"] for r in results.get("results", [])],
|
||||
"relations": results.get("relations", []),
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Stage 4: Report Generation — Semantic + Graph Retrieval
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def quick_search(self, query: str, limit: int = 10) -> dict:
|
||||
"""Semantic search + graph relations across all agents.
|
||||
|
||||
MiroFish equivalent: ZepToolsService.quick_search()
|
||||
Returns both vector-matched memories and related graph triples.
|
||||
"""
|
||||
results = self.memory.search(
|
||||
query,
|
||||
user_id=self.project_id,
|
||||
limit=limit
|
||||
)
|
||||
return {
|
||||
"memories": [r["memory"] for r in results.get("results", [])],
|
||||
"relations": results.get("relations", []),
|
||||
}
|
||||
|
||||
def panorama_search(self) -> dict:
|
||||
"""Retrieve all memories + all graph relations.
|
||||
|
||||
MiroFish equivalent: ZepToolsService.panorama_search()
|
||||
Returns the complete knowledge state for report generation.
|
||||
"""
|
||||
results = self.memory.get_all(user_id=self.project_id)
|
||||
return {
|
||||
"memories": [r["memory"] for r in results.get("results", [])],
|
||||
"relations": results.get("relations", []),
|
||||
}
|
||||
|
||||
def agent_search(self, agent_id: str, query: str, limit: int = 10) -> dict:
|
||||
"""Search within a single agent's memory space."""
|
||||
results = self.memory.search(
|
||||
query,
|
||||
run_id=agent_id,
|
||||
limit=limit
|
||||
)
|
||||
return {
|
||||
"memories": [r["memory"] for r in results.get("results", [])],
|
||||
"relations": results.get("relations", []),
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Cleanup
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def cleanup(self):
|
||||
"""Delete all memories and graph data for this simulation."""
|
||||
self.memory.delete_all(user_id=self.project_id)
|
||||
print(f" Cleaned up all memories for {self.project_id}")
|
||||
|
||||
|
||||
# ======================================================================
|
||||
# Run the full 5-stage pipeline
|
||||
# ======================================================================
|
||||
|
||||
def main():
|
||||
project_id = f"mirofish_housing_{int(time.time())}"
|
||||
config = build_config()
|
||||
sim = MiroFishSimulation(project_id=project_id, config=config)
|
||||
|
||||
# ==================================================================
|
||||
# STAGE 1: Graph Building — Ingest seed documents
|
||||
# ==================================================================
|
||||
print("=" * 60)
|
||||
print("STAGE 1: Graph Building")
|
||||
print("=" * 60)
|
||||
|
||||
sim.ingest_documents([
|
||||
"The city council proposed a new zoning reform allowing higher "
|
||||
"density housing in suburban areas. Mayor Chen expressed strong "
|
||||
"support, citing a 40% housing shortage affecting young professionals. "
|
||||
"The reform would allow buildings up to 8 stories in previously "
|
||||
"restricted 3-story zones.",
|
||||
|
||||
"Local homeowners association president Wang opposes the reform, "
|
||||
"arguing it will decrease property values by 15-20%. The association "
|
||||
"represents 5,000 homeowners in the affected districts. Wang has "
|
||||
"organized three community meetings and collected 2,000 signatures.",
|
||||
|
||||
"Professor Li from Beijing University published research showing "
|
||||
"similar reforms in Shenzhen led to 15% price drops in existing "
|
||||
"homes but created 30% more affordable housing units within 3 years. "
|
||||
"The study covered 12 districts and 50,000 housing units.",
|
||||
])
|
||||
|
||||
# ==================================================================
|
||||
# STAGE 2: Environment Setup — Enrich agent profiles
|
||||
# ==================================================================
|
||||
print("\n" + "=" * 60)
|
||||
print("STAGE 2: Environment Setup")
|
||||
print("=" * 60)
|
||||
|
||||
mayor_context = sim.enrich_agent_profile(
|
||||
"Mayor Chen",
|
||||
"Mayor Chen housing reform zoning policy"
|
||||
)
|
||||
wang_context = sim.enrich_agent_profile(
|
||||
"Wang",
|
||||
"Wang homeowner opposition property values petition"
|
||||
)
|
||||
li_context = sim.enrich_agent_profile(
|
||||
"Professor Li",
|
||||
"Professor Li research housing data Shenzhen"
|
||||
)
|
||||
|
||||
print("\n Example profile context for Mayor Chen:")
|
||||
for fact in mayor_context["facts"][:3]:
|
||||
print(f" Fact: {fact}")
|
||||
for rel in mayor_context["relations"][:3]:
|
||||
src = rel.get("source", "?")
|
||||
edge = rel.get("relationship", "?")
|
||||
dst = rel.get("destination", rel.get("target", "?"))
|
||||
print(f" Relation: {src} --[{edge}]--> {dst}")
|
||||
|
||||
# ==================================================================
|
||||
# STAGE 3: Simulation — Run agent interactions
|
||||
# ==================================================================
|
||||
print("\n" + "=" * 60)
|
||||
print("STAGE 3: Simulation")
|
||||
print("=" * 60)
|
||||
|
||||
# Round 1: Opening statements
|
||||
sim.run_round(1, [
|
||||
("mayor_chen", "Mayor Chen", "CREATE_POST",
|
||||
"This reform will create 10,000 new housing units by 2028. "
|
||||
"Young families deserve affordable homes. #HousingForAll",
|
||||
"twitter"),
|
||||
|
||||
("wang_homeowner", "Wang", "CREATE_POST",
|
||||
"Our property values will plummet! The council ignores the "
|
||||
"voices of 5,000 homeowners. #StopTheReform",
|
||||
"twitter"),
|
||||
|
||||
("prof_li", "Professor Li", "CREATE_POST",
|
||||
"New analysis: Shenzhen zoning data shows net positive outcomes "
|
||||
"after 3 years. Short-term pain, long-term gain for housing equity.",
|
||||
"twitter"),
|
||||
])
|
||||
|
||||
# Round 2: Debate and interaction
|
||||
sim.run_round(2, [
|
||||
("wang_homeowner", "Wang", "CREATE_COMMENT",
|
||||
"Replied to Professor Li: 'Shenzhen is a tier-1 city with "
|
||||
"completely different dynamics. Your comparison is misleading.'",
|
||||
"twitter"),
|
||||
|
||||
("mayor_chen", "Mayor Chen", "LIKE_POST",
|
||||
"Liked Professor Li's post about Shenzhen housing data.",
|
||||
"twitter"),
|
||||
|
||||
("prof_li", "Professor Li", "CREATE_COMMENT",
|
||||
"Replied to Wang: 'The methodology controls for city tier "
|
||||
"and population density. I invite you to review the full dataset.'",
|
||||
"twitter"),
|
||||
|
||||
("mayor_chen", "Mayor Chen", "CREATE_POST",
|
||||
"Data from @ProfLi confirms what we've been saying: zoning "
|
||||
"reform works. Let's move forward with evidence, not fear.",
|
||||
"twitter"),
|
||||
])
|
||||
|
||||
# Round 3: Escalation and platform expansion
|
||||
sim.run_round(3, [
|
||||
("wang_homeowner", "Wang", "CREATE_POST",
|
||||
"Filing formal petition with 3,000 signatures against the "
|
||||
"zoning reform. Council meeting next Tuesday. All homeowners "
|
||||
"must attend!",
|
||||
"reddit"),
|
||||
|
||||
("mayor_chen", "Mayor Chen", "CREATE_POST",
|
||||
"Announcing public town hall on zoning reform this Saturday. "
|
||||
"All voices welcome. Data-driven decisions benefit everyone.",
|
||||
"twitter"),
|
||||
|
||||
("prof_li", "Professor Li", "CREATE_POST",
|
||||
"Published full dataset and methodology on my university page. "
|
||||
"Transparency is essential for informed public debate.",
|
||||
"twitter"),
|
||||
|
||||
("wang_homeowner", "Wang", "FOLLOW",
|
||||
"Followed @MayorChen to monitor policy updates.",
|
||||
"twitter"),
|
||||
])
|
||||
|
||||
# Mid-simulation: agent recalls own memory + graph
|
||||
print("\n Mid-simulation recall for Mayor Chen:")
|
||||
mayor_recall = sim.recall_agent_memory(
|
||||
"mayor_chen",
|
||||
"What positions have I taken on housing reform?"
|
||||
)
|
||||
for mem in mayor_recall["memories"]:
|
||||
print(f" Memory: {mem}")
|
||||
for rel in mayor_recall["relations"][:3]:
|
||||
src = rel.get("source", "?")
|
||||
edge = rel.get("relationship", "?")
|
||||
dst = rel.get("destination", rel.get("target", "?"))
|
||||
print(f" Relation: {src} --[{edge}]--> {dst}")
|
||||
|
||||
# ==================================================================
|
||||
# STAGE 4: Report Generation — Retrieve memories + graph for analysis
|
||||
# ==================================================================
|
||||
print("\n" + "=" * 60)
|
||||
print("STAGE 4: Report Generation")
|
||||
print("=" * 60)
|
||||
|
||||
# Quick search: targeted query
|
||||
print("\n Quick Search: 'opposition to housing reform'")
|
||||
opposition = sim.quick_search("opposition to housing reform", limit=5)
|
||||
for mem in opposition["memories"]:
|
||||
print(f" Memory: {mem}")
|
||||
for rel in opposition["relations"][:3]:
|
||||
src = rel.get("source", "?")
|
||||
edge = rel.get("relationship", "?")
|
||||
dst = rel.get("destination", rel.get("target", "?"))
|
||||
print(f" Relation: {src} --[{edge}]--> {dst}")
|
||||
|
||||
# Agent-specific search
|
||||
print("\n Agent Search: Wang's activities")
|
||||
wang_activities = sim.agent_search("wang_homeowner", "all actions and statements")
|
||||
for mem in wang_activities["memories"]:
|
||||
print(f" Memory: {mem}")
|
||||
|
||||
# Panorama: full overview
|
||||
print("\n Panorama Search: all memories + relations")
|
||||
panorama = sim.panorama_search()
|
||||
print(f" Total memories: {len(panorama['memories'])}")
|
||||
print(f" Total relations: {len(panorama['relations'])}")
|
||||
for mem in panorama["memories"][:5]:
|
||||
print(f" Memory: {mem}")
|
||||
if len(panorama["memories"]) > 5:
|
||||
print(f" ... and {len(panorama['memories']) - 5} more")
|
||||
for rel in panorama["relations"][:5]:
|
||||
src = rel.get("source", "?")
|
||||
edge = rel.get("relationship", "?")
|
||||
dst = rel.get("destination", rel.get("target", "?"))
|
||||
print(f" Relation: {src} --[{edge}]--> {dst}")
|
||||
|
||||
# ==================================================================
|
||||
# STAGE 5: Deep Interaction — Post-simulation queries
|
||||
# ==================================================================
|
||||
print("\n" + "=" * 60)
|
||||
print("STAGE 5: Deep Interaction")
|
||||
print("=" * 60)
|
||||
|
||||
queries = [
|
||||
"How did the debate evolve across the three rounds?",
|
||||
"What evidence was cited by each side?",
|
||||
"Who supports and who opposes the reform?",
|
||||
]
|
||||
|
||||
for query in queries:
|
||||
print(f"\n Query: '{query}'")
|
||||
results = sim.quick_search(query, limit=3)
|
||||
for mem in results["memories"][:2]:
|
||||
print(f" Memory: {mem}")
|
||||
for rel in results["relations"][:2]:
|
||||
src = rel.get("source", rel.get("source_node", "?"))
|
||||
edge = rel.get("relationship", rel.get("relation", "?"))
|
||||
dst = rel.get("destination", rel.get("destination_node", "?"))
|
||||
print(f" Relation: {src} --[{edge}]--> {dst}")
|
||||
|
||||
# ==================================================================
|
||||
# Summary
|
||||
# ==================================================================
|
||||
print("\n" + "=" * 60)
|
||||
print("SIMULATION COMPLETE")
|
||||
print("=" * 60)
|
||||
print(f" Project ID: {project_id}")
|
||||
print(f" Documents ingested: {sim.stats['documents_ingested']}")
|
||||
print(f" Activities tracked: {sim.stats['activities_recorded']}")
|
||||
print(f" Rounds completed: {sim.stats['rounds_completed']}")
|
||||
print(f" Total memories: {len(panorama['memories'])}")
|
||||
print(f" Total relations: {len(panorama['relations'])}")
|
||||
|
||||
# Cleanup (uncomment to delete all memories + graph data)
|
||||
# sim.cleanup()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("MiroFish Swarm Prediction Simulation powered by Mem0 Graph Memory\n")
|
||||
main()
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### Graph Memory: The Right Fit for MiroFish
|
||||
|
||||
MiroFish's entire pipeline revolves around a **knowledge graph** — it extracts entities from documents, builds relationships, and queries the graph throughout simulation and reporting. Mem0's Graph Memory provides the same capabilities:
|
||||
|
||||
| MiroFish needs | Zep Cloud | Mem0 Graph Memory |
|
||||
|---|---|---|
|
||||
| **Entity extraction** | Built-in via Zep API | Automatic via LLM extraction |
|
||||
| **Relationship mining** | Graph edges | `(source) --[relationship]--> (destination)` triples |
|
||||
| **Semantic + keyword search** | Semantic + BM25 | Vector similarity + graph relation retrieval |
|
||||
| **Graph traversal** | Node/edge queries | `relations` array in search results |
|
||||
| **Per-agent isolation** | Single shared graph in MiroFish | Native `run_id` scoping |
|
||||
| **Self-hosting** | No (cloud only) | Yes — Neo4j, Memgraph, Kuzu, Neptune |
|
||||
| **Node/memory limits** | Capped on free tier | Unlimited (self-hosted) |
|
||||
|
||||
### How search() Returns Both Memories and Relations
|
||||
|
||||
When Graph Memory is enabled, every `search()` call returns two arrays:
|
||||
|
||||
```python
|
||||
results = memory.search("housing reform", user_id="my_sim")
|
||||
|
||||
# Vector-matched memories (ordered by similarity)
|
||||
results["results"] # [{"memory": "...", "score": 0.85, ...}, ...]
|
||||
|
||||
# Graph relations connected to query entities
|
||||
results["relations"] # [{"source": "mayor_chen", "relationship": "supports", "destination": "zoning_reform"}, ...]
|
||||
```
|
||||
|
||||
This is what makes Mem0 Graph Memory a natural replacement for Zep — you get semantic search AND structured graph data in a single call.
|
||||
|
||||
### Per-Agent Memory Isolation
|
||||
|
||||
`user_id` scopes the simulation project. `run_id` tags individual agent actions at storage time (we use `run_id` instead of `agent_id` since no assistant memories are involved). Searches use `user_id` for project-wide retrieval:
|
||||
|
||||
```python
|
||||
# Store project-level memories (seed documents)
|
||||
memory.add(
|
||||
[{"role": "user", "content": "Mayor Chen supports the zoning reform."}],
|
||||
user_id="my_sim"
|
||||
)
|
||||
|
||||
# Store agent-specific memories (simulation actions)
|
||||
memory.add(
|
||||
[{"role": "user", "content": "Mayor Chen [CREATE_POST]: Reform works!"}],
|
||||
run_id="mayor_chen"
|
||||
)
|
||||
|
||||
# Search project-level memories (seed docs)
|
||||
memory.search("housing reform", user_id="my_sim")
|
||||
|
||||
# Search agent-specific memories (actions stored with run_id)
|
||||
memory.search("housing reform", run_id="mayor_chen")
|
||||
|
||||
# Get all project-level memories + graph relations
|
||||
memory.get_all(user_id="my_sim")
|
||||
```
|
||||
|
||||
<Note>
|
||||
Use `user_id` for project-level data (seed documents) and `run_id` for agent actions — both for `add()` and `search()`. Always match the scope: if you `add()` with `run_id`, `search()` with `run_id`. Use the message list format `[{"role": "user", "content": "..."}]` for all `add()` calls — it works on both OSS and Cloud.
|
||||
</Note>
|
||||
|
||||
### Stage Mapping
|
||||
|
||||
| MiroFish Stage | What Happens | Mem0 Graph Memory Call |
|
||||
|---|---|---|
|
||||
| **1. Graph Building** | Ingest docs, extract entities | `memory.add(doc, user_id=project)` — entities/relations extracted automatically |
|
||||
| **2. Environment Setup** | Enrich agent personas from graph | `memory.search(query, user_id=project)` — returns facts + relations |
|
||||
| **3. Simulation** | Track per-agent actions | `memory.add(messages, run_id=agent)` |
|
||||
| **3. Simulation** | Mid-round recall | `memory.search(query, run_id=agent)` |
|
||||
| **4. Report Generation** | Targeted analysis | `memory.search(query, user_id=project)` — memories + graph |
|
||||
| **4. Report Generation** | Full overview | `memory.get_all(user_id=project)` — all memories + all relations |
|
||||
| **5. Deep Interaction** | Follow-up queries | `memory.search(query, user_id=project)` |
|
||||
|
||||
### Zep-to-Mem0 Migration Reference
|
||||
|
||||
For developers replacing MiroFish's Zep integration. Note that Mem0 Graph Memory covers the core graph operations but some Zep features have no direct equivalent — see caveats below.
|
||||
|
||||
| MiroFish Service | Zep Call | Mem0 Graph Memory Equivalent | Caveat |
|
||||
|---|---|---|---|
|
||||
| GraphBuilderService | `client.graph.create()` | Implicit on first `memory.add()` | |
|
||||
| GraphBuilderService | `client.graph.set_ontology()` | `custom_prompt` in graph_store config | Freeform text, not a typed schema like Zep's `EntityModel`/`EdgeModel` |
|
||||
| GraphBuilderService | `client.graph.add_batch(episodes)` | `memory.add()` per chunk | No batch API — call per chunk |
|
||||
| GraphBuilderService | `client.graph.episode.get(uuid)` | Not needed (add is synchronous in OSS) | |
|
||||
| GraphBuilderService | `client.graph.delete(id)` | `memory.delete_all(user_id=...)` | |
|
||||
| ZepEntityReader | `client.graph.node.get_by_graph_id()` | `memory.get_all(user_id=...)` → `relations` | |
|
||||
| ZepEntityReader | `client.graph.node.get(uuid)` | `memory.search(entity_name, user_id=...)` | Semantic search, not exact ID lookup |
|
||||
| ZepEntityReader | `client.graph.node.get_entity_edges()` | `memory.search(entity_name, user_id=...)` → `relations` | Returns all matching relations, not edges for a specific node |
|
||||
| ZepGraphMemoryUpdater | `client.graph.add(type="text")` | `memory.add(messages, run_id=...)` | No batch buffering or retry — implement in your wrapper |
|
||||
| ZepToolsService | `search_graph(query, scope)` | `memory.search(query, user_id=...)` → memories + relations | |
|
||||
| ZepToolsService | `get_entities()` | `memory.get_all(user_id=...)` → `relations` | |
|
||||
| ZepToolsService | Panorama (all nodes + edges) | `memory.get_all(user_id=...)` | No temporal fact separation (active vs historical) |
|
||||
| ZepToolsService | InsightForge (multi-query decomposition) | Not available | Implement LLM-driven sub-query decomposition in your own ReportAgent |
|
||||
| OasisProfileGenerator | `client.graph.search()` | `memory.search(query, user_id=...)` | |
|
||||
|
||||
<Note>
|
||||
**What Mem0 Graph Memory does not cover**: Zep's typed ontology schemas (`EntityModel`, `EdgeModel`), temporal fact lifecycle (`valid_at`/`invalid_at`/`expired_at`), single-node-by-ID lookup, and InsightForge's multi-query decomposition. For InsightForge-like functionality, implement sub-query logic in your own ReportAgent using `memory.search()` as the retrieval primitive.
|
||||
</Note>
|
||||
|
||||
### Custom Extraction Prompts
|
||||
|
||||
Guide what entities and relationships Mem0 extracts — analogous to (but less structured than) Zep's `set_ontology()`:
|
||||
|
||||
```python
|
||||
config = {
|
||||
"graph_store": {
|
||||
"provider": "neo4j",
|
||||
"config": {"url": "...", "username": "...", "password": "..."},
|
||||
"custom_prompt": (
|
||||
"Extract all people, organizations, policies, locations, "
|
||||
"and their relationships. Capture support/opposition stances, "
|
||||
"affiliations, and quantitative claims."
|
||||
),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Action Types
|
||||
|
||||
MiroFish's OASIS engine produces these agent action types. Format them as natural language when storing. Skip `DO_NOTHING` actions (no memory value). `TREND` and `REFRESH` are Reddit-only discovery actions — store if you want to track browsing behavior.
|
||||
|
||||
| Action Type | Platform | Example Memory Content |
|
||||
|---|---|---|
|
||||
| `CREATE_POST` | Both | `"Mayor Chen [CREATE_POST]: This reform will create 10,000 units"` |
|
||||
| `CREATE_COMMENT` | Reddit | `"Wang [CREATE_COMMENT]: Replied to Prof Li: 'Your data is misleading'"` |
|
||||
| `LIKE_POST` | Both | `"Mayor Chen [LIKE_POST]: Liked Prof Li's post about Shenzhen data"` |
|
||||
| `REPOST` | Twitter | `"Prof Li [REPOST]: Reposted Mayor Chen's town hall announcement"` |
|
||||
| `FOLLOW` | Both | `"Wang [FOLLOW]: Followed @MayorChen"` |
|
||||
| `QUOTE_POST` | Twitter | `"Mayor Chen [QUOTE_POST]: 'Data confirms reform works' quoting Prof Li"` |
|
||||
| `DISLIKE_POST` | Reddit | `"Wang [DISLIKE_POST]: Downvoted Mayor Chen's reform post"` |
|
||||
| `TREND` | Reddit | `"Prof Li [TREND]: Browsed trending topics"` |
|
||||
| `DO_NOTHING` | Both | Skip — no memory value |
|
||||
|
||||
## Running the Example
|
||||
|
||||
```bash
|
||||
# Option A: Neo4j (production)
|
||||
export OPENAI_API_KEY="sk-..."
|
||||
export NEO4J_URL="neo4j://localhost:7687"
|
||||
export NEO4J_USERNAME="neo4j"
|
||||
export NEO4J_PASSWORD="password"
|
||||
python mirofish_swarm_memory.py
|
||||
|
||||
# Option B: Kuzu (zero dependencies, just need OpenAI key)
|
||||
export OPENAI_API_KEY="sk-..."
|
||||
python mirofish_swarm_memory.py # auto-detects missing NEO4J_URL, uses Kuzu
|
||||
```
|
||||
|
||||
<Note>
|
||||
Exact output varies as Mem0 automatically extracts and deduplicates entities. The specific relations and memory counts depend on LLM extraction quality.
|
||||
</Note>
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Unique `user_id` per simulation** — Use timestamps or UUIDs (e.g., `mirofish_housing_1742198400`) to prevent memory collisions between runs
|
||||
2. **Always set `run_id` for agent actions** — Per-agent isolation prevents memory cross-contamination between agents
|
||||
3. **Use `custom_prompt`** — Guide entity extraction to capture domain-specific relationships (people, policies, stances)
|
||||
4. **Format actions as natural language** — `"Mayor Chen [CREATE_POST]: content"` extracts better entities than raw JSON
|
||||
5. **Query relations for reports** — The `relations` array in search results gives structured `(source, relationship, destination)` triples for building analytical reports
|
||||
6. **Cleanup old simulations** — Call `delete_all(user_id=...)` when a simulation run is no longer needed
|
||||
|
||||
## Resources
|
||||
|
||||
- [MiroFish GitHub](https://github.com/666ghj/MiroFish) — Source code and setup guide
|
||||
- [MiroFish Documentation](https://deepwiki.com/666ghj/MiroFish) — Full framework docs
|
||||
- [Mem0 Graph Memory](/open-source/features/graph-memory) — Graph Memory documentation
|
||||
- [Mem0 Documentation](https://docs.mem0.ai/introduction) — Full API reference
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Graph Memory" icon="network-wired" href="/open-source/features/graph-memory">
|
||||
Full Graph Memory documentation with provider setup.
|
||||
</Card>
|
||||
<Card title="MiroFish GitHub" icon="fish" href="https://github.com/666ghj/MiroFish">
|
||||
MiroFish source code and setup guide.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -112,7 +112,7 @@ async def search_memory(
|
||||
query: The search query.
|
||||
"""
|
||||
user_id = context.context.user_id or "default_user"
|
||||
memories = await client.search(query, user_id=user_id)
|
||||
memories = await client.search(query, filters={"user_id": user_id})
|
||||
results = '\n'.join([result["memory"] for result in memories["results"]])
|
||||
return str(results)
|
||||
```
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
---
|
||||
title: Bedrock with Persistent Memory
|
||||
description: "Pair Mem0 with AWS Bedrock, OpenSearch, and Neptune for a managed stack."
|
||||
description: "Pair Mem0 with AWS Bedrock and OpenSearch for a managed stack."
|
||||
---
|
||||
|
||||
|
||||
This example demonstrates how to configure and use the `mem0ai` SDK with **AWS Bedrock**, **OpenSearch Service (AOSS)**, and **AWS Neptune Analytics** for persistent memory capabilities in Python.
|
||||
This example demonstrates how to configure and use the `mem0ai` SDK with **AWS Bedrock** and **OpenSearch Service (AOSS)** for persistent memory capabilities in Python.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the required dependencies to include the Amazon data stack, including **boto3**, **opensearch-py**, and **langchain-aws**:
|
||||
|
||||
```bash
|
||||
pip install "mem0ai[graph,extras]"
|
||||
pip install "mem0ai[extras]"
|
||||
```
|
||||
|
||||
## Environment Setup
|
||||
@@ -38,7 +38,6 @@ This sets up Mem0 with:
|
||||
- [AWS Bedrock for LLM](https://docs.mem0.ai/components/llms/models/aws_bedrock)
|
||||
- [AWS Bedrock for embeddings](https://docs.mem0.ai/components/embedders/models/aws_bedrock#aws-bedrock)
|
||||
- [OpenSearch as the vector store](https://docs.mem0.ai/components/vectordbs/dbs/opensearch)
|
||||
- [Graph Memory guide](https://docs.mem0.ai/open-source/features/graph-memory)
|
||||
|
||||
```python
|
||||
import boto3
|
||||
@@ -79,12 +78,6 @@ config = {
|
||||
"embedding_model_dims": 1024,
|
||||
}
|
||||
},
|
||||
"graph_store": {
|
||||
"provider": "neptune",
|
||||
"config": {
|
||||
"endpoint": f"neptune-graph://my-graph-identifier",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# Initialize the memory system
|
||||
@@ -93,8 +86,6 @@ m = Memory.from_config(config)
|
||||
|
||||
## Usage
|
||||
|
||||
Reference [Notebook example](https://github.com/mem0ai/mem0/blob/main/examples/graph-db-demo/neptune-example.ipynb)
|
||||
|
||||
### Add a memory
|
||||
|
||||
```python
|
||||
@@ -112,13 +103,13 @@ result = m.add(messages, user_id="alice", metadata={"category": "movie_recommend
|
||||
### Search a memory
|
||||
|
||||
```python
|
||||
relevant_memories = m.search(query, user_id="alice")
|
||||
relevant_memories = m.search(query, filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
### Get all memories
|
||||
|
||||
```python
|
||||
all_memories = m.get_all(user_id="alice")
|
||||
all_memories = m.get_all(filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
### Get a specific memory
|
||||
@@ -129,15 +120,12 @@ memory = m.get(memory_id)
|
||||
|
||||
## Conclusion
|
||||
|
||||
With Mem0 and AWS services like Bedrock, OpenSearch, and Neptune Analytics, you can build intelligent AI companions that remember, adapt, and personalize their responses over time. This makes them ideal for long-term assistants, tutors, or support bots with persistent memory and natural conversation abilities.
|
||||
With Mem0 and AWS services like Bedrock and OpenSearch, you can build intelligent AI companions that remember, adapt, and personalize their responses over time. This makes them ideal for long-term assistants, tutors, or support bots with persistent memory and natural conversation abilities.
|
||||
|
||||
---
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Neptune Analytics with Mem0" icon="database" href="/cookbooks/integrations/neptune-analytics">
|
||||
Explore graph-based memory storage with AWS Neptune Analytics.
|
||||
</Card>
|
||||
<Card title="Graph Memory Features" icon="sitemap" href="/platform/features/graph-memory">
|
||||
Learn how to leverage knowledge graphs for entity relationships.
|
||||
<Card title="Memory Evaluation" icon="chart-line" href="/core-concepts/memory-evaluation">
|
||||
Understand how Mem0's memory system is benchmarked and evaluated.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
---
|
||||
title: Graph Memory on Neptune
|
||||
description: "Combine Mem0 graph memory with AWS Neptune Analytics and Bedrock."
|
||||
---
|
||||
|
||||
|
||||
This example demonstrates how to configure and use the `mem0ai` SDK with **AWS Bedrock** and **AWS Neptune Analytics** for persistent memory capabilities in Python.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the required dependencies to include the Amazon data stack, including **boto3** and **langchain-aws**:
|
||||
|
||||
```bash
|
||||
pip install "mem0ai[graph,extras]"
|
||||
```
|
||||
|
||||
## Environment Setup
|
||||
|
||||
Set your AWS environment variables:
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
# Set these in your environment or notebook
|
||||
os.environ['AWS_REGION'] = 'us-west-2'
|
||||
os.environ['AWS_ACCESS_KEY_ID'] = 'AK00000000000000000'
|
||||
os.environ['AWS_SECRET_ACCESS_KEY'] = 'AS00000000000000000'
|
||||
|
||||
# Confirm they are set
|
||||
print(os.environ['AWS_REGION'])
|
||||
print(os.environ['AWS_ACCESS_KEY_ID'])
|
||||
print(os.environ['AWS_SECRET_ACCESS_KEY'])
|
||||
```
|
||||
|
||||
## Configuration and Usage
|
||||
|
||||
This sets up Mem0 with:
|
||||
- [AWS Bedrock for LLM](https://docs.mem0.ai/components/llms/models/aws_bedrock)
|
||||
- [AWS Bedrock for embeddings](https://docs.mem0.ai/components/embedders/models/aws_bedrock#aws-bedrock)
|
||||
- [Neptune Analytics as the vector store](https://docs.mem0.ai/components/vectordbs/dbs/neptune_analytics)
|
||||
- [Graph Memory guide](https://docs.mem0.ai/open-source/features/graph-memory).
|
||||
|
||||
```python
|
||||
import boto3
|
||||
from mem0.memory.main import Memory
|
||||
|
||||
region = 'us-west-2'
|
||||
neptune_analytics_endpoint = 'neptune-graph://my-graph-identifier'
|
||||
|
||||
config = {
|
||||
"embedder": {
|
||||
"provider": "aws_bedrock",
|
||||
"config": {
|
||||
"model": "amazon.titan-embed-text-v2:0"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
"provider": "aws_bedrock",
|
||||
"config": {
|
||||
"model": "us.anthropic.claude-3-7-sonnet-20250219-v1:0",
|
||||
"temperature": 0.1,
|
||||
"max_tokens": 2000
|
||||
}
|
||||
},
|
||||
"vector_store": {
|
||||
"provider": "neptune",
|
||||
"config": {
|
||||
"collection_name": "mem0",
|
||||
"endpoint": neptune_analytics_endpoint,
|
||||
},
|
||||
},
|
||||
"graph_store": {
|
||||
"provider": "neptune",
|
||||
"config": {
|
||||
"endpoint": neptune_analytics_endpoint,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# Initialize the memory system
|
||||
m = Memory.from_config(config)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Reference [Notebook example](https://github.com/mem0ai/mem0/blob/main/examples/graph-db-demo/neptune-example.ipynb)
|
||||
|
||||
#### Add a memory:
|
||||
|
||||
```python
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
|
||||
{"role": "assistant", "content": "How about a thriller movies? They can be quite engaging."},
|
||||
{"role": "user", "content": "I'm not a big fan of thriller movies but I love sci-fi movies."},
|
||||
{"role": "assistant", "content": "Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future."}
|
||||
]
|
||||
|
||||
# Store inferred memories (default behavior)
|
||||
result = m.add(messages, user_id="alice", metadata={"category": "movie_recommendations"})
|
||||
```
|
||||
|
||||
#### Search a memory:
|
||||
```python
|
||||
relevant_memories = m.search(query, user_id="alice")
|
||||
```
|
||||
|
||||
#### Get all memories:
|
||||
```python
|
||||
all_memories = m.get_all(user_id="alice")
|
||||
```
|
||||
|
||||
#### Get a specific memory:
|
||||
```python
|
||||
memory = m.get(memory_id)
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
With Mem0 and AWS services like Bedrock and Neptune Analytics, you can build intelligent AI companions that remember, adapt, and personalize their responses over time. This makes them ideal for long-term assistants, tutors, or support bots with persistent memory and natural conversation abilities.
|
||||
|
||||
---
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="AWS Bedrock with Mem0" icon="aws" href="/cookbooks/integrations/aws-bedrock">
|
||||
Combine Neptune Analytics with AWS Bedrock for complete AWS stack.
|
||||
</Card>
|
||||
<Card title="Graph Memory Architecture" icon="sitemap" href="/cookbooks/essentials/choosing-memory-architecture-vector-vs-graph">
|
||||
Understand when to use graph vs vector memory for your use case.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -121,7 +121,7 @@ const carRecommendationTool = zodResponsesFunction({
|
||||
|
||||
// Use the tool in your OpenAI request
|
||||
const response = await openAIClient.responses.create({
|
||||
model: "gpt-4.1-nano-2025-04-14",
|
||||
model: "gpt-5-mini",
|
||||
tools: [{ type: "web_search_preview" }, carRecommendationTool],
|
||||
input: `${getMemoryString(relevantMemories)}\n${userInput}`,
|
||||
});
|
||||
@@ -133,7 +133,7 @@ Combine memory with web search for up-to-date recommendations:
|
||||
|
||||
```javascript
|
||||
const response = await openAIClient.responses.create({
|
||||
model: "gpt-4.1-nano-2025-04-14",
|
||||
model: "gpt-5-mini",
|
||||
tools: [{ type: "web_search_preview" }, carRecommendationTool],
|
||||
input: `${getMemoryString(relevantMemories)}\n${userInput}`,
|
||||
});
|
||||
@@ -204,7 +204,7 @@ async function main(memory = false) {
|
||||
}
|
||||
|
||||
const response = await openAIClient.responses.create({
|
||||
model: "gpt-4.1-nano-2025-04-14",
|
||||
model: "gpt-5-mini",
|
||||
tools: [{ type: "web_search_preview" }, tool],
|
||||
input: `${getMemoryString(relevantMemories)}\n${input}`,
|
||||
});
|
||||
|
||||
@@ -202,7 +202,7 @@ Preferences:
|
||||
]
|
||||
|
||||
response = openai.chat.completions.create(
|
||||
model="gpt-4.1-nano-2025-04-14",
|
||||
model="gpt-5-mini",
|
||||
messages=messages
|
||||
)
|
||||
clean_response = response.choices[0].message.content.strip()
|
||||
|
||||
@@ -79,7 +79,7 @@ class CustomerSupportAIAgent:
|
||||
:param user_id: Optional user ID to filter memories.
|
||||
:return: List of memories.
|
||||
"""
|
||||
return self.memory.get_all(user_id=user_id)
|
||||
return self.memory.get_all(filters={"user_id": user_id})
|
||||
|
||||
# Instantiate the CustomerSupportAIAgent
|
||||
support_agent = CustomerSupportAIAgent()
|
||||
|
||||
@@ -45,7 +45,7 @@ class CollaborativeAgent:
|
||||
|
||||
def brainstorm(self, prompt):
|
||||
# Get recent messages for context
|
||||
memories = self.mem.search(prompt, run_id=self.run_id, limit=5)["results"]
|
||||
memories = self.mem.search(prompt, filters={"run_id": self.run_id}, top_k=5)["results"]
|
||||
context = "\n".join(f"- {m['memory']} (by {m.get('actor_id', 'Unknown')})" for m in memories)
|
||||
client = OpenAI()
|
||||
messages = [
|
||||
@@ -53,14 +53,14 @@ class CollaborativeAgent:
|
||||
{"role": "user", "content": f"Prompt: {prompt}\nContext:\n{context}"}
|
||||
]
|
||||
reply = client.chat.completions.create(
|
||||
model="gpt-4.1-nano-2025-04-14",
|
||||
model="gpt-5-mini",
|
||||
messages=messages
|
||||
).choices[0].message.content.strip()
|
||||
self.add_message("assistant", "assistant", reply)
|
||||
return reply
|
||||
|
||||
def get_all_messages(self):
|
||||
return self.mem.get_all(run_id=self.run_id)["results"]
|
||||
return self.mem.get_all(filters={"run_id": self.run_id})["results"]
|
||||
|
||||
def print_sorted_by_time(self):
|
||||
messages = self.get_all_messages()
|
||||
|
||||
@@ -201,10 +201,7 @@ Here are some examples of how Mem0 can be integrated into various applications:
|
||||
>
|
||||
Persistent personality for Eliza agents.
|
||||
</Card>
|
||||
<Card title="Browser Extension Memory" icon="globe" href="/cookbooks/frameworks/chrome-extension">
|
||||
Universal memory layer for Chrome.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,354 @@
|
||||
---
|
||||
title: "Memory Evaluation"
|
||||
description: "Understand how Mem0's memory system is evaluated, benchmark results, and how to run evaluations on your own data."
|
||||
icon: "chart-bar"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
## Why Memory Evaluation Matters
|
||||
|
||||
Most AI agent memory systems retrieve information by maximizing context window size. That works on benchmarks but not in production, where every token adds cost. **Token efficiency** — achieving high accuracy with less context per query — is what separates benchmark performance from production viability.
|
||||
|
||||
The new Mem0 algorithm achieves competitive accuracy on LoCoMo, LongMemEval, and BEAM while averaging **under 7,000 tokens per retrieval call**. Full-context approaches on the same benchmarks routinely consume 25,000+ tokens per query.
|
||||
|
||||
Evaluating a memory system at scale comes down to three parameters: **accuracy** (what the benchmarks measure), **cost** (context tokens per query), and **performance** (latency). Optimizing one is easy. Balancing all three at scale is the actual problem.
|
||||
|
||||
Some benchmarks today — particularly smaller ones like LoCoMo and LongMemEval — can be materially improved by aggressive retrieval strategies, larger context windows, or frontier models. That does not necessarily mean the underlying memory system has gotten better. We evaluate under constraints that reflect how memory systems actually run in production: limited context windows and practical token budgets.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
Mem0's memory system operates across two phases — **extraction** (writing) and **retrieval** (reading) — with an entity linking layer connecting them.
|
||||
|
||||
### Memory Extraction (Distillation)
|
||||
|
||||
When new conversations arrive, the extraction pipeline processes them through five stages:
|
||||
|
||||
1. **Store New Memories** — Conversation enters the pipeline asynchronously (after the agent responds)
|
||||
2. **Context Lookup** — Find related existing memories to avoid duplicates
|
||||
3. **Distill Memories** — Single-pass LLM extraction produces ADD-only facts from input + context
|
||||
4. **Deduplicate + Embed** — Hash-based deduplication, then vectorize new memories
|
||||
5. **Entity Linking** — Identify entities (proper nouns, quoted text, compound noun phrases) and link them across memories
|
||||
|
||||
Memories are distributed across three storage layers, each tuned for a specific retrieval pattern:
|
||||
|
||||
| Store | Contents | Purpose |
|
||||
|---|---|---|
|
||||
| **Vector Database** | Memory text, embeddings, metadata (timestamps, hash, categories, attributed_to) | Primary fact storage + semantic retrieval |
|
||||
| **Entity Store** | Entities + embeddings + linked memory IDs | Entity-based retrieval boost |
|
||||
| **SQL Database** | History log (ADD events) + rolling message window | Audit trail + extraction dedup context |
|
||||
|
||||
<Info>
|
||||
The key architectural decision is **ADD-only extraction**. New facts are stored alongside old ones — nothing is overwritten or deleted. When information changes, both the old and new facts survive. This preserves temporal context and eliminates information loss from premature consolidation.
|
||||
</Info>
|
||||
|
||||
### Multi-Signal Retrieval
|
||||
|
||||
When a query arrives, the retrieval pipeline scores candidates across three signals in parallel:
|
||||
|
||||
1. **Semantic Search** — Vector similarity scoring against memory embeddings
|
||||
2. **Keyword Search** — Normalized term matching via BM25 with verb-form lemmatization
|
||||
3. **Entity Search** — Entity graph matching boosts memories linked to query entities
|
||||
|
||||
Results are fused via rank scoring into a final top-K set. Different query types lean on different signals:
|
||||
|
||||
| Query Type | Primary Signal | Example |
|
||||
|---|---|---|
|
||||
| Conceptual | Semantic | "What does the user think about remote work?" |
|
||||
| Factual/exact | BM25 keyword | "What meetings did I attend last week?" |
|
||||
| Entity-centric | Entity matching | "What do we know about Alice?" |
|
||||
| Temporal | Semantic + keyword | "When did the user first mention the project?" |
|
||||
|
||||
The combined score outperformed every individual signal across every category tested.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
### LoCoMo
|
||||
|
||||
[LoCoMo](https://github.com/snap-stanford/locomo) tests single-hop, multi-hop, open-domain, and temporal memory recall across conversational sessions.
|
||||
|
||||
| Category | Old Algorithm | New Algorithm | Delta |
|
||||
|---|---|---|---|
|
||||
| **Overall** | **71.4** | **91.6** | **+20.2** |
|
||||
| Single-hop | 76.6 | 92.3 | +15.7 |
|
||||
| Multi-hop | 70.2 | 93.3 | +23.1 |
|
||||
| Open-domain | 57.3 | 76.0 | +18.7 |
|
||||
| Temporal | 63.2 | 92.8 | +29.6 |
|
||||
|
||||
*Mean tokens: 6,956*
|
||||
|
||||
The two largest gains are **temporal queries (+29.6)** and **multi-hop reasoning (+23.1)**. Both categories directly test the ADD-only architecture (preserving temporal context) and entity linking (connecting facts across memories).
|
||||
|
||||
### LongMemEval
|
||||
|
||||
[LongMemEval](https://github.com/xiaowu0162/LongMemEval) evaluates memory across single-session and multi-session contexts, including knowledge updates and temporal reasoning.
|
||||
|
||||
| Category | Old Algorithm | New Algorithm | Delta |
|
||||
|---|---|---|---|
|
||||
| **Overall** | **67.8** | **93.4** | **+25.6** |
|
||||
| Single-session (user) | 94.3 | 97.1 | +2.8 |
|
||||
| Single-session (assistant) | 46.4 | 100.0 | +53.6 |
|
||||
| Single-session (preference) | 76.7 | 96.7 | +20.0 |
|
||||
| Knowledge update | 79.5 | 96.2 | +16.7 |
|
||||
| Temporal reasoning | 51.1 | 93.2 | +42.1 |
|
||||
| Multi-session | 70.7 | 86.5 | +15.8 |
|
||||
|
||||
*Mean tokens: 6,787*
|
||||
|
||||
The biggest gain is **single-session assistant (+53.6)** — the previous algorithm had a blind spot for agent-generated facts. The new algorithm treats them as first-class memories.
|
||||
|
||||
The **+42.1 on temporal reasoning** reflects the ADD-only architecture preserving chronological context that the previous UPDATE/DELETE model would destroy.
|
||||
|
||||
### BEAM
|
||||
|
||||
[BEAM](https://github.com/mem0ai/memory-benchmarks) evaluates memory systems at 1M and 10M token scales across ten task categories. It is the only public benchmark that operates at context volumes production AI agents actually encounter.
|
||||
|
||||
| Category | 1M | 10M |
|
||||
|---|---|---|
|
||||
| **Overall** | **64.1** | **48.6** |
|
||||
| preference_following | 88.3 | 90.4 |
|
||||
| instruction_following | 85.2 | 82.5 |
|
||||
| information_extraction | 70.0 | 56.3 |
|
||||
| knowledge_update | 65.0 | 75.0 |
|
||||
| multi_session_reasoning | 65.2 | 26.1 |
|
||||
| summarization | 63.5 | 46.9 |
|
||||
| temporal_reasoning | 61.8 | 16.3 |
|
||||
| event_ordering | 53.6 | 20.2 |
|
||||
| abstention | 52.5 | 40.0 |
|
||||
| contradiction_resolution | 35.7 | 32.5 |
|
||||
|
||||
*Mean tokens (1M): 6,719. Mean tokens (10M): 6,914.*
|
||||
|
||||
<Info>
|
||||
**BEAM is the most relevant benchmark here.** It operates at 1M and 10M token scales and cannot be solved by simply expanding the context window. The results at 10M reflect where memory systems actually stand at production context volumes. The system holds up well on preference following, instruction following, and knowledge updates at both scales. Weaker categories at 10M (temporal reasoning, event ordering, multi-session reasoning) are open problems across the field — they require higher-order representations of how events relate to each other across time, which is a primary focus of our ongoing research.
|
||||
</Info>
|
||||
|
||||
### Performance Summary
|
||||
|
||||
All results use a single-pass retrieval setup: one retrieval call, one answer, no agentic loops.
|
||||
|
||||
| Benchmark | Old Algorithm | New Algorithm | Average tokens / query |
|
||||
|---|---|---|---|
|
||||
| **LoCoMo** | 71.4 | **91.6** | 6,956 |
|
||||
| **LongMemEval** | 67.8 | **93.4** | 6,787 |
|
||||
| **BEAM (1M)** | — | **64.1** | 6,719 |
|
||||
| **BEAM (10M)** | — | **48.6** | 6,914 |
|
||||
|
||||
<Info>
|
||||
Scores reflect Mem0's managed platform, which includes proprietary optimizations not available in the open-source SDK. Open-source users should expect directionally similar gains but not identical numbers.
|
||||
</Info>
|
||||
|
||||
All benchmarks run on the same production-representative model stack. Scores carry a ±1 point confidence interval due to judge inconsistency.
|
||||
|
||||
## Running Evaluations
|
||||
|
||||
The full evaluation framework is [open-sourced](https://github.com/mem0ai/memory-benchmarks) so anyone can reproduce the numbers independently. It supports both Mem0 Cloud and self-hosted OSS backends.
|
||||
|
||||
### Setup
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Mem0 Cloud">
|
||||
```bash
|
||||
git clone https://github.com/mem0ai/memory-benchmarks.git
|
||||
cd memory-benchmarks
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Set your API keys
|
||||
export MEM0_API_KEY=m0-your-key
|
||||
export OPENAI_API_KEY=sk-your-key
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Mem0 OSS (Docker)">
|
||||
```bash
|
||||
git clone https://github.com/mem0ai/memory-benchmarks.git
|
||||
cd memory-benchmarks
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Copy and configure environment
|
||||
cp .env.example .env
|
||||
# Edit .env to add OPENAI_API_KEY
|
||||
|
||||
# Start local Mem0 server + Qdrant
|
||||
docker compose up -d
|
||||
# Mem0 server: http://localhost:8888
|
||||
# Qdrant: http://localhost:6333
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Running a Benchmark
|
||||
|
||||
Each benchmark is a Python module with its own runner ([source code](https://github.com/mem0ai/memory-benchmarks/tree/main/benchmarks)). All share common CLI options:
|
||||
|
||||
| Option | Default | Description |
|
||||
|---|---|---|
|
||||
| `--project-name` | (required) | Run identifier for tracking results |
|
||||
| `--backend` | `oss` | `oss` (self-hosted) or `cloud` (Mem0 Platform) |
|
||||
| `--mem0-api-key` | — | Mem0 API key (required for `cloud` backend) |
|
||||
| `--mem0-host` | `http://localhost:8888` | Mem0 server URL (for `oss` backend) |
|
||||
| `--top-k` | `200` | Number of memories to retrieve per query |
|
||||
| `--top-k-cutoffs` | `10,20,50,200` | Evaluate accuracy at multiple retrieval depths (BEAM default: `100`) |
|
||||
| `--answerer-model` | *(varies)* | LLM for generating answers from retrieved memories |
|
||||
| `--judge-model` | *(varies)* | LLM for judging answer correctness |
|
||||
| `--provider` | `openai` | LLM provider: `openai`, `anthropic`, `azure` |
|
||||
| `--judge-provider` | (same as `--provider`) | Override provider for the judge model |
|
||||
| `--max-workers` | `10` | Parallel workers for evaluation |
|
||||
| `--predict-only` | — | Stop after search, skip answer + judge phases |
|
||||
| `--evaluate-only` | — | Skip ingest + search, evaluate existing results |
|
||||
| `--resume` | — | Resume from checkpoint (BEAM and LongMemEval; on by default for LongMemEval) |
|
||||
|
||||
<CodeGroup>
|
||||
```bash LoCoMo
|
||||
# ~300 questions across 10 conversations (fastest benchmark)
|
||||
python -m benchmarks.locomo.run \
|
||||
--project-name my-eval \
|
||||
--backend cloud \
|
||||
--mem0-api-key $MEM0_API_KEY \
|
||||
--top-k 200
|
||||
|
||||
# Self-hosted
|
||||
python -m benchmarks.locomo.run \
|
||||
--project-name my-eval \
|
||||
--top-k 200
|
||||
```
|
||||
|
||||
```bash LongMemEval
|
||||
# 500 questions across 6 categories
|
||||
python -m benchmarks.longmemeval.run \
|
||||
--project-name my-eval \
|
||||
--backend cloud \
|
||||
--mem0-api-key $MEM0_API_KEY \
|
||||
--all-questions \
|
||||
--top-k 200
|
||||
|
||||
# Self-hosted
|
||||
python -m benchmarks.longmemeval.run \
|
||||
--project-name my-eval \
|
||||
--all-questions \
|
||||
--top-k 200
|
||||
```
|
||||
|
||||
```bash BEAM
|
||||
# 1M token scale (100 conversations)
|
||||
python -m benchmarks.beam.run \
|
||||
--project-name my-eval \
|
||||
--backend cloud \
|
||||
--mem0-api-key $MEM0_API_KEY \
|
||||
--chat-sizes 1M \
|
||||
--conversations 0-99 \
|
||||
--top-k 200
|
||||
|
||||
# 10M token scale
|
||||
python -m benchmarks.beam.run \
|
||||
--project-name my-eval \
|
||||
--backend cloud \
|
||||
--mem0-api-key $MEM0_API_KEY \
|
||||
--chat-sizes 10M \
|
||||
--conversations 0-99 \
|
||||
--top-k 200
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Custom Model Configuration
|
||||
|
||||
To run evaluations with custom models (Azure OpenAI, Ollama, etc.), copy one of the provided configs:
|
||||
|
||||
```bash
|
||||
# Available configs: openai.yaml, azure-openai.yaml, ollama.yaml
|
||||
cp configs/azure-openai.yaml mem0-config.yaml
|
||||
# Edit mem0-config.yaml with your model details
|
||||
|
||||
# Uncomment the volume mount in docker-compose.yml, then restart:
|
||||
docker compose down && docker compose up -d
|
||||
```
|
||||
|
||||
### Viewing Results
|
||||
|
||||
Results are saved to `results/[benchmark]/` and can be explored through the built-in web UI:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev -- -p 3001
|
||||
# Open http://localhost:3001
|
||||
```
|
||||
|
||||
The UI lets you browse per-question results, inspect retrieval details, and compare multiple runs.
|
||||
|
||||
### Result Format
|
||||
|
||||
Each evaluated question produces a structured result:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "locomo_q_001",
|
||||
"group": "temporal",
|
||||
"question": "When did the user first mention moving?",
|
||||
"ground_truth": "During the March 3rd conversation",
|
||||
"retrieval": {
|
||||
"search_query": "when did user mention moving",
|
||||
"search_results": ["..."],
|
||||
"search_latency_ms": 123.4,
|
||||
"total_results": 42
|
||||
},
|
||||
"generation": {
|
||||
"generated_answer": "The user first mentioned moving on March 3rd",
|
||||
"model": "<answerer-model>",
|
||||
"prompt_tokens": 500,
|
||||
"completion_tokens": 100
|
||||
},
|
||||
"judgment": {
|
||||
"judgment": "CORRECT",
|
||||
"score": 0.85,
|
||||
"reason": "Answer correctly identifies the date",
|
||||
"model": "<judge-model>"
|
||||
},
|
||||
"cutoff_results": {
|
||||
"top_10": { "score": 0.75, "judgment": "CORRECT" },
|
||||
"top_50": { "score": 0.85, "judgment": "CORRECT" },
|
||||
"top_200": { "score": 0.90, "judgment": "CORRECT" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Interpreting Results
|
||||
|
||||
When evaluating memory systems, keep these considerations in mind:
|
||||
|
||||
- **Saturating a small benchmark is not the same as building a memory system that works at scale.** Small benchmarks can be brute-forced with aggressive retrieval and frontier models.
|
||||
- **Token efficiency matters as much as accuracy.** A system that scores 95% using 25K tokens per query isn't comparable to one scoring 90% using 7K tokens. Report mean tokens per query alongside scores.
|
||||
- **Compare at equal constraints.** Always compare systems using the same retrieval budget, the same model, and the same latency budget. A frontier model at maximum recall is not comparable to a smaller production-grade model at production-realistic retrieval depth.
|
||||
- **Watch for score ceiling effects.** Categories like "single-session user" are already near-saturated (97%+). Improvements in these categories are less meaningful than gains in harder categories like temporal reasoning or multi-session.
|
||||
- **BEAM at 10M is the real test.** Any system can look good at small scale. The 10M-token BEAM benchmark reveals whether the retrieval system actually scales.
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="What judge model is used for evaluation?">
|
||||
The judge model is configurable via `--judge-model` and `--judge-provider` flags. See the [evaluation repository](https://github.com/mem0ai/memory-benchmarks) for the current defaults. Scores carry a ±1 point confidence interval due to judge inconsistency.
|
||||
</Accordion>
|
||||
<Accordion title="Can I evaluate with a different extraction model?">
|
||||
Yes. For self-hosted, configure the extraction model in your `mem0-config.yaml` (see the `configs/` directory of the evaluation repo for provider-specific examples). For Mem0 Cloud, extraction uses the platform's default. Using a frontier model will likely produce higher scores but at higher cost and latency.
|
||||
</Accordion>
|
||||
<Accordion title="Why are BEAM scores lower than LoCoMo/LongMemEval?">
|
||||
BEAM operates at 1M and 10M token scales — orders of magnitude larger than LoCoMo or LongMemEval. At these scales, similar content appears multiple times across the window, and the memory system must surface the exact correct memory over many close matches. The scores reflect the genuine difficulty of the task, not a regression in the algorithm.
|
||||
</Accordion>
|
||||
<Accordion title="How do I contribute a new benchmark?">
|
||||
Open a pull request to the [memory-benchmarks repository](https://github.com/mem0ai/memory-benchmarks) with your benchmark implementation. See the repository README for the expected interface and format.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Resources
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Evaluation Repository" icon="github" href="https://github.com/mem0ai/memory-benchmarks">
|
||||
Open-source evaluation framework for reproducing all benchmark results
|
||||
</Card>
|
||||
<Card title="Research" icon="flask" href="https://mem0.ai/research">
|
||||
Published research papers and technical reports
|
||||
</Card>
|
||||
<Card title="Blog Post" icon="newspaper" href="https://mem0.ai/blog/new-algorithm">
|
||||
Detailed writeup of the new algorithm design and results
|
||||
</Card>
|
||||
<Card title="Platform Migration" icon="arrow-right" href="/migration/platform-v2-to-v3">
|
||||
Guide for migrating your Platform integration
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -85,7 +85,6 @@ const messages = [
|
||||
|
||||
await client.add(messages, {
|
||||
user_id: "alice",
|
||||
version: "v2",
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -173,7 +172,6 @@ For full list of supported fields, required formats, and advanced options, see t
|
||||
| Capability | Mem0 Platform | Mem0 OSS |
|
||||
| --- | --- | --- |
|
||||
| Conflict resolution | Automatic with dashboard visibility | SDK handles merges locally; you control storage |
|
||||
| Graph writes | Toggle per request (`enable_graph=True`) | Requires configuring a graph provider |
|
||||
| Rate limits | Managed quotas per workspace | Limited by your hardware and provider APIs |
|
||||
| Dashboard visibility | Yes — inspect memories visually | Inspect via CLI, logs, or custom UI |
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ import MemoryClient from 'mem0ai';
|
||||
|
||||
const client = new MemoryClient({ apiKey: "your-api-key" });
|
||||
|
||||
client.deleteAll({ user_id: "alice" })
|
||||
client.deleteAll({ userId: "alice" })
|
||||
.then(result => console.log(result))
|
||||
.catch(error => console.error(error));
|
||||
```
|
||||
@@ -162,12 +162,12 @@ import MemoryClient from 'mem0ai';
|
||||
const client = new MemoryClient({ apiKey: "your-api-key" });
|
||||
|
||||
// Delete all memories across every user in the project
|
||||
client.deleteAll({ user_id: "*" })
|
||||
client.deleteAll({ userId: "*" })
|
||||
.then(result => console.log(result))
|
||||
.catch(error => console.error(error));
|
||||
|
||||
// Full project wipe — all four filters must be explicitly set to "*"
|
||||
client.deleteAll({ user_id: "*", agent_id: "*", app_id: "*", run_id: "*" })
|
||||
client.deleteAll({ userId: "*", agentId: "*", appId: "*", runId: "*" })
|
||||
.then(result => console.log(result))
|
||||
.catch(error => console.error(error));
|
||||
```
|
||||
|
||||
@@ -56,7 +56,7 @@ Search converts your natural language question into a vector embedding, then fin
|
||||
client.search("What are Alice's hobbies?", filters={"user_id": "alice"})
|
||||
|
||||
# OSS
|
||||
m.search("What are Alice's hobbies?", user_id="alice")
|
||||
m.search("What are Alice's hobbies?", filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
<Tip>
|
||||
@@ -74,7 +74,7 @@ m.search("What are Alice's hobbies?", user_id="alice")
|
||||
|
||||
| Capability | Mem0 Platform | Mem0 OSS |
|
||||
| --- | --- | --- |
|
||||
| **user_id usage** | In `filters={"user_id": "alice"}` for search/get_all | As parameter `user_id="alice"` for all operations |
|
||||
| **Entity IDs on search / get_all** | Inside `filters={"user_id": "alice"}` | Inside `filters={"user_id": "alice"}` (aligned with Platform in v3 — top-level kwargs raise `ValueError`) |
|
||||
| **Filter syntax** | Logical operators (`AND`, `OR`, comparisons) with field-level access | Basic field filters, extend via Python hooks |
|
||||
| **Reranking** | Toggle `rerank=True` with managed reranker catalog | Requires configuring local or third-party rerankers |
|
||||
| **Thresholds** | Request-level configuration (`threshold`, `top_k`) | Controlled via SDK parameters |
|
||||
@@ -125,14 +125,13 @@ from mem0 import Memory
|
||||
|
||||
m = Memory()
|
||||
|
||||
# Simple search
|
||||
related_memories = m.search("Should I drink coffee or tea?", user_id="alice")
|
||||
# Simple search — entity IDs go in `filters`
|
||||
related_memories = m.search("Should I drink coffee or tea?", filters={"user_id": "alice"})
|
||||
|
||||
# Search with filters
|
||||
# Search with additional metadata filters (combine entity + metadata in the same dict)
|
||||
memories = m.search(
|
||||
"food preferences",
|
||||
user_id="alice",
|
||||
filters={"categories": {"contains": "diet"}}
|
||||
filters={"user_id": "alice", "categories": {"contains": "diet"}},
|
||||
)
|
||||
```
|
||||
|
||||
@@ -141,13 +140,14 @@ import { Memory } from 'mem0ai/oss';
|
||||
|
||||
const memory = new Memory();
|
||||
|
||||
// Simple search
|
||||
const relatedMemories = memory.search("Should I drink coffee or tea?", { userId: "alice" });
|
||||
// Simple search — entity IDs go inside `filters`
|
||||
const relatedMemories = memory.search("Should I drink coffee or tea?", {
|
||||
filters: { userId: "alice" },
|
||||
});
|
||||
|
||||
// Search with filters (if supported)
|
||||
// Combine entity + metadata filters in the same filters object
|
||||
const memories = memory.search("food preferences", {
|
||||
userId: "alice",
|
||||
filters: { categories: { contains: "diet" } }
|
||||
filters: { userId: "alice", categories: { contains: "diet" } },
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -176,8 +176,12 @@ client.search("query", filters={
|
||||
|
||||
*OSS:*
|
||||
```python
|
||||
# Get memories from a specific agent session
|
||||
m.search("query", user_id="alice", agent_id="chatbot", run_id="session-123")
|
||||
# Get memories from a specific agent session — entity IDs combined in filters
|
||||
m.search("query", filters={
|
||||
"user_id": "alice",
|
||||
"agent_id": "chatbot",
|
||||
"run_id": "session-123",
|
||||
})
|
||||
```
|
||||
|
||||
**Filter by Date Range:**
|
||||
|
||||
+77
-34
@@ -55,7 +55,8 @@
|
||||
"core-concepts/memory-operations/add",
|
||||
"core-concepts/memory-operations/search",
|
||||
"core-concepts/memory-operations/update",
|
||||
"core-concepts/memory-operations/delete"
|
||||
"core-concepts/memory-operations/delete",
|
||||
"core-concepts/memory-evaluation"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -70,7 +71,6 @@
|
||||
"platform/features/v2-memory-filters",
|
||||
"platform/features/entity-scoped-memory",
|
||||
"platform/features/async-client",
|
||||
"platform/features/async-mode-default-change",
|
||||
"platform/features/multimodal-support",
|
||||
"platform/features/custom-categories"
|
||||
]
|
||||
@@ -79,8 +79,6 @@
|
||||
"group": "Advanced Features",
|
||||
"icon": "bolt",
|
||||
"pages": [
|
||||
"platform/features/graph-memory",
|
||||
"platform/features/graph-threshold",
|
||||
"platform/features/advanced-retrieval",
|
||||
"platform/advanced-memory-operations",
|
||||
"platform/features/criteria-retrieval",
|
||||
@@ -94,8 +92,7 @@
|
||||
"pages": [
|
||||
"platform/features/direct-import",
|
||||
"platform/features/memory-export",
|
||||
"platform/features/timestamp",
|
||||
"platform/features/expiration-date"
|
||||
"platform/features/timestamp"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -121,9 +118,8 @@
|
||||
"group": "Migration Guide",
|
||||
"icon": "arrow-right",
|
||||
"pages": [
|
||||
"migration/platform-v2-to-v3",
|
||||
"migration/oss-to-platform",
|
||||
"migration/v0-to-v1",
|
||||
"migration/breaking-changes",
|
||||
"migration/api-changes"
|
||||
]
|
||||
},
|
||||
@@ -133,16 +129,6 @@
|
||||
"pages": [
|
||||
"platform/contribute"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Release Notes",
|
||||
"icon": "rocket",
|
||||
"pages": [
|
||||
"changelog/highlights",
|
||||
"changelog/sdk",
|
||||
"changelog/platform",
|
||||
"changelog/openclaw"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -177,13 +163,11 @@
|
||||
"icon": "server",
|
||||
"pages": [
|
||||
"open-source/features/overview",
|
||||
"open-source/features/graph-memory",
|
||||
"open-source/features/metadata-filtering",
|
||||
"open-source/features/reranker-search",
|
||||
"open-source/features/async-memory",
|
||||
"open-source/features/multimodal-support",
|
||||
"open-source/features/custom-fact-extraction-prompt",
|
||||
"open-source/features/custom-update-memory-prompt",
|
||||
"open-source/features/custom-instructions",
|
||||
"open-source/features/rest-api",
|
||||
"open-source/features/openai_compatibility"
|
||||
]
|
||||
@@ -310,6 +294,13 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Migration",
|
||||
"icon": "arrow-right",
|
||||
"pages": [
|
||||
"migration/oss-v2-to-v3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Community & Support",
|
||||
"icon": "users",
|
||||
@@ -376,7 +367,6 @@
|
||||
"cookbooks/integrations/mastra-agent",
|
||||
"cookbooks/integrations/healthcare-google-adk",
|
||||
"cookbooks/integrations/aws-bedrock",
|
||||
"cookbooks/integrations/neptune-analytics",
|
||||
"cookbooks/integrations/tavily-search"
|
||||
]
|
||||
},
|
||||
@@ -388,9 +378,7 @@
|
||||
"cookbooks/frameworks/llamaindex-multiagent",
|
||||
"cookbooks/frameworks/multimodal-retrieval",
|
||||
"cookbooks/frameworks/eliza-os-character",
|
||||
"cookbooks/frameworks/chrome-extension",
|
||||
"cookbooks/frameworks/gemini-3-with-mem0-mcp",
|
||||
"cookbooks/frameworks/mirofish-swarm-memory"
|
||||
"cookbooks/frameworks/gemini-3-with-mem0-mcp"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -562,6 +550,21 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Release Notes",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Release Notes",
|
||||
"icon": "rocket",
|
||||
"pages": [
|
||||
"changelog/highlights",
|
||||
"changelog/sdk",
|
||||
"changelog/platform",
|
||||
"changelog/openclaw"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -612,6 +615,30 @@
|
||||
]
|
||||
},
|
||||
"redirects": [
|
||||
{
|
||||
"source": "/migration/breaking-changes",
|
||||
"destination": "/"
|
||||
},
|
||||
{
|
||||
"source": "/migration/v0-to-v1",
|
||||
"destination": "/"
|
||||
},
|
||||
{
|
||||
"source": "/platform/features/expiration-date",
|
||||
"destination": "/"
|
||||
},
|
||||
{
|
||||
"source": "/platform/features/async-mode-default-change",
|
||||
"destination": "/"
|
||||
},
|
||||
{
|
||||
"source": "/open-source/features/custom-fact-extraction-prompt",
|
||||
"destination": "/open-source/features/custom-instructions"
|
||||
},
|
||||
{
|
||||
"source": "/platform/features/graph-memory",
|
||||
"destination": "/migration/oss-v2-to-v3"
|
||||
},
|
||||
{
|
||||
"source": "/changelog",
|
||||
"destination": "/changelog/highlights"
|
||||
@@ -710,11 +737,23 @@
|
||||
},
|
||||
{
|
||||
"source": "/examples/aws_neptune_analytics_hybrid_store",
|
||||
"destination": "/cookbooks/integrations/neptune-analytics"
|
||||
"destination": "/migration/oss-v2-to-v3"
|
||||
},
|
||||
{
|
||||
"source": "/examples/aws_neptune_analytics_hybrid_st",
|
||||
"destination": "/cookbooks/integrations/neptune-analytics"
|
||||
"destination": "/migration/oss-v2-to-v3"
|
||||
},
|
||||
{
|
||||
"source": "/cookbooks/integrations/neptune-analytics",
|
||||
"destination": "/migration/oss-v2-to-v3"
|
||||
},
|
||||
{
|
||||
"source": "/platform/features/graph-threshold",
|
||||
"destination": "/migration/platform-v2-to-v3"
|
||||
},
|
||||
{
|
||||
"source": "/open-source/features/custom-update-memory-prompt",
|
||||
"destination": "/open-source/features/custom-instructions"
|
||||
},
|
||||
{
|
||||
"source": "/examples/personalized-search-tavily-mem0",
|
||||
@@ -774,7 +813,11 @@
|
||||
},
|
||||
{
|
||||
"source": "/examples/chrome-extension",
|
||||
"destination": "/cookbooks/frameworks/chrome-extension"
|
||||
"destination": "/cookbooks/overview"
|
||||
},
|
||||
{
|
||||
"source": "/cookbooks/frameworks/chrome-extension",
|
||||
"destination": "/cookbooks/overview"
|
||||
},
|
||||
{
|
||||
"source": "/examples",
|
||||
@@ -782,11 +825,11 @@
|
||||
},
|
||||
{
|
||||
"source": "/open-source/graph_memory/overview",
|
||||
"destination": "/open-source/features/graph-memory"
|
||||
"destination": "/migration/oss-v2-to-v3"
|
||||
},
|
||||
{
|
||||
"source": "/open-source/graph_memory/features",
|
||||
"destination": "/open-source/features/graph-memory"
|
||||
"destination": "/migration/oss-v2-to-v3"
|
||||
},
|
||||
{
|
||||
"source": "/v0x/examples/ai_companion_js",
|
||||
@@ -814,7 +857,7 @@
|
||||
},
|
||||
{
|
||||
"source": "/v0x/examples/chrome-extension",
|
||||
"destination": "/cookbooks/frameworks/chrome-extension"
|
||||
"destination": "/cookbooks/overview"
|
||||
},
|
||||
{
|
||||
"source": "/v0x/examples/youtube-assistant",
|
||||
@@ -882,7 +925,7 @@
|
||||
},
|
||||
{
|
||||
"source": "/v0x/examples/aws_neptune_analytics_hybrid_store",
|
||||
"destination": "/cookbooks/integrations/neptune-analytics"
|
||||
"destination": "/migration/oss-v2-to-v3"
|
||||
},
|
||||
{
|
||||
"source": "/features/memory-export",
|
||||
@@ -966,7 +1009,7 @@
|
||||
},
|
||||
{
|
||||
"source": "/features/graph-memory",
|
||||
"destination": "/platform/features/graph-memory"
|
||||
"destination": "/migration/oss-v2-to-v3"
|
||||
},
|
||||
{
|
||||
"source": "/features/:slug",
|
||||
@@ -1074,7 +1117,7 @@
|
||||
},
|
||||
{
|
||||
"source": "/open-source/graph-memory",
|
||||
"destination": "/open-source/features/graph-memory"
|
||||
"destination": "/migration/oss-v2-to-v3"
|
||||
},
|
||||
{
|
||||
"source": "/cookbooks/customer-support-agent",
|
||||
|
||||
@@ -50,7 +50,7 @@ local_config = {
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4.1-nano-2025-04-14",
|
||||
"model": "gpt-5-mini",
|
||||
"temperature": 0.1,
|
||||
"max_tokens": 2000,
|
||||
},
|
||||
@@ -103,7 +103,7 @@ def demonstrate_sync_memory(local_config, sample_messages, sample_preferences, u
|
||||
]
|
||||
|
||||
for query in search_queries:
|
||||
results = memory.search(query, user_id=user_id)
|
||||
results = memory.search(query, filters={"user_id": user_id})
|
||||
|
||||
if results and "results" in results:
|
||||
for j, result in enumerate(results['results']):
|
||||
@@ -111,7 +111,7 @@ def demonstrate_sync_memory(local_config, sample_messages, sample_preferences, u
|
||||
else:
|
||||
print("No results found")
|
||||
|
||||
all_memories = memory.get_all(user_id=user_id)
|
||||
all_memories = memory.get_all(filters={"user_id": user_id})
|
||||
if all_memories and "results" in all_memories:
|
||||
print(f"Total memories: {len(all_memories['results'])}")
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ from agno.tools.mem0 import Mem0Tools
|
||||
|
||||
agent = Agent(
|
||||
name="Memory Agent",
|
||||
model=OpenAIChat(id="gpt-4.1-nano-2025-04-14"),
|
||||
model=OpenAIChat(id="gpt-5-mini"),
|
||||
tools=[Mem0Tools()],
|
||||
description="An assistant that remembers and personalizes using Mem0 memory."
|
||||
)
|
||||
@@ -126,7 +126,7 @@ def chat_user(
|
||||
|
||||
if user_input:
|
||||
# Search for relevant memories
|
||||
memories = client.search(user_input, user_id=user_id)
|
||||
memories = client.search(user_input, filters={"user_id": user_id})
|
||||
memory_context = "\n".join(f"- {m['memory']}" for m in memories['results'])
|
||||
|
||||
# Construct the prompt
|
||||
|
||||
@@ -72,7 +72,7 @@ Create a function to get context-aware responses based on user's question and pr
|
||||
|
||||
```python
|
||||
def get_context_aware_response(question):
|
||||
relevant_memories = memory_client.search(question, user_id=USER_ID)
|
||||
relevant_memories = memory_client.search(question, filters={"user_id": USER_ID})
|
||||
context = "\n".join([m["memory"] for m in relevant_memories.get('results', [])])
|
||||
|
||||
prompt = f"""Answer the user question considering the previous interactions:
|
||||
@@ -104,7 +104,7 @@ manager = ConversableAgent(
|
||||
)
|
||||
|
||||
def escalate_to_manager(question):
|
||||
relevant_memories = memory_client.search(question, user_id=USER_ID)
|
||||
relevant_memories = memory_client.search(question, filters={"user_id": USER_ID})
|
||||
context = "\n".join([m["memory"] for m in relevant_memories.get('results', [])])
|
||||
|
||||
prompt = f"""
|
||||
|
||||
@@ -107,10 +107,10 @@ messages = [
|
||||
m.add(messages, user_id="alice", metadata={"category": "movie_recommendations"})
|
||||
|
||||
# Search for memory
|
||||
relevant = m.search("What kind of movies does Alice like?", user_id="alice")
|
||||
relevant = m.search("What kind of movies does Alice like?", filters={"user_id": "alice"})
|
||||
|
||||
# Retrieve all user memories
|
||||
all_memories = m.get_all(user_id="alice")
|
||||
all_memories = m.get_all(filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
## Key Features
|
||||
@@ -125,8 +125,5 @@ all_memories = m.get_all(user_id="alice")
|
||||
<Card title="AWS Bedrock Cookbook" icon="aws" href="/cookbooks/integrations/aws-bedrock">
|
||||
Complete guide to using Bedrock with Mem0
|
||||
</Card>
|
||||
<Card title="Neptune Analytics Cookbook" icon="database" href="/cookbooks/integrations/neptune-analytics">
|
||||
Build graph memory with AWS Neptune
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ memories = mem0.search(
|
||||
{"categories": {"contains": "travel"}}
|
||||
]
|
||||
},
|
||||
limit=5
|
||||
top_k=5
|
||||
)
|
||||
|
||||
# Configure agent with custom model settings
|
||||
|
||||
@@ -16,7 +16,7 @@ Combining Mem0 with Keywords AI allows you to:
|
||||
4. Optimize token usage and reduce costs
|
||||
|
||||
<Note>
|
||||
You can get your Mem0 API key, user_id, and org_id from the <a href="https://app.mem0.ai/" rel="nofollow">Mem0 dashboard</a>. These are required for proper integration.
|
||||
You can get your Mem0 API key from the <a href="https://app.mem0.ai/" rel="nofollow">Mem0 dashboard</a>.
|
||||
</Note>
|
||||
|
||||
## Setup and Configuration
|
||||
@@ -56,7 +56,7 @@ config = {
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4.1-nano-2025-04-14",
|
||||
"model": "gpt-5-mini",
|
||||
"temperature": 0.0,
|
||||
"api_key": keywordsai_api_key,
|
||||
"openai_base_url": base_url,
|
||||
@@ -107,7 +107,6 @@ response = client.chat.completions.create(
|
||||
extra_body={
|
||||
"mem0_params": {
|
||||
"user_id": "test_user",
|
||||
"org_id": "org_1",
|
||||
"api_key": os.environ.get("MEM0_API_KEY"),
|
||||
"add_memories": {
|
||||
"messages": messages,
|
||||
|
||||
@@ -29,10 +29,7 @@ import os
|
||||
|
||||
os.environ["MEM0_API_KEY"] = "your-api-key"
|
||||
|
||||
client = MemoryClient(
|
||||
org_id=your_org_id,
|
||||
project_id=your_project_id
|
||||
)
|
||||
client = MemoryClient()
|
||||
```
|
||||
|
||||
## Available Tools
|
||||
|
||||
@@ -40,7 +40,7 @@ load_dotenv()
|
||||
# os.environ["MEM0_API_KEY"] = "your-mem0-api-key"
|
||||
|
||||
# Initialize LangChain and Mem0
|
||||
llm = ChatOpenAI(model="gpt-4.1-nano-2025-04-14")
|
||||
llm = ChatOpenAI(model="gpt-5-mini")
|
||||
mem0 = MemoryClient()
|
||||
```
|
||||
|
||||
@@ -66,7 +66,7 @@ Create functions to handle context retrieval, response generation, and addition
|
||||
def retrieve_context(query: str, user_id: str) -> List[Dict]:
|
||||
"""Retrieve relevant context from Mem0"""
|
||||
try:
|
||||
memories = mem0.search(query, user_id=user_id)
|
||||
memories = mem0.search(query, filters={"user_id": user_id})
|
||||
memory_list = memories['results']
|
||||
|
||||
serialized_memories = ' '.join([mem["memory"] for mem in memory_list])
|
||||
|
||||
@@ -68,7 +68,7 @@ def chatbot(state: State):
|
||||
|
||||
try:
|
||||
# Retrieve relevant memories
|
||||
memories = mem0.search(messages[-1].content, user_id=user_id)
|
||||
memories = mem0.search(messages[-1].content, filters={"user_id": user_id})
|
||||
|
||||
# Handle dict response format
|
||||
memory_list = memories['results']
|
||||
|
||||
@@ -148,7 +148,7 @@ async def entrypoint(ctx: JobContext):
|
||||
|
||||
session = AgentSession(
|
||||
stt=deepgram.STT(),
|
||||
llm=openai.LLM(model="gpt-4.1-nano-2025-04-14"),
|
||||
llm=openai.LLM(model="gpt-5-mini"),
|
||||
tts=openai.TTS(voice="ash",),
|
||||
turn_detection=EnglishModel(),
|
||||
vad=silero.VAD.load(),
|
||||
|
||||
@@ -83,7 +83,7 @@ config = {
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4.1-nano-2025-04-14",
|
||||
"model": "gpt-5-mini",
|
||||
"temperature": 0.2,
|
||||
"max_tokens": 2000,
|
||||
},
|
||||
@@ -116,7 +116,7 @@ from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
# os.environ["OPENAI_API_KEY"] = "<your-openai-api-key>"
|
||||
llm = OpenAI(model="gpt-4.1-nano-2025-04-14")
|
||||
llm = OpenAI(model="gpt-5-mini")
|
||||
```
|
||||
|
||||
### SimpleChatEngine
|
||||
|
||||
@@ -45,7 +45,7 @@ mem0 = MemoryClient()
|
||||
@function_tool
|
||||
def search_memory(query: str, user_id: str) -> str:
|
||||
"""Search through past conversations and memories"""
|
||||
memories = mem0.search(query, user_id=user_id, limit=3)
|
||||
memories = mem0.search(query, filters={"user_id": user_id}, top_k=3)
|
||||
if memories and memories.get('results'):
|
||||
return "\n".join([f"- {mem['memory']}" for mem in memories['results']])
|
||||
return "No relevant memories found."
|
||||
@@ -64,7 +64,7 @@ agent = Agent(
|
||||
Use the save_memory tool to store important information about the user.
|
||||
Always personalize your responses based on available memory.""",
|
||||
tools=[search_memory, save_memory],
|
||||
model="gpt-4.1-nano-2025-04-14"
|
||||
model="gpt-5-mini"
|
||||
)
|
||||
|
||||
def chat_with_agent(user_input: str, user_id: str) -> str:
|
||||
@@ -115,7 +115,7 @@ travel_agent = Agent(
|
||||
understand the user's travel preferences and history before making recommendations.
|
||||
After providing your response, use store_conversation to save important details.""",
|
||||
tools=[search_memory, save_memory],
|
||||
model="gpt-4.1-nano-2025-04-14"
|
||||
model="gpt-5-mini"
|
||||
)
|
||||
|
||||
health_agent = Agent(
|
||||
@@ -124,7 +124,7 @@ health_agent = Agent(
|
||||
understand the user's health goals and dietary preferences.
|
||||
After providing advice, use store_conversation to save relevant information.""",
|
||||
tools=[search_memory, save_memory],
|
||||
model="gpt-4.1-nano-2025-04-14"
|
||||
model="gpt-5-mini"
|
||||
)
|
||||
|
||||
# Triage agent with handoffs
|
||||
@@ -135,7 +135,7 @@ triage_agent = Agent(
|
||||
For health-related questions (fitness, diet, wellness, exercise), hand off to the Health Advisor.
|
||||
For general questions, handle them directly using available tools.""",
|
||||
handoffs=[travel_agent, health_agent],
|
||||
model="gpt-4.1-nano-2025-04-14"
|
||||
model="gpt-5-mini"
|
||||
)
|
||||
|
||||
def chat_with_handoffs(user_input: str, user_id: str) -> str:
|
||||
@@ -215,7 +215,7 @@ Customize memory behavior:
|
||||
memories = mem0.search(
|
||||
query="travel preferences",
|
||||
user_id="alex",
|
||||
limit=5 # Number of memories to retrieve
|
||||
top_k=5 # Number of memories to retrieve
|
||||
)
|
||||
|
||||
# Add metadata to memories
|
||||
|
||||
@@ -147,7 +147,6 @@ openclaw mem0 stats
|
||||
| `apiKey` | `string` | — | **Required.** Mem0 API key (supports `${MEM0_API_KEY}`) |
|
||||
| `orgId` | `string` | — | Organization ID |
|
||||
| `projectId` | `string` | — | Project ID |
|
||||
| `enableGraph` | `boolean` | `false` | Entity graph for relationships |
|
||||
| `customInstructions` | `string` | *(built-in)* | Extraction rules — what to store, how to format |
|
||||
| `customCategories` | `object` | *(12 defaults)* | Category name → description map for tagging |
|
||||
|
||||
@@ -155,7 +154,7 @@ openclaw mem0 stats
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `customPrompt` | `string` | *(built-in)* | Extraction prompt for memory processing |
|
||||
| `customInstructions` | `string` | *(built-in)* | Extraction prompt for memory processing |
|
||||
| `oss.embedder.provider` | `string` | `"openai"` | Embedding provider (`"openai"`, `"ollama"`, etc.) |
|
||||
| `oss.embedder.config` | `object` | — | Provider config: `apiKey`, `model`, `baseURL` |
|
||||
| `oss.vectorStore.provider` | `string` | `"memory"` | Vector store (`"memory"`, `"qdrant"`, `"chroma"`, etc.) |
|
||||
|
||||
@@ -52,7 +52,7 @@ npm install @mem0/vercel-ai-provider
|
||||
|
||||
> **Note**: The `openai` provider is set as default. Consider using `MEM0_API_KEY` and `OPENAI_API_KEY` as environment variables for security.
|
||||
|
||||
> **Note**: The `mem0Config` is optional. It is used to set the global config for the Mem0 Client (eg. `user_id`, `agent_id`, `app_id`, `run_id`, `org_id`, `project_id` etc).
|
||||
> **Note**: The `mem0Config` is optional. It is used to set the global config for the Mem0 Client (eg. `user_id`, `agent_id`, `app_id`, `run_id` etc).
|
||||
|
||||
3. Add Memories to Enhance Context:
|
||||
|
||||
@@ -78,7 +78,7 @@ npm install @mem0/vercel-ai-provider
|
||||
|
||||
> `getMemories` will return raw memories in the form of an array of objects, while `retrieveMemories` will return a response in string format with a system prompt ingested with the retrieved memories.
|
||||
|
||||
> `getMemories` is an object with two keys: `results` and `relations` if `enable_graph` is enabled. Otherwise, it will return an array of objects.
|
||||
> `getMemories` returns an array of memory objects.
|
||||
|
||||
### 1. Basic Text Generation with Memory Context
|
||||
|
||||
@@ -270,24 +270,6 @@ main();
|
||||
|
||||
> **Note**: File support is available with providers that support multimodal capabilities like Google's Gemini models. The example shows how to process PDF files, but you can also work with images, text files, and other supported formats.
|
||||
|
||||
## Graph Memory
|
||||
|
||||
Mem0 AI SDK now supports Graph Memory. You can enable it by setting `enable_graph` to `true` in the `mem0Config` object.
|
||||
|
||||
```typescript
|
||||
const mem0 = createMem0({
|
||||
mem0Config: { enable_graph: true },
|
||||
});
|
||||
```
|
||||
|
||||
You can also pass `enable_graph` in the standalone functions. This includes `getMemories`, `retrieveMemories`, and `addMemories`.
|
||||
|
||||
```typescript
|
||||
const memories = await getMemories(prompt, { user_id: "borat", mem0ApiKey: "m0-xxx", enable_graph: true });
|
||||
```
|
||||
|
||||
The `getMemories` function will return an object with two keys: `results` and `relations`, if `enable_graph` is set to `true`. Otherwise, it will return an array of objects.
|
||||
|
||||
## Supported LLM Providers
|
||||
|
||||
| Provider | Configuration Value |
|
||||
|
||||
+1
-5
@@ -45,7 +45,6 @@ Key differentiators:
|
||||
- [Custom Categories](https://docs.mem0.ai/platform/features/custom-categories): Define domain-specific categories to improve memory organization
|
||||
|
||||
### Advanced Features
|
||||
- [Graph Memory](https://docs.mem0.ai/platform/features/graph-memory): Build and query relationships between entities for contextually relevant retrieval
|
||||
- [Graph Threshold](https://docs.mem0.ai/platform/features/graph-threshold): Configure graph relationship sensitivity and strength
|
||||
- [Advanced Retrieval](https://docs.mem0.ai/platform/features/advanced-retrieval): Enhanced search with keyword search, reranking, and filtering capabilities
|
||||
- [Criteria-Based Retrieval](https://docs.mem0.ai/platform/features/criteria-retrieval): Targeted memory retrieval using custom criteria
|
||||
@@ -81,12 +80,11 @@ Key differentiators:
|
||||
|
||||
### Open Source Features
|
||||
- [Features Overview](https://docs.mem0.ai/open-source/features/overview): Overview of all open-source features
|
||||
- [Graph Memory](https://docs.mem0.ai/open-source/features/graph-memory): Build and query entity relationships using graph stores like Neo4j
|
||||
- [Metadata Filtering](https://docs.mem0.ai/open-source/features/metadata-filtering): Advanced filtering using custom metadata fields
|
||||
- [Reranker Search](https://docs.mem0.ai/open-source/features/reranker-search): Enhanced search results with reranking models
|
||||
- [Async Memory](https://docs.mem0.ai/open-source/features/async-memory): Asynchronous memory operations for better performance
|
||||
- [Multimodal Support](https://docs.mem0.ai/open-source/features/multimodal-support): Handle text, images, and documents in self-hosted setup
|
||||
- [Custom Fact Extraction](https://docs.mem0.ai/open-source/features/custom-fact-extraction-prompt): Tailor information extraction for specific use cases
|
||||
- [Custom Instructions](https://docs.mem0.ai/open-source/features/custom-instructions): Tailor information extraction for specific use cases
|
||||
- [Custom Memory Update Prompt](https://docs.mem0.ai/open-source/features/custom-update-memory-prompt): Customize how memories are updated and merged
|
||||
- [REST API Server](https://docs.mem0.ai/open-source/features/rest-api): FastAPI-based server with core operations and OpenAPI documentation
|
||||
- [OpenAI Compatibility](https://docs.mem0.ai/open-source/features/openai_compatibility): Seamless integration with OpenAI-compatible APIs
|
||||
@@ -245,9 +243,7 @@ Key differentiators:
|
||||
- [LlamaIndex Multiagent](https://docs.mem0.ai/cookbooks/frameworks/llamaindex-multiagent): Multi-agent systems with shared memory
|
||||
- [Multimodal Retrieval](https://docs.mem0.ai/cookbooks/frameworks/multimodal-retrieval): Memory systems handling text, images, and documents
|
||||
- [Eliza OS Character](https://docs.mem0.ai/cookbooks/frameworks/eliza-os-character): Character-based AI with persistent personality
|
||||
- [Chrome Extension](https://docs.mem0.ai/cookbooks/frameworks/chrome-extension): Browser extensions that remember user interactions
|
||||
- [Gemini with Mem0 MCP](https://docs.mem0.ai/cookbooks/frameworks/gemini-3-with-mem0-mcp): Google Gemini integration using MCP server
|
||||
- [Mirofish Swarm Memory](https://docs.mem0.ai/cookbooks/frameworks/mirofish-swarm-memory): Swarm-based multi-agent memory patterns
|
||||
|
||||
## API Reference
|
||||
|
||||
|
||||
@@ -319,7 +319,7 @@ config = {
|
||||
"graph_store": {...},
|
||||
"version": "v1.0", # ❌ v1.0 no longer supported
|
||||
"history_db_path": "...",
|
||||
"custom_fact_extraction_prompt": "..."
|
||||
"custom_instructions": "..."
|
||||
}
|
||||
```
|
||||
|
||||
@@ -336,7 +336,7 @@ config = {
|
||||
},
|
||||
"version": "v1.1", # ✅ v1.1+ only
|
||||
"history_db_path": "...",
|
||||
"custom_fact_extraction_prompt": "...",
|
||||
"custom_instructions": "...",
|
||||
"custom_update_memory_prompt": "..." # ✅ NEW: Custom update prompt
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,383 +0,0 @@
|
||||
---
|
||||
title: Breaking Changes in v1.0.0
|
||||
description: 'Complete list of breaking changes when upgrading from v0.x to v1.0.0 '
|
||||
icon: "triangle-exclamation"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
**Important:** This page lists all breaking changes. Please review carefully before upgrading.
|
||||
</Warning>
|
||||
|
||||
## API Version Changes
|
||||
|
||||
### Removed v1.0 API Support
|
||||
|
||||
**Breaking Change:** The v1.0 API format is completely removed and no longer supported.
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
# This was supported in v0.x
|
||||
config = {
|
||||
"version": "v1.0" # ❌ No longer supported
|
||||
}
|
||||
|
||||
result = m.add(
|
||||
"memory content",
|
||||
user_id="alice"
|
||||
)
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
# v1.1 is the minimum supported version
|
||||
config = {
|
||||
"version": "v1.1" # ✅ Required minimum
|
||||
}
|
||||
|
||||
result = m.add(
|
||||
"memory content",
|
||||
user_id="alice"
|
||||
)
|
||||
```
|
||||
|
||||
**Error Message:**
|
||||
```
|
||||
ValueError: The v1.0 API format is no longer supported in mem0ai 1.0.0+.
|
||||
Please use v1.1 format which returns a dict with 'results' key.
|
||||
```
|
||||
|
||||
## Parameter Removals
|
||||
|
||||
### 1. version Parameter in Method Calls
|
||||
|
||||
**Breaking Change:** Version parameter removed from method calls.
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
result = m.add("content", user_id="alice", version="v1.0")
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
result = m.add("content", user_id="alice")
|
||||
```
|
||||
|
||||
### 2. async_mode Parameter (Platform Client)
|
||||
|
||||
**Change:** For `MemoryClient` (Platform API), `async_mode` now defaults to `True` but can still be configured.
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-key")
|
||||
result = client.add("content", user_id="alice", async_mode=True)
|
||||
result = client.add("content", user_id="alice", async_mode=False)
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-key")
|
||||
|
||||
# async_mode now defaults to True, but you can still override it
|
||||
result = client.add("content", user_id="alice") # Uses async_mode=True by default
|
||||
|
||||
# You can still explicitly set it to False if needed
|
||||
result = client.add("content", user_id="alice", async_mode=False)
|
||||
```
|
||||
|
||||
## Response Format Changes
|
||||
|
||||
### Standardized Response Structure
|
||||
|
||||
**Breaking Change:** All responses now return a standardized dictionary format.
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
# Could return different formats based on version configuration
|
||||
result = m.add("content", user_id="alice")
|
||||
# With v1.0: Returns [{"id": "...", "memory": "...", "event": "ADD"}]
|
||||
# With v1.1: Returns {"results": [{"id": "...", "memory": "...", "event": "ADD"}]}
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
# Always returns standardized format
|
||||
result = m.add("content", user_id="alice")
|
||||
# Always returns: {"results": [{"id": "...", "memory": "...", "event": "ADD"}]}
|
||||
|
||||
# Access results consistently
|
||||
for memory in result["results"]:
|
||||
print(memory["memory"])
|
||||
```
|
||||
|
||||
## Configuration Changes
|
||||
|
||||
### Version Configuration
|
||||
|
||||
**Breaking Change:** Default API version changed.
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
# v1.0 was supported
|
||||
config = {
|
||||
"version": "v1.0" # ❌ No longer supported
|
||||
}
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
# v1.1 is minimum, v1.1 is default
|
||||
config = {
|
||||
"version": "v1.1" # ✅ Minimum supported
|
||||
}
|
||||
|
||||
# Or omit for default
|
||||
config = {
|
||||
# version defaults to v1.1
|
||||
}
|
||||
```
|
||||
|
||||
### Memory Configuration
|
||||
|
||||
**Breaking Change:** Some configuration options have changed defaults.
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
from mem0 import Memory
|
||||
|
||||
# Default configuration in v0.x
|
||||
m = Memory() # Used default settings suitable for v0.x
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
from mem0 import Memory
|
||||
|
||||
# Default configuration optimized for v1.0.0
|
||||
m = Memory() # Uses v1.1+ optimized defaults
|
||||
|
||||
# Explicit configuration recommended
|
||||
config = {
|
||||
"version": "v1.1",
|
||||
"vector_store": {
|
||||
"provider": "qdrant",
|
||||
"config": {
|
||||
"host": "localhost",
|
||||
"port": 6333
|
||||
}
|
||||
}
|
||||
}
|
||||
m = Memory.from_config(config)
|
||||
```
|
||||
|
||||
## Method Signature Changes
|
||||
|
||||
### Search Method
|
||||
|
||||
**Enhanced but backward compatible:**
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
results = m.search(
|
||||
"query",
|
||||
user_id="alice",
|
||||
filters={"key": "value"} # Simple key-value only
|
||||
)
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
# Basic usage remains the same
|
||||
results = m.search("query", user_id="alice")
|
||||
|
||||
# Enhanced filtering available (optional)
|
||||
results = m.search(
|
||||
"query",
|
||||
user_id="alice",
|
||||
filters={
|
||||
"AND": [
|
||||
{"key": "value"},
|
||||
{"score": {"gte": 0.8}}
|
||||
]
|
||||
},
|
||||
rerank=True # New parameter
|
||||
)
|
||||
```
|
||||
|
||||
## Error Handling Changes
|
||||
|
||||
### New Error Types
|
||||
|
||||
**Breaking Change:** More specific error types and messages.
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
try:
|
||||
result = m.add("content", user_id="alice", version="v1.0")
|
||||
except Exception as e:
|
||||
print(f"Generic error: {e}")
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
try:
|
||||
result = m.add("content", user_id="alice")
|
||||
except ValueError as e:
|
||||
if "v1.0 API format is no longer supported" in str(e):
|
||||
# Handle version error specifically
|
||||
print("Please upgrade your code to use v1.1+ format")
|
||||
else:
|
||||
print(f"Value error: {e}")
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {e}")
|
||||
```
|
||||
|
||||
### Validation Changes
|
||||
|
||||
**Breaking Change:** Stricter parameter validation.
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
# Some invalid parameters might have been ignored
|
||||
result = m.add(
|
||||
"content",
|
||||
user_id="alice",
|
||||
invalid_param="ignored" # Might have been silently ignored
|
||||
)
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
# Strict validation - unknown parameters cause errors
|
||||
try:
|
||||
result = m.add(
|
||||
"content",
|
||||
user_id="alice",
|
||||
invalid_param="value" # ❌ Will raise TypeError
|
||||
)
|
||||
except TypeError as e:
|
||||
print(f"Invalid parameter: {e}")
|
||||
```
|
||||
|
||||
## Import Changes
|
||||
|
||||
### No Breaking Changes in Imports
|
||||
|
||||
**Good News:** Import statements remain the same.
|
||||
|
||||
```python
|
||||
# These imports work in both v0.x and v1.0.0
|
||||
from mem0 import Memory, AsyncMemory
|
||||
from mem0 import MemoryConfig
|
||||
```
|
||||
|
||||
## Dependency Changes
|
||||
|
||||
### Minimum Python Version
|
||||
|
||||
**Potential Breaking Change:** Check Python version requirements.
|
||||
|
||||
#### Before (v0.x)
|
||||
- Python 3.8+ supported
|
||||
|
||||
#### After (v1.0.0 )
|
||||
- Python 3.9+ required (check current requirements)
|
||||
|
||||
### Package Dependencies
|
||||
|
||||
**Breaking Change:** Some dependencies updated with potential breaking changes.
|
||||
|
||||
```bash
|
||||
# Check for conflicts after upgrade
|
||||
pip install --upgrade mem0ai
|
||||
pip check # Verify no dependency conflicts
|
||||
```
|
||||
|
||||
## Data Migration
|
||||
|
||||
### Database Schema
|
||||
|
||||
**Good News:** No database schema changes required.
|
||||
|
||||
- Existing memories remain compatible
|
||||
- No data migration required
|
||||
- Vector store data unchanged
|
||||
|
||||
### Memory Format
|
||||
|
||||
**Good News:** Memory storage format unchanged.
|
||||
|
||||
- Existing memories work with v1.0.0
|
||||
- Search continues to work with old memories
|
||||
- No re-indexing required
|
||||
|
||||
## Testing Changes
|
||||
|
||||
### Test Updates Required
|
||||
|
||||
**Breaking Change:** Update tests for new response format.
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
def test_add_memory():
|
||||
result = m.add("content", user_id="alice")
|
||||
assert isinstance(result, list) # ❌ No longer true
|
||||
assert len(result) > 0
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
def test_add_memory():
|
||||
result = m.add("content", user_id="alice")
|
||||
assert isinstance(result, dict) # ✅ Always dict
|
||||
assert "results" in result # ✅ Always has results key
|
||||
assert len(result["results"]) > 0
|
||||
```
|
||||
|
||||
## Rollback Considerations
|
||||
|
||||
### Safe Rollback Process
|
||||
|
||||
If you need to rollback:
|
||||
|
||||
```bash
|
||||
# 1. Rollback package
|
||||
pip install mem0ai==0.1.20 # Last stable v0.x
|
||||
|
||||
# 2. Revert code changes
|
||||
git checkout previous_commit
|
||||
|
||||
# 3. Test functionality
|
||||
python test_mem0_functionality.py
|
||||
```
|
||||
|
||||
### Data Safety
|
||||
|
||||
- **Safe:** Memories stored in v0.x format work with v1.0.0
|
||||
- **Safe:** Rollback doesn't lose data
|
||||
- **Safe:** Vector store data remains intact
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Review all breaking changes** in your codebase
|
||||
2. **Update method calls** to remove deprecated parameters
|
||||
3. **Update response handling** to use standardized format
|
||||
4. **Test thoroughly** with your existing data
|
||||
5. **Update error handling** for new error types
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Migration Guide" icon="arrow-right" href="/migration/v0-to-v1">
|
||||
Step-by-step migration instructions
|
||||
</Card>
|
||||
<Card title="API Changes" icon="code" href="/migration/api-changes">
|
||||
Complete API reference changes
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Warning>
|
||||
**Need Help?** If you encounter issues during migration, check our [GitHub Discussions](https://github.com/mem0ai/mem0/discussions) or community support channels.
|
||||
</Warning>
|
||||
@@ -81,6 +81,10 @@ client = MemoryClient(api_key="m0-...")
|
||||
**Critical Change**: Platform uses v2 endpoints that require filtering parameters to be nested inside a `filters` dictionary.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
The `limit` parameter has been removed in favor of `top_k` across all SDKs. Update any code using `limit=` to use `top_k=` instead.
|
||||
</Note>
|
||||
|
||||
| Method | Open Source | Platform |
|
||||
| ------ | ----------- | -------- |
|
||||
| `search()` | `m.search(query, user_id="alex")` | `client.search(query, filters={"user_id": "alex"})` |
|
||||
@@ -121,18 +125,18 @@ Note: `add()` and `delete()` methods remain unchanged. The `update()` method is
|
||||
<CodeGroup>
|
||||
```python Open Source (Old)
|
||||
# Get all memories for a user
|
||||
memories = m.get_all(user_id="alex", limit=10)
|
||||
memories = m.get_all(user_id="alex", top_k=10)
|
||||
|
||||
# Get memories with pagination
|
||||
memories = m.get_all(user_id="alex", limit=5, offset=10)
|
||||
memories = m.get_all(user_id="alex", top_k=5, offset=10)
|
||||
```
|
||||
|
||||
```python Platform (New)
|
||||
# Get all memories for a user
|
||||
memories = client.get_all(filters={"user_id": "alex"}, limit=10)
|
||||
memories = client.get_all(filters={"user_id": "alex"}, top_k=10)
|
||||
|
||||
# Get memories with pagination
|
||||
memories = client.get_all(filters={"user_id": "alex"}, limit=5, offset=10)
|
||||
memories = client.get_all(filters={"user_id": "alex"}, top_k=5, offset=10)
|
||||
```
|
||||
</CodeGroup>
|
||||
</Accordion>
|
||||
@@ -283,7 +287,7 @@ The Platform introduces powerful capabilities not available in OSS:
|
||||
"user preferences",
|
||||
filters={"user_id": "alex"},
|
||||
rerank=True, # Platform exclusive
|
||||
limit=5
|
||||
top_k=5
|
||||
)
|
||||
|
||||
# Search with keyword expansion
|
||||
@@ -335,7 +339,7 @@ The Platform introduces powerful capabilities not available in OSS:
|
||||
{"timestamp": {"gte": "2024-01-01"}}
|
||||
]
|
||||
},
|
||||
limit=100
|
||||
top_k=100
|
||||
)
|
||||
|
||||
# Monitor usage patterns
|
||||
|
||||
@@ -0,0 +1,538 @@
|
||||
---
|
||||
title: "Open Source: Migrating to the New Memory Algorithm"
|
||||
description: "Guide for self-hosted Mem0 users to upgrade to the new memory algorithm with ADD-only extraction, hybrid search, and entity linking."
|
||||
icon: "arrow-right"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
**Breaking changes ahead.** This release includes renamed parameters, removed parameters, changed defaults, and a fundamentally different extraction model. Read this guide before upgrading.
|
||||
</Warning>
|
||||
|
||||
## Overview
|
||||
|
||||
The new Mem0 release redesigns both extraction and retrieval, and cleans up the SDK surface across Python and TypeScript:
|
||||
|
||||
- **Extraction**: Single-pass ADD-only (one LLM call, no UPDATE/DELETE)
|
||||
- **Retrieval**: Multi-signal hybrid search (semantic + BM25 keyword + entity matching)
|
||||
- **Entity linking**: Automatic entity extraction and cross-memory linking
|
||||
- **SDK cleanup**: Deprecated parameters removed, naming conventions standardized
|
||||
- **API surface aligned with Platform**: Entity IDs now follow the same convention across OSS and Platform — top-level kwargs for `add()` / `delete_all()`, inside `filters` for `search()` / `get_all()`
|
||||
|
||||
These changes produce a **+20 point improvement on LoCoMo** (71.4 → 91.6) and **+26 point improvement on LongMemEval** (67.8 → 93.4), while cutting extraction latency roughly in half.
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### Python Open Source
|
||||
|
||||
| Change | Old | New | Migration |
|
||||
|---|---|---|---|
|
||||
| `search()` / `get_all()` entity IDs | Top-level kwargs (`user_id="..."`) | Inside `filters` dict | `m.search("q", filters={"user_id": "..."})` — top-level kwargs now raise `ValueError` |
|
||||
| `top_k` default | `100` | `20` | Pass `top_k=100` explicitly to restore |
|
||||
| `threshold` default | `None` (no filtering) | `0.1` (filters low-relevance) | Pass `threshold=0.0` for old behavior |
|
||||
| `threshold` validation | Any float | Must be in `[0, 1]` | Out-of-range values now raise `ValueError` |
|
||||
| `rerank` default | `True` | `False` | Pass `rerank=True` to restore |
|
||||
| Entity ID validation | Accepted any string | Trimmed; empty / whitespace-only rejected (`ValueError`) | Pass a non-empty identifier without internal spaces |
|
||||
| `messages` in `add()` | Could be `None` | Must be `str` / `dict` / `list[dict]` — other types raise `Mem0ValidationError` (code `VALIDATION_003`) | Always pass a string, dict, or list of messages |
|
||||
| `add()` events | Returns `ADD`, `UPDATE`, `DELETE` | Returns `ADD` only | Update code expecting UPDATE/DELETE |
|
||||
| Custom extraction prompt | `custom_fact_extraction_prompt` | `custom_instructions` | Rename in config |
|
||||
| Custom update prompt | `custom_update_memory_prompt` | Deprecated | Use `custom_instructions` instead |
|
||||
| Graph memory | `enable_graph` + `graph_store` in config | Removed | Graph store support has been removed entirely |
|
||||
| Qdrant client | `>=1.9.1` | `>=1.12.0` | Update dependency |
|
||||
| Upstash client | `>=0.1.0` | `>=0.6.0` | Update dependency |
|
||||
|
||||
### TypeScript Open Source
|
||||
|
||||
| Change | Old | New | Migration |
|
||||
|---|---|---|---|
|
||||
| Search parameter | `search(query, { limit: 10 })` | `search(query, { topK: 10 })` | Rename `limit` → `topK` |
|
||||
| `topK` default | `100` | `20` | Pass `topK: 100` explicitly to restore |
|
||||
| `search()` / `getAll()` entity IDs | Top-level options (`userId: "..."`) | Inside `filters` object | `m.search("q", { filters: { userId: "..." } })` |
|
||||
| `threshold` validation | Any number | Must be in `[0, 1]` | Out-of-range values now throw |
|
||||
| Entity ID validation | Any string | Trimmed; empty / whitespace-only rejected | Pass non-empty identifiers without internal spaces |
|
||||
| `messages` in `add()` | Could be `null` / `undefined` | Required — throws on null/undefined | Always pass a string or array |
|
||||
| Payload key for lemmatized text | `text_lemmatized` (snake_case) | `textLemmatized` (camelCase) | TS-only internal field. If you share a vector store collection between Python and TS SDKs, lemma-based BM25 will not resolve across languages — keep collections language-scoped. |
|
||||
| Custom prompt | `customPrompt` | `customInstructions` | Rename in config |
|
||||
| Graph memory | `enableGraph` + `graphStore` in config | Removed | Graph store support has been removed entirely |
|
||||
| Default graph config | Neo4j default config applied | No default graph config | Graph store config is no longer used |
|
||||
|
||||
### Python Client SDK
|
||||
|
||||
| Change | Old | New | Migration |
|
||||
|---|---|---|---|
|
||||
| Constructor | `MemoryClient(api_key, org_id, project_id)` | `MemoryClient(api_key)` | Remove `org_id`, `project_id` from constructor |
|
||||
| Method options | `client.add(messages, **kwargs)` | `client.add(messages, options=AddMemoryOptions(...))` | Use typed option classes (or `**kwargs` still works) |
|
||||
| Removed params | `api_version`, `output_format`, `async_mode`, `filter_memories`, `expiration_date`, `keyword_search`, `force_add_only`, `batch_size`, `immutable`, `includes`, `excludes`, `enable_graph`, `org_name`, `project_name` | — | Remove from all calls |
|
||||
|
||||
### TypeScript Client SDK
|
||||
|
||||
| Change | Old | New | Migration |
|
||||
|---|---|---|---|
|
||||
| Constructor | `new MemoryClient({ apiKey, organizationId, projectId })` | `new MemoryClient({ apiKey })` | Remove `organizationId`, `projectId`, `organizationName`, `projectName` |
|
||||
| All params | snake_case: `user_id`, `agent_id`, `top_k` | camelCase: `userId`, `agentId`, `topK` | Rename all params to camelCase |
|
||||
| Removed params | `api_version`, `output_format`, `async_mode`, `enable_graph`, `org_id`, `project_id`, `org_name`, `project_name`, `filter_memories`, `batch_size`, `force_add_only`, `immutable`, `expiration_date`, `includes`, `excludes`, `keyword_search` | — | Remove from all calls |
|
||||
| Output format enum | `OutputFormat.V1`, `OutputFormat.V1_1` | Removed | v1.1 is now always used |
|
||||
| API version enum | `API_VERSION.V1`, `API_VERSION.V2` | Removed | Handled internally |
|
||||
|
||||
## Step-by-Step Migration
|
||||
|
||||
### 1. Update Installation
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Python">
|
||||
```bash
|
||||
# Basic upgrade
|
||||
pip install --upgrade mem0ai
|
||||
|
||||
# For hybrid search + entity extraction (recommended)
|
||||
pip install --upgrade "mem0ai[nlp]"
|
||||
python -m spacy download en_core_web_sm
|
||||
|
||||
# Qdrant users: also install fastembed to enable BM25 keyword search
|
||||
pip install fastembed
|
||||
```
|
||||
|
||||
<Info>
|
||||
**Supported Python versions for `[nlp]` extras: 3.10 – 3.12.** spaCy and its `blis` / `thinc` dependencies do not yet ship prebuilt wheels for Python 3.13, so installs on 3.13 will fail at build time. Use Python 3.12 (or older) for the `[nlp]` extras until upstream support lands. The base `mem0ai` package works on all supported Python versions; only the NLP extras are constrained.
|
||||
</Info>
|
||||
</Tab>
|
||||
<Tab title="TypeScript">
|
||||
```bash
|
||||
npm install mem0ai@latest
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Info>
|
||||
The Python `[nlp]` extra installs [spaCy](https://spacy.io/) for entity extraction and keyword lemmatization. Without it, Mem0 still works but falls back to semantic-only search (no entity linking, no BM25 lemmatization).
|
||||
</Info>
|
||||
|
||||
<Warning>
|
||||
**Qdrant users — install `fastembed` to enable BM25 keyword search.** The Qdrant backend uses [fastembed](https://github.com/qdrant/fastembed) to encode sparse (BM25) vectors alongside dense vectors in the same collection. Without it, BM25 is silently disabled and search falls back to semantic-only — you'll see a log warning `"fastembed not installed — BM25 keyword search disabled"` on the first search call. Other vector stores use their native full-text capabilities and don't need `fastembed`.
|
||||
|
||||
```bash
|
||||
pip install fastembed
|
||||
```
|
||||
</Warning>
|
||||
|
||||
### 2. Update Configuration
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Python OSS">
|
||||
```python
|
||||
# Before
|
||||
config = {
|
||||
"custom_fact_extraction_prompt": "Focus on user preferences", # [REMOVED] Renamed
|
||||
"custom_update_memory_prompt": "Be concise when updating", # [REMOVED] Deprecated
|
||||
"graph_store": {
|
||||
"provider": "neo4j",
|
||||
"config": { "url": "...", "username": "...", "password": "..." }
|
||||
},
|
||||
"enable_graph": True, # [REMOVED] Removed
|
||||
}
|
||||
|
||||
# After
|
||||
config = {
|
||||
"custom_instructions": "Focus on user preferences", # [OK] New name
|
||||
# custom_update_memory_prompt removed — use custom_instructions
|
||||
# enable_graph and graph_store removed — graph store support has been removed
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="TypeScript OSS">
|
||||
```typescript
|
||||
// Before
|
||||
const config = {
|
||||
customPrompt: "Focus on user preferences", // [REMOVED] Renamed
|
||||
enableGraph: true, // [REMOVED] Removed
|
||||
graphStore: {
|
||||
provider: "neo4j",
|
||||
config: { url: "...", username: "...", password: "..." }
|
||||
}
|
||||
};
|
||||
|
||||
// After
|
||||
const config = {
|
||||
customInstructions: "Focus on user preferences", // [OK] New name
|
||||
// enableGraph and graphStore removed — graph store support has been removed
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### 3. Update Search Calls
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Python OSS">
|
||||
```python
|
||||
# Before — entity IDs as top-level kwargs
|
||||
results = m.search(
|
||||
"what meetings did I attend?",
|
||||
user_id="alice",
|
||||
top_k=20
|
||||
)
|
||||
for r in results:
|
||||
print(r["score"]) # Was raw cosine similarity
|
||||
|
||||
# After — entity IDs go inside `filters` (matches Platform API)
|
||||
results = m.search(
|
||||
"what meetings did I attend?",
|
||||
filters={"user_id": "alice"}, # [REMOVED top-level kwarg, use filters]
|
||||
top_k=20, # New default is 20 (was 100)
|
||||
threshold=0.1, # New default (pass 0.0 to disable)
|
||||
rerank=False # New default (pass True to restore)
|
||||
)
|
||||
for r in results:
|
||||
print(r["score"])
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Passing `user_id`, `agent_id`, or `run_id` as a top-level kwarg to `search()` or `get_all()` now raises `ValueError`. They must be inside the `filters` dict. The change aligns the OSS SDK with the Platform API contract.
|
||||
</Warning>
|
||||
</Tab>
|
||||
<Tab title="TypeScript OSS">
|
||||
```typescript
|
||||
// Before — entity IDs as top-level options
|
||||
const results = await m.search("what meetings did I attend?", {
|
||||
userId: "alice",
|
||||
limit: 20 // [REMOVED] Renamed to 'topK' for consistency
|
||||
});
|
||||
|
||||
// After — entity IDs go inside `filters` (matches Platform API)
|
||||
const results = await m.search("what meetings did I attend?", {
|
||||
filters: { userId: "alice" }, // [REMOVED top-level option, use filters]
|
||||
topK: 20 // [OK] Renamed from 'limit'
|
||||
});
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Python Client SDK">
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
from mem0.client.types import SearchMemoryOptions
|
||||
|
||||
# Before
|
||||
client = MemoryClient(api_key="...", org_id="org-1", project_id="proj-1")
|
||||
results = client.search("query", user_id="alice", top_k=20, enable_graph=True)
|
||||
|
||||
# After
|
||||
client = MemoryClient(api_key="...") # org_id, project_id removed
|
||||
results = client.search(
|
||||
"query",
|
||||
options=SearchMemoryOptions(
|
||||
filters={"user_id": "alice"},
|
||||
top_k=20
|
||||
)
|
||||
)
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="TypeScript Client SDK">
|
||||
```typescript
|
||||
// Before
|
||||
const client = new MemoryClient({
|
||||
apiKey: "...",
|
||||
organizationId: "org-1", // [REMOVED] Removed
|
||||
projectId: "proj-1" // [REMOVED] Removed
|
||||
});
|
||||
const results = await client.search("query", {
|
||||
user_id: "alice", // [REMOVED] snake_case
|
||||
top_k: 20, // [REMOVED] snake_case
|
||||
enable_graph: true // [REMOVED] Removed
|
||||
});
|
||||
|
||||
// After
|
||||
const client = new MemoryClient({ apiKey: "..." });
|
||||
const results = await client.search("query", {
|
||||
filters: { userId: "alice" },
|
||||
topK: 20
|
||||
});
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### 4. Update Add Calls
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Python OSS">
|
||||
```python
|
||||
# Before — could return ADD, UPDATE, DELETE events
|
||||
result = m.add("I love hiking and my dog's name is Max", user_id="alice")
|
||||
for item in result["results"]:
|
||||
if item["event"] == "ADD":
|
||||
print("New memory:", item["memory"])
|
||||
elif item["event"] == "UPDATE":
|
||||
print("Updated:", item["memory"]) # [REMOVED] No longer returned
|
||||
elif item["event"] == "DELETE":
|
||||
print("Deleted:", item["memory"]) # [REMOVED] No longer returned
|
||||
|
||||
# After — only ADD events
|
||||
result = m.add("I love hiking and my dog's name is Max", user_id="alice")
|
||||
for item in result["results"]:
|
||||
print("New memory:", item["memory"]) # Only ADD events
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Python Client SDK">
|
||||
```python
|
||||
from mem0.client.types import AddMemoryOptions
|
||||
|
||||
# Before
|
||||
client.add(messages, user_id="alice", async_mode=True, output_format="v1.1")
|
||||
|
||||
# After — async_mode and output_format removed (async by default, v1.1 always)
|
||||
client.add(
|
||||
messages,
|
||||
options=AddMemoryOptions(user_id="alice")
|
||||
)
|
||||
# Or using **kwargs
|
||||
client.add(messages, user_id="alice")
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="TypeScript Client SDK">
|
||||
```typescript
|
||||
// Before
|
||||
await client.add(messages, {
|
||||
user_id: "alice", // [REMOVED] snake_case
|
||||
async_mode: true, // [REMOVED] Removed
|
||||
output_format: "v1.1", // [REMOVED] Removed
|
||||
enable_graph: true // [REMOVED] Removed
|
||||
});
|
||||
|
||||
// After
|
||||
await client.add(messages, {
|
||||
userId: "alice" // [OK] camelCase
|
||||
});
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Tip>
|
||||
The ADD-only model means memories accumulate over time. When information changes, the new fact is stored alongside the old one. Retrieval handles ranking — the most relevant, current information surfaces first.
|
||||
</Tip>
|
||||
|
||||
### 5. Update Vector Store Dependencies
|
||||
|
||||
If you're using Qdrant or Upstash, update your client libraries:
|
||||
|
||||
```bash
|
||||
# Qdrant users
|
||||
pip install "qdrant-client>=1.12.0"
|
||||
|
||||
# Upstash users
|
||||
pip install "upstash-vector>=0.6.0"
|
||||
```
|
||||
|
||||
### 6. Entity Store Setup
|
||||
|
||||
The new algorithm automatically creates a parallel entity store collection named `{your_collection}_entities`. No manual setup is required — it's created on first use.
|
||||
|
||||
<Warning>
|
||||
Make sure your vector store user/credentials have permission to create new collections. If you're using a managed vector database with restricted permissions, pre-create the `{collection_name}_entities` collection with the same embedding dimensions as your main collection.
|
||||
</Warning>
|
||||
|
||||
## Graph Memory → Entity Linking
|
||||
|
||||
Graph store support has been removed from the open-source SDK. It is replaced by **built-in entity linking**, which runs natively with no external dependencies.
|
||||
|
||||
**What was removed:**
|
||||
- `enable_graph` / `enableGraph` config flag
|
||||
- `graph_store` / `graphStore` configuration block (Neo4j, Memgraph, Kuzu, Apache AGE, Neptune)
|
||||
- All graph memory code paths (~4000 lines)
|
||||
|
||||
**What replaces it:**
|
||||
|
||||
Entity linking extracts entities (proper nouns, quoted text, compound noun phrases) from every memory during the add pipeline and stores them in a parallel collection (`{collection}_entities`) inside your existing vector store. At search time, entities from the query are matched against this collection and used to boost relevant memories. The boost is folded into the combined `score` on each result.
|
||||
|
||||
**Migration:**
|
||||
- Remove `enable_graph` / `enableGraph` from your config
|
||||
- Remove the `graph_store` / `graphStore` block — it is no longer read
|
||||
- Uninstall graph drivers (neo4j, memgraph, etc.) if you were using them only for Mem0
|
||||
- No data migration is required. Entity linking activates automatically on the next `add()` call.
|
||||
|
||||
<Warning>
|
||||
Graph relationships exposed via the old `relations` field on search results are no longer populated. Entity relationships are consumed indirectly through retrieval ranking, not exposed as a queryable graph structure. If your application depended on traversing graph relationships directly, you will need to redesign that part against the new API.
|
||||
</Warning>
|
||||
|
||||
## How the New Algorithm Works
|
||||
|
||||
### Extraction: Single-Pass ADD-Only
|
||||
|
||||
```
|
||||
Input conversation
|
||||
→ Retrieve top-10 related existing memories (for deduplication context)
|
||||
→ Single LLM call: extract all distinct new facts
|
||||
→ Batch embed extracted memories
|
||||
→ Hash-based deduplication (MD5, prevents exact duplicates)
|
||||
→ Batch insert into vector store
|
||||
→ Entity extraction + linking
|
||||
```
|
||||
|
||||
The previous algorithm used two LLM calls — one to extract candidate facts, one to decide ADD/UPDATE/DELETE actions against existing memories. The new algorithm collapses this into a single call that only adds. The model spends its capacity on understanding the input rather than diffing against existing state.
|
||||
|
||||
### Retrieval: Multi-Signal Hybrid Search
|
||||
|
||||
```
|
||||
Query
|
||||
→ Preprocess (lemmatize keywords, extract entities)
|
||||
→ Parallel scoring:
|
||||
1. Semantic search (vector similarity)
|
||||
2. BM25 keyword search (normalized term matching)
|
||||
3. Entity matching (entity graph boost)
|
||||
→ Score fusion → Top-K selection
|
||||
```
|
||||
|
||||
**Scoring:** The three signals are normalized and fused into a single combined `score` per result. The fusion adapts based on which signals are available at runtime (semantic-only, semantic + BM25, or all three when spaCy + the entity store are active).
|
||||
|
||||
**BM25 is a boost signal, not a recall expander.** Only semantic search results are candidates — BM25 and entity scores boost ranking but don't add new candidates.
|
||||
|
||||
## Vector Store Compatibility
|
||||
|
||||
All 15 supported vector stores have been enhanced with two new capabilities:
|
||||
|
||||
| Capability | Purpose | Fallback if Unsupported |
|
||||
|---|---|---|
|
||||
| `keyword_search()` | BM25/full-text keyword matching | Falls back to semantic-only search |
|
||||
| `search_batch()` | Batch search for entity matching | Falls back to sequential search |
|
||||
|
||||
**Qdrant-specific changes:**
|
||||
- Now uses sparse vectors (BM25) alongside dense vectors in the same collection
|
||||
- Requires `fastembed` library for BM25 encoding (lazy-loaded, gracefully degrades)
|
||||
- Install: `pip install fastembed`
|
||||
|
||||
**All other vector stores:**
|
||||
- Enhanced with `keyword_search()` methods using their native full-text capabilities
|
||||
- No additional dependencies required
|
||||
|
||||
## Graceful Degradation
|
||||
|
||||
The new features degrade gracefully when optional dependencies are missing:
|
||||
|
||||
| Missing Dependency | Impact | Search Still Works? |
|
||||
|---|---|---|
|
||||
| spaCy (`mem0ai[nlp]`) | No entity extraction, no BM25 lemmatization | Yes (semantic-only) |
|
||||
| `fastembed` (Qdrant) | No BM25 keyword search | Yes (semantic + entity) |
|
||||
| Entity store unavailable | No entity boosting | Yes (semantic + BM25) |
|
||||
|
||||
You always get semantic search. Hybrid search features layer on top when available.
|
||||
|
||||
## Removed Parameters Reference
|
||||
|
||||
These parameters have been removed across all SDKs. Remove them from your code:
|
||||
|
||||
### Python Client SDK — Removed parameters
|
||||
|
||||
**Constructor:** `org_id`, `project_id`
|
||||
|
||||
**All methods:** `api_version`, `output_format`, `async_mode`, `org_name`, `project_name`, `org_id`, `project_id`
|
||||
|
||||
**add():** `enable_graph`, `immutable`, `expiration_date`, `filter_memories`, `batch_size`, `force_add_only`, `includes`, `excludes`, `keyword_search`
|
||||
|
||||
**search():** `enable_graph`
|
||||
|
||||
**get_all():** `enable_graph`
|
||||
|
||||
**project.update():** `enable_graph`
|
||||
|
||||
### TypeScript Client SDK — Removed parameters
|
||||
|
||||
**Constructor:** `organizationId`, `projectId`, `organizationName`, `projectName`
|
||||
|
||||
**All methods:** `OutputFormat` enum, `API_VERSION` enum
|
||||
|
||||
**add():** `enable_graph` / `enableGraph`, `async_mode` / `asyncMode`, `output_format` / `outputFormat`, `immutable`, `expiration_date` / `expirationDate`, `filter_memories` / `filterMemories`, `batch_size` / `batchSize`, `force_add_only` / `forceAddOnly`, `includes`, `excludes`, `keyword_search` / `keywordSearch`
|
||||
|
||||
**search():** `enable_graph` / `enableGraph`
|
||||
|
||||
**get_all():** `enable_graph` / `enableGraph`
|
||||
|
||||
### Python OSS — Removed/renamed parameters
|
||||
|
||||
**Config:** `custom_fact_extraction_prompt` → renamed to `custom_instructions`
|
||||
|
||||
**Config:** `custom_update_memory_prompt` → deprecated, use `custom_instructions`
|
||||
|
||||
**Config:** `enable_graph` + `graph_store` → removed (graph store support removed entirely)
|
||||
|
||||
### TypeScript OSS — Removed/renamed parameters
|
||||
|
||||
**Config:** `customPrompt` → renamed to `customInstructions`
|
||||
|
||||
**Config:** `enableGraph` + `graphStore` → removed (graph store support removed entirely)
|
||||
|
||||
**search():** `limit` → renamed to `topK`
|
||||
|
||||
## Common Issues
|
||||
|
||||
### TypeScript: `limit` is not a valid parameter
|
||||
|
||||
The `limit` parameter has been renamed to `topK` in the TypeScript OSS:
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const results = await m.search("query", { userId: "alice", limit: 20 });
|
||||
|
||||
// After
|
||||
const results = await m.search("query", { filters: { userId: "alice" }, topK: 20 });
|
||||
```
|
||||
|
||||
### TypeScript Client: snake_case params no longer work
|
||||
|
||||
All TypeScript Client SDK parameters now use camelCase. The SDK handles conversion to/from the API automatically:
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
await client.search("query", { user_id: "alice", top_k: 20 });
|
||||
|
||||
// After
|
||||
await client.search("query", { filters: { userId: "alice" }, topK: 20 });
|
||||
```
|
||||
|
||||
### `ValueError: Top-level entity parameters not supported in search() / get_all()`
|
||||
|
||||
`search()` and `get_all()` now require entity IDs inside `filters`. Top-level kwargs raise `ValueError`. This aligns the OSS SDK with the Platform API.
|
||||
|
||||
```python
|
||||
# Before
|
||||
results = m.search("query", user_id="alice", top_k=20)
|
||||
|
||||
# After
|
||||
results = m.search("query", filters={"user_id": "alice"}, top_k=20)
|
||||
```
|
||||
|
||||
`add()` and `delete_all()` continue to accept entity IDs as top-level kwargs.
|
||||
|
||||
### Search returns fewer results than before
|
||||
|
||||
The default `threshold` changed from `None` to `0.1`. Low-relevance results that were previously included are now filtered out. To restore the old behavior:
|
||||
|
||||
```python
|
||||
results = m.search("query", filters={"user_id": "alice"}, threshold=0.0)
|
||||
```
|
||||
|
||||
### spaCy model not found
|
||||
|
||||
If you see errors about missing spaCy models, download the required model:
|
||||
|
||||
```bash
|
||||
python -m spacy download en_core_web_sm
|
||||
```
|
||||
|
||||
If spaCy is not installed at all, install the NLP extras:
|
||||
|
||||
```bash
|
||||
pip install "mem0ai[nlp]"
|
||||
```
|
||||
|
||||
### Entity store collection creation fails
|
||||
|
||||
The entity store tries to create a `{collection_name}_entities` collection automatically. If your vector database has restricted permissions, pre-create this collection with the same embedding dimensions as your main collection.
|
||||
|
||||
### Score values are different from before
|
||||
|
||||
The top-level `score` still ranges `[0, 1]`, but it is computed differently in v3. Relative ranking between results stays comparable, but absolute numbers shift — retune any hard thresholds in your app against representative queries.
|
||||
|
||||
If you need the raw cosine similarity for a specific use case, run an unboosted vector query directly against your vector store via `vector_store.search(...)`.
|
||||
|
||||
## Need Help?
|
||||
|
||||
- Join our [Discord community](https://mem0.ai/discord) for real-time support
|
||||
- Open an issue on [GitHub](https://github.com/mem0ai/mem0/issues)
|
||||
- Check the [evaluation docs](/core-concepts/memory-evaluation) to benchmark the new algorithm on your data
|
||||
@@ -0,0 +1,328 @@
|
||||
---
|
||||
title: "Platform: Migrating to the New Memory Algorithm"
|
||||
description: "Guide for Mem0 Platform users to adopt the new memory algorithm with single-pass extraction, entity linking, and multi-signal retrieval."
|
||||
icon: "arrow-right"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**No action required for most users.** The new algorithm is rolling out automatically to all Mem0 Platform projects. This guide covers what changed, what to expect, and how to take full advantage of the new capabilities.
|
||||
</Info>
|
||||
|
||||
## Overview
|
||||
|
||||
The new Mem0 memory algorithm is a ground-up redesign of how memories are extracted, stored, and retrieved. It scores **91.6 on LoCoMo** and **93.4 on LongMemEval** — a +20 and +26 point improvement over the previous algorithm — while cutting extraction latency roughly in half.
|
||||
|
||||
| What Changed | Before | After |
|
||||
|---|---|---|
|
||||
| **Extraction** | Two LLM passes (extract + merge) | Single-pass ADD-only (one LLM call) |
|
||||
| **Memory mutations** | ADD, UPDATE, DELETE | ADD only — nothing is overwritten or deleted |
|
||||
| **Agent-generated facts** | Often ignored | First-class, stored with equal weight |
|
||||
| **Entity linking** | Not available | Entities extracted and linked across memories |
|
||||
| **Graph memory** | Separate graph store + dashboard visualization | Replaced by built-in entity linking, no graph visuals on platform dashboard |
|
||||
| **Retrieval** | Semantic (vector) only | Hybrid retrieval combining multiple signals |
|
||||
|
||||
## What This Means for Your Application
|
||||
|
||||
### Memories accumulate instead of being overwritten
|
||||
|
||||
The previous algorithm could UPDATE or DELETE existing memories during extraction. The new algorithm only adds new facts. When information changes (e.g., a user moves from New York to San Francisco), both facts are preserved with temporal context. This means:
|
||||
|
||||
- **More memories over time** — your memory count will grow rather than plateau
|
||||
- **Better temporal reasoning** — the system can distinguish "used to live in New York" from "now lives in San Francisco"
|
||||
- **No information loss** — facts that seemed contradictory but were actually complementary are preserved
|
||||
|
||||
<Tip>
|
||||
If your application previously relied on UPDATE/DELETE behavior to keep memory counts low, the new algorithm handles this at retrieval time instead. Multi-signal retrieval ranks the most relevant, current information higher without destroying historical context.
|
||||
</Tip>
|
||||
|
||||
### Agent-generated facts are now captured
|
||||
|
||||
Previously, when an agent said something like "I've booked your flight for March 3rd," the system would often ignore it and only store what the user explicitly stated. The new algorithm treats agent-generated facts as first-class memories. If your application involves agents that confirm actions, provide recommendations, or share information, you'll see significantly better recall on those interactions.
|
||||
|
||||
### Retrieval is hybrid now
|
||||
|
||||
Search now uses hybrid retrieval, which improves ranking quality — especially for queries involving exact keywords, proper nouns, or entities that appear across multiple memories. The response shape is unchanged:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"id": "mem-uuid",
|
||||
"memory": "User moved to San Francisco in January 2026",
|
||||
"score": 0.82,
|
||||
"metadata": {},
|
||||
"categories": ["location"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The top-level `score` remains a `[0, 1]` value. Relative ranking between results stays comparable to v2, but absolute numbers shift since the scoring method changed — retune any hard thresholds in your app against representative queries.
|
||||
|
||||
## API Changes
|
||||
|
||||
### New V3 Endpoints
|
||||
|
||||
The new algorithm is available through the V3 API. The endpoints split into per-operation paths:
|
||||
|
||||
| Operation | SDK method | Endpoint |
|
||||
|---|---|---|
|
||||
| Add memories | `client.add()` | `POST /v3/memories/add/` |
|
||||
| Search memories | `client.search()` | `POST /v3/memories/search/` |
|
||||
| Get all memories (paginated) | `client.get_all()` | `POST /v3/memories/` |
|
||||
|
||||
<Info>
|
||||
`get_all` / list now returns a paginated envelope: `{"count": int, "next": str | null, "previous": str | null, "results": [...]}`. Pass `page` and `page_size` as query params to paginate; defaults return the first page.
|
||||
</Info>
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-api-key")
|
||||
|
||||
# Add memories (same interface, improved extraction)
|
||||
result = client.add(
|
||||
messages=[
|
||||
{"role": "user", "content": "I just moved to San Francisco from New York"},
|
||||
{"role": "assistant", "content": "That's exciting! I'll update your location preferences."}
|
||||
],
|
||||
user_id="alice"
|
||||
)
|
||||
|
||||
# Search with multi-signal retrieval
|
||||
results = client.search(
|
||||
query="where does the user live?",
|
||||
filters={"user_id": "alice"}
|
||||
)
|
||||
|
||||
# List memories (paginated)
|
||||
page = client.get_all(filters={"user_id": "alice"}, page=1, page_size=50)
|
||||
# page == {"count": 123, "next": "...", "previous": None, "results": [...]}
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
# Add memories
|
||||
curl -X POST https://api.mem0.ai/v3/memories/add/ \
|
||||
-H "Authorization: Token your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"messages": [
|
||||
{"role": "user", "content": "I just moved to San Francisco from New York"},
|
||||
{"role": "assistant", "content": "That'\''s exciting! I'\''ll update your location preferences."}
|
||||
],
|
||||
"user_id": "alice"
|
||||
}'
|
||||
|
||||
# Search memories
|
||||
curl -X POST https://api.mem0.ai/v3/memories/search/ \
|
||||
-H "Authorization: Token your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "where does the user live?",
|
||||
"filters": {"user_id": "alice"}
|
||||
}'
|
||||
|
||||
# List memories (paginated)
|
||||
curl -X POST 'https://api.mem0.ai/v3/memories/?page=1&page_size=50' \
|
||||
-H "Authorization: Token your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"filters": {"user_id": "alice"}}'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Search Parameter Changes
|
||||
|
||||
| Parameter | V1/V2 | V3 | Notes |
|
||||
|---|---|---|---|
|
||||
| `top_k` | Supported | Supported (1-1000, default 10) | No change |
|
||||
| `threshold` | Default: none | Default: `0.1` | Pass `0.0` to disable |
|
||||
| `rerank` | Default: `true` | Default: `false` | Pass `true` to enable (adds latency) |
|
||||
| Entity IDs in `search` / `get_all` | Top-level | Inside `filters` dict | Top-level raises 400 |
|
||||
|
||||
### Response Format
|
||||
|
||||
**Add response** — asynchronous, returns an `event_id` for polling:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Memory processing has been queued for background execution",
|
||||
"status": "PENDING",
|
||||
"event_id": "evt-uuid"
|
||||
}
|
||||
```
|
||||
|
||||
Poll status via `GET /v1/event/{event_id}/` — status will be `SUCCEEDED` or `FAILED`.
|
||||
|
||||
**Search response** — combined multi-signal score per result:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"id": "mem-uuid",
|
||||
"memory": "User moved to San Francisco from New York in January 2026",
|
||||
"score": 0.82,
|
||||
"metadata": {},
|
||||
"categories": ["location"],
|
||||
"created_at": "2026-01-15T10:30:00Z",
|
||||
"updated_at": "2026-01-15T10:30:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**List response** — paginated envelope (new in V3):
|
||||
|
||||
```json
|
||||
{
|
||||
"count": 123,
|
||||
"next": "https://api.mem0.ai/v3/memories/?page=2&page_size=50",
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": "mem-uuid",
|
||||
"memory": "...",
|
||||
"metadata": {},
|
||||
"categories": [],
|
||||
"created_at": "2026-01-15T10:30:00Z",
|
||||
"updated_at": "2026-01-15T10:30:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## SDK Breaking Changes
|
||||
|
||||
Alongside the algorithm update, the Python and TypeScript client SDKs have been cleaned up. These changes affect how you initialize and call the client.
|
||||
|
||||
### Python Client SDK
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
# Before
|
||||
client = MemoryClient(
|
||||
api_key="...",
|
||||
org_id="org-1", # [REMOVED] Removed
|
||||
project_id="proj-1" # [REMOVED] Removed
|
||||
)
|
||||
client.add(messages, user_id="alice", async_mode=True, output_format="v1.1")
|
||||
|
||||
# After
|
||||
client = MemoryClient(api_key="...")
|
||||
client.add(messages, user_id="alice")
|
||||
# async_mode and output_format removed (async by default, v1.1 always)
|
||||
```
|
||||
|
||||
**Removed parameters:** `org_id`, `project_id`, `api_version`, `output_format`, `async_mode`, `enable_graph`, `immutable`, `expiration_date`, `filter_memories`, `batch_size`, `force_add_only`, `includes`, `excludes`, `keyword_search`, `org_name`, `project_name`
|
||||
|
||||
### TypeScript Client SDK
|
||||
|
||||
All parameters now use **camelCase** (the SDK handles conversion to/from the API automatically):
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const client = new MemoryClient({
|
||||
apiKey: "...",
|
||||
organizationId: "org-1", // [REMOVED] Removed
|
||||
projectId: "proj-1" // [REMOVED] Removed
|
||||
});
|
||||
await client.search("query", {
|
||||
user_id: "alice", // [REMOVED] snake_case
|
||||
top_k: 20, // [REMOVED] snake_case
|
||||
enable_graph: true // [REMOVED] Removed
|
||||
});
|
||||
|
||||
// After
|
||||
const client = new MemoryClient({ apiKey: "..." });
|
||||
await client.search("query", {
|
||||
filters: { userId: "alice" }, // [OK] inside filters
|
||||
topK: 20 // [OK] camelCase
|
||||
});
|
||||
```
|
||||
|
||||
**Removed:** `OutputFormat` enum, `API_VERSION` enum, `organizationId`, `projectId`, `organizationName`, `projectName`, `enableGraph`, `asyncMode`, `outputFormat`, `immutable`, `expirationDate`, `filterMemories`, `batchSize`, `forceAddOnly`, `includes`, `excludes`, `keywordSearch`
|
||||
|
||||
<Info>
|
||||
For the full list of parameter changes across all SDKs, see the [OSS migration guide](/migration/oss-v2-to-v3#removed-parameters-reference).
|
||||
</Info>
|
||||
|
||||
## Graph Memory → Entity Linking
|
||||
|
||||
Graph memory has been replaced by **built-in entity linking**. The changes:
|
||||
|
||||
- **Graph visualizations removed from the platform dashboard.** The graph view in your project dashboard is no longer available.
|
||||
- **`enable_graph` project setting removed.** The toggle is gone from the dashboard; the API parameter is ignored.
|
||||
- **No external graph store to configure.** Previously graph memory required a separate Neo4j (or similar) deployment. Entity linking runs natively inside the platform — nothing to provision, no connection strings to manage.
|
||||
- **Entity linking is the native replacement.** Entities (proper nouns, quoted text, compound noun phrases) are automatically extracted from every memory and linked across memories belonging to the same user. At search time, entities from the query are matched against this index and used to boost ranking. The boost is folded into the combined `score` returned on each result.
|
||||
|
||||
**No migration work is required.** Entity linking activates automatically for all projects on the new algorithm. Existing memories are not re-processed, but any new memories you add will be indexed for entity-based retrieval going forward.
|
||||
|
||||
<Note>
|
||||
If your application previously read graph relations from the API response (`relations` field on search results), note that this field is no longer populated. Entity relationships are now consumed indirectly through retrieval ranking, not exposed as a separate graph structure.
|
||||
</Note>
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
<Steps>
|
||||
<Step title="Review your search thresholds">
|
||||
The default `threshold` is now `0.1` (previously no threshold). If your application was relying on unfiltered results, explicitly pass `threshold=0.0` in your search calls to preserve the old behavior. In most cases, the new default is better — it filters out low-relevance noise.
|
||||
</Step>
|
||||
<Step title="Review reranking usage">
|
||||
Reranking is now `false` by default. If your application depended on reranked results, add `rerank=True` to your search calls. Note that reranking adds latency (~200-400ms) but can improve ordering quality for complex queries.
|
||||
</Step>
|
||||
<Step title="Update score handling (optional)">
|
||||
The top-level `score` field continues to work as before. It is now a combined multi-signal score (semantic + keyword + entity) rather than pure cosine similarity, so the absolute numbers will differ. Relative ranking remains comparable — if you have threshold-based filtering in your app, retune on a representative query set.
|
||||
</Step>
|
||||
<Step title="Adjust memory count expectations">
|
||||
With ADD-only extraction, memory counts will grow over time rather than being consolidated. This is by design — retrieval handles relevance ranking. If you have hard limits on memory count, consider using the memory expiration feature or periodic cleanup.
|
||||
</Step>
|
||||
<Step title="Test with representative queries">
|
||||
The biggest improvements are in temporal reasoning (+29.6 on LoCoMo), multi-hop queries (+23.1), and assistant memory recall (+53.6 on LongMemEval). Test queries in these categories to see the improvement.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
- **V1 and V2 endpoints continue to work.** There is no requirement to migrate to V3 endpoints immediately.
|
||||
- **Existing memories are preserved.** The new algorithm does not modify or re-process previously stored memories.
|
||||
- **Search response shape is unchanged.** The top-level `score` and `results[]` array are the same; existing code that reads `score` continues to work. What changed is the scoring method behind the number (multi-signal fusion instead of pure cosine), so the absolute values shift even when ranking stays comparable.
|
||||
- **List response shape changed.** `get_all` now returns a paginated envelope (`{count, next, previous, results}`) instead of a bare `{results: [...]}`. Update code that reads `response["results"]` to continue working, or switch to the client SDKs which handle both shapes.
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
| Metric | Previous Algorithm | New Algorithm |
|
||||
|---|---|---|
|
||||
| **LoCoMo Overall** | 71.4 | **91.6** (+20.2) |
|
||||
| **LongMemEval Overall** | 67.8 | **93.4** (+25.6) |
|
||||
| **Extraction latency (p50)** | ~2.0s | **~1.0s** |
|
||||
| **Mean tokens per query** | — | 6.8-7.0K (top200) |
|
||||
|
||||
All benchmarks were run on a production-representative stack — deliberately avoiding frontier models to keep numbers representative of real production workloads.
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Do I need to re-process my existing memories?">
|
||||
No. Existing memories remain as-is. New memories added after the rollout will use the new extraction algorithm. Both old and new memories are searchable through the same retrieval pipeline.
|
||||
</Accordion>
|
||||
<Accordion title="Will my memory count increase faster now?">
|
||||
Yes. The ADD-only approach means memories accumulate rather than being consolidated. This is intentional — the retrieval system handles ranking and relevance. If you need to manage memory volume, use the expiration date feature or the delete API.
|
||||
</Accordion>
|
||||
<Accordion title="Can I opt out of the new algorithm?">
|
||||
The new algorithm is the default for all platform users. If you have a specific need to use the previous extraction behavior, contact support.
|
||||
</Accordion>
|
||||
<Accordion title="How does entity linking affect my existing integrations?">
|
||||
Entity linking is automatic and transparent. It improves retrieval quality without requiring any changes to your integration. Entities are extracted from both new memories and search queries, and matched automatically.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Need Help?
|
||||
|
||||
If you run into issues during migration or have questions about the new algorithm:
|
||||
|
||||
- Join our [Discord community](https://mem0.ai/discord) for real-time support
|
||||
- Email us at [support@mem0.ai](mailto:support@mem0.ai)
|
||||
- Check the [API reference](/api-reference) for detailed endpoint documentation
|
||||
@@ -1,481 +0,0 @@
|
||||
---
|
||||
title: Migrating from v0.x to v1.0.0
|
||||
description: 'Complete guide to upgrade your Mem0 implementation to version 1.0.0 '
|
||||
icon: "arrow-right"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
**Breaking Changes Ahead!** Mem0 1.0.0 introduces several breaking changes. Please read this guide carefully before upgrading.
|
||||
</Warning>
|
||||
|
||||
## Overview
|
||||
|
||||
Mem0 1.0.0 is a major release that modernizes the API, improves performance, and adds powerful new features. This guide will help you migrate your existing v0.x implementation to the new version.
|
||||
|
||||
## Key Changes Summary
|
||||
|
||||
| Feature | v0.x | v1.0.0 | Migration Required |
|
||||
|---------|------|-------------|-------------------|
|
||||
| API Version | v1.0 supported | v1.0 **removed**, v1.1+ only | ✅ Yes |
|
||||
| Async Mode (Platform Client) | Optional/manual | Defaults to `True`, configurable | ⚠️ Partial |
|
||||
| Metadata Filtering | Basic | Enhanced with operators | ⚠️ Optional |
|
||||
| Reranking | Not available | Full support | ⚠️ Optional |
|
||||
|
||||
## Step-by-Step Migration
|
||||
|
||||
### 1. Update Installation
|
||||
|
||||
```bash
|
||||
# Update to the latest version
|
||||
pip install --upgrade mem0ai
|
||||
```
|
||||
|
||||
### 2. Remove Deprecated Parameters
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
from mem0 import Memory
|
||||
|
||||
# These parameters are no longer supported
|
||||
m = Memory()
|
||||
result = m.add(
|
||||
"I love pizza",
|
||||
user_id="alice",
|
||||
version="v1.0" # ❌ REMOVED
|
||||
)
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
from mem0 import Memory
|
||||
|
||||
# Clean, simplified API
|
||||
m = Memory()
|
||||
result = m.add(
|
||||
"I love pizza",
|
||||
user_id="alice"
|
||||
# version parameter removed
|
||||
)
|
||||
```
|
||||
|
||||
### 3. Update Configuration
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "qdrant",
|
||||
"config": {
|
||||
"host": "localhost",
|
||||
"port": 6333
|
||||
}
|
||||
},
|
||||
"version": "v1.0" # ❌ No longer supported
|
||||
}
|
||||
|
||||
m = Memory.from_config(config)
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "qdrant",
|
||||
"config": {
|
||||
"host": "localhost",
|
||||
"port": 6333
|
||||
}
|
||||
},
|
||||
"version": "v1.1" # ✅ v1.1 is the minimum supported version
|
||||
}
|
||||
|
||||
m = Memory.from_config(config)
|
||||
```
|
||||
|
||||
### 4. Handle Response Format Changes
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
# Response could be a list or dict depending on version
|
||||
result = m.add("I love coffee", user_id="alice")
|
||||
|
||||
if isinstance(result, list):
|
||||
# Handle list format
|
||||
for item in result:
|
||||
print(item["memory"])
|
||||
else:
|
||||
# Handle dict format
|
||||
print(result["results"])
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
# Response is always a standardized dict with "results" key
|
||||
result = m.add("I love coffee", user_id="alice")
|
||||
|
||||
# Always access via "results" key
|
||||
for item in result["results"]:
|
||||
print(item["memory"])
|
||||
```
|
||||
|
||||
### 5. Update Search Operations
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
# Basic search
|
||||
results = m.search("What do I like?", user_id="alice")
|
||||
|
||||
# With filters
|
||||
results = m.search(
|
||||
"What do I like?",
|
||||
user_id="alice",
|
||||
filters={"category": "food"}
|
||||
)
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
# Same basic search API
|
||||
results = m.search("What do I like?", user_id="alice")
|
||||
|
||||
# Enhanced filtering with operators (optional upgrade)
|
||||
results = m.search(
|
||||
"What do I like?",
|
||||
user_id="alice",
|
||||
filters={
|
||||
"AND": [
|
||||
{"category": "food"},
|
||||
{"rating": {"gte": 8}}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
# New: Reranking support (optional)
|
||||
results = m.search(
|
||||
"What do I like?",
|
||||
user_id="alice",
|
||||
rerank=True # Requires reranker configuration
|
||||
)
|
||||
```
|
||||
|
||||
### 6. Platform Client async_mode Default Changed
|
||||
|
||||
**Change:** For `MemoryClient`, the `async_mode` parameter now defaults to `True` for better performance.
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-key")
|
||||
|
||||
# Had to explicitly set async_mode
|
||||
result = client.add("I enjoy hiking", user_id="alice", async_mode=True)
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-key")
|
||||
|
||||
# async_mode now defaults to True (best performance)
|
||||
result = client.add("I enjoy hiking", user_id="alice")
|
||||
|
||||
# You can still override if needed for synchronous processing
|
||||
result = client.add("I enjoy hiking", user_id="alice", async_mode=False)
|
||||
```
|
||||
|
||||
## Configuration Migration
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
#### Before (v0.x)
|
||||
```python
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "qdrant",
|
||||
"config": {
|
||||
"host": "localhost",
|
||||
"port": 6333
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-3.5-turbo",
|
||||
"api_key": "your-key"
|
||||
}
|
||||
},
|
||||
"version": "v1.0"
|
||||
}
|
||||
```
|
||||
|
||||
#### After (v1.0.0 )
|
||||
```python
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "qdrant",
|
||||
"config": {
|
||||
"host": "localhost",
|
||||
"port": 6333
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-3.5-turbo",
|
||||
"api_key": "your-key"
|
||||
}
|
||||
},
|
||||
"version": "v1.1", # Minimum supported version
|
||||
|
||||
# New optional features
|
||||
"reranker": {
|
||||
"provider": "cohere",
|
||||
"config": {
|
||||
"model": "rerank-english-v3.0",
|
||||
"api_key": "your-cohere-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Enhanced Features (Optional)
|
||||
|
||||
```python
|
||||
# Take advantage of new features
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "qdrant",
|
||||
"config": {
|
||||
"host": "localhost",
|
||||
"port": 6333
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4",
|
||||
"api_key": "your-key"
|
||||
}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "text-embedding-3-small",
|
||||
"api_key": "your-key"
|
||||
}
|
||||
},
|
||||
"reranker": {
|
||||
"provider": "sentence_transformer",
|
||||
"config": {
|
||||
"model": "cross-encoder/ms-marco-MiniLM-L-6-v2"
|
||||
}
|
||||
},
|
||||
"version": "v1.1"
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling Migration
|
||||
|
||||
### Before (v0.x)
|
||||
```python
|
||||
try:
|
||||
result = m.add("memory", user_id="alice", version="v1.0")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
```
|
||||
|
||||
### After (v1.0.0 )
|
||||
```python
|
||||
try:
|
||||
result = m.add("memory", user_id="alice")
|
||||
except ValueError as e:
|
||||
if "v1.0 API format is no longer supported" in str(e):
|
||||
print("Please upgrade your code to use v1.1+ format")
|
||||
else:
|
||||
print(f"Error: {e}")
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {e}")
|
||||
```
|
||||
|
||||
## Testing Your Migration
|
||||
|
||||
### 1. Basic Functionality Test
|
||||
|
||||
```python
|
||||
def test_basic_functionality():
|
||||
m = Memory()
|
||||
|
||||
# Test add
|
||||
result = m.add("I love testing", user_id="test_user")
|
||||
assert "results" in result
|
||||
assert len(result["results"]) > 0
|
||||
|
||||
# Test search
|
||||
search_results = m.search("testing", user_id="test_user")
|
||||
assert "results" in search_results
|
||||
|
||||
# Test get_all
|
||||
all_memories = m.get_all(user_id="test_user")
|
||||
assert "results" in all_memories
|
||||
|
||||
print("✅ Basic functionality test passed")
|
||||
|
||||
test_basic_functionality()
|
||||
```
|
||||
|
||||
### 2. Enhanced Features Test
|
||||
|
||||
```python
|
||||
def test_enhanced_features():
|
||||
config = {
|
||||
"reranker": {
|
||||
"provider": "sentence_transformer",
|
||||
"config": {
|
||||
"model": "cross-encoder/ms-marco-MiniLM-L-6-v2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m = Memory.from_config(config)
|
||||
|
||||
# Test reranking
|
||||
m.add("I love advanced features", user_id="test_user")
|
||||
results = m.search("features", user_id="test_user", rerank=True)
|
||||
assert "results" in results
|
||||
|
||||
# Test enhanced filtering
|
||||
results = m.search(
|
||||
"features",
|
||||
user_id="test_user",
|
||||
filters={"user_id": {"eq": "test_user"}}
|
||||
)
|
||||
assert "results" in results
|
||||
|
||||
print("✅ Enhanced features test passed")
|
||||
|
||||
test_enhanced_features()
|
||||
```
|
||||
|
||||
## Common Migration Issues
|
||||
|
||||
### Issue 1: Version Error
|
||||
|
||||
**Error:**
|
||||
```
|
||||
ValueError: The v1.0 API format is no longer supported in mem0ai 1.0.0+
|
||||
```
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
# Remove version parameters or set to v1.1+
|
||||
config = {
|
||||
# ... other config
|
||||
"version": "v1.1" # or remove entirely for default
|
||||
}
|
||||
```
|
||||
|
||||
### Issue 2: Response Format Error
|
||||
|
||||
**Error:**
|
||||
```
|
||||
KeyError: 'results'
|
||||
```
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
# Always access response via "results" key
|
||||
result = m.add("memory", user_id="alice")
|
||||
memories = result["results"] # Not result directly
|
||||
```
|
||||
|
||||
### Issue 3: Parameter Error
|
||||
|
||||
**Error:**
|
||||
```
|
||||
TypeError: add() got an unexpected keyword argument 'output_format'
|
||||
```
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
# Remove deprecated parameters
|
||||
result = m.add(
|
||||
"memory",
|
||||
user_id="alice"
|
||||
# Remove: version
|
||||
)
|
||||
```
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If you encounter issues during migration:
|
||||
|
||||
### 1. Immediate Rollback
|
||||
|
||||
```bash
|
||||
# Downgrade to last v0.x version
|
||||
pip install mem0ai==0.1.20 # Replace with your last working version
|
||||
```
|
||||
|
||||
### 2. Gradual Migration
|
||||
|
||||
```python
|
||||
# Test both versions side by side
|
||||
import mem0_v0 # Your old version
|
||||
import mem0 # New version
|
||||
|
||||
def compare_results(query, user_id):
|
||||
old_results = mem0_v0.search(query, user_id=user_id)
|
||||
new_results = mem0.search(query, user_id=user_id)
|
||||
|
||||
print("Old format:", old_results)
|
||||
print("New format:", new_results["results"])
|
||||
```
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
### Before (v0.x)
|
||||
```python
|
||||
# Sequential operations
|
||||
result1 = m.add("memory 1", user_id="alice")
|
||||
result2 = m.add("memory 2", user_id="alice")
|
||||
result3 = m.search("query", user_id="alice")
|
||||
```
|
||||
|
||||
### After (v1.0.0 )
|
||||
```python
|
||||
# Better async performance
|
||||
async def batch_operations():
|
||||
async_memory = AsyncMemory()
|
||||
|
||||
# Concurrent operations
|
||||
results = await asyncio.gather(
|
||||
async_memory.add("memory 1", user_id="alice"),
|
||||
async_memory.add("memory 2", user_id="alice"),
|
||||
async_memory.search("query", user_id="alice")
|
||||
)
|
||||
return results
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Complete the migration** using this guide
|
||||
2. **Test thoroughly** with your existing data
|
||||
3. **Explore new features** like enhanced filtering and reranking
|
||||
4. **Update your documentation** to reflect the new API
|
||||
5. **Monitor performance** and optimize as needed
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Breaking Changes" icon="triangle-exclamation" href="/migration/breaking-changes">
|
||||
Detailed list of all breaking changes
|
||||
</Card>
|
||||
<Card title="API Changes" icon="code" href="/migration/api-changes">
|
||||
Complete API reference changes
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Info>
|
||||
Need help with migration? Check our [GitHub Discussions](https://github.com/mem0ai/mem0/discussions) or reach out to our community for support.
|
||||
</Info>
|
||||
@@ -130,7 +130,7 @@ memory = Memory.from_config_file("config.yaml")
|
||||
</Tabs>
|
||||
|
||||
<Info icon="check">
|
||||
Run `memory.add(["Remember my favorite cafe in Tokyo."], user_id="alex")` and then `memory.search("favorite cafe", user_id="alex")`. You should see the Qdrant collection populate and the reranker mark the memory as a top hit.
|
||||
Run `memory.add(["Remember my favorite cafe in Tokyo."], user_id="alex")` and then `memory.search("favorite cafe", filters={"user_id": "alex"})`. You should see the Qdrant collection populate and the reranker mark the memory as a top hit.
|
||||
</Info>
|
||||
|
||||
## Tune component settings
|
||||
|
||||
@@ -90,7 +90,7 @@ async def get_memory():
|
||||
|
||||
async def safe_memory_usage():
|
||||
async with get_memory() as memory:
|
||||
return await memory.search("test query", user_id="alice")
|
||||
return await memory.search("test query", filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
<Tip>
|
||||
@@ -145,7 +145,7 @@ async def robust_memory_search():
|
||||
memory = AsyncMemory()
|
||||
|
||||
async def search_operation():
|
||||
return await memory.search("test query", user_id="alice")
|
||||
return await memory.search("test query", filters={"user_id": "alice"})
|
||||
|
||||
return await with_timeout_and_retry(search_operation)
|
||||
```
|
||||
@@ -173,11 +173,11 @@ result = await memory.add(
|
||||
# Search memories
|
||||
results = await memory.search(
|
||||
query="Where am I travelling?",
|
||||
user_id="alice"
|
||||
filters={"user_id": "alice"}
|
||||
)
|
||||
|
||||
# List memories
|
||||
all_memories = await memory.get_all(user_id="alice")
|
||||
all_memories = await memory.get_all(filters={"user_id": "alice"})
|
||||
|
||||
# Get a specific memory
|
||||
specific_memory = await memory.get(memory_id="memory-id-here")
|
||||
@@ -213,13 +213,11 @@ await memory.add(
|
||||
run_id="consultation-001"
|
||||
)
|
||||
|
||||
all_user_memories = await memory.get_all(user_id="alice")
|
||||
agent_memories = await memory.get_all(user_id="alice", agent_id="diet-assistant")
|
||||
session_memories = await memory.get_all(user_id="alice", run_id="consultation-001")
|
||||
all_user_memories = await memory.get_all(filters={"user_id": "alice"})
|
||||
agent_memories = await memory.get_all(filters={"user_id": "alice", "agent_id": "diet-assistant"})
|
||||
session_memories = await memory.get_all(filters={"user_id": "alice", "run_id": "consultation-001"})
|
||||
specific_memories = await memory.get_all(
|
||||
user_id="alice",
|
||||
agent_id="diet-assistant",
|
||||
run_id="consultation-001"
|
||||
filters={"user_id": "alice", "agent_id": "diet-assistant", "run_id": "consultation-001"}
|
||||
)
|
||||
|
||||
history = await memory.history(memory_id="memory-id-here")
|
||||
@@ -240,7 +238,7 @@ async_openai_client = AsyncOpenAI()
|
||||
async_memory = AsyncMemory()
|
||||
|
||||
async def chat_with_memories(message: str, user_id: str = "default_user") -> str:
|
||||
search_result = await async_memory.search(query=message, user_id=user_id, limit=3)
|
||||
search_result = await async_memory.search(query=message, filters={"user_id": user_id}, top_k=3)
|
||||
relevant_memories = search_result["results"]
|
||||
memories_str = "\n".join(f"- {entry['memory']}" for entry in relevant_memories)
|
||||
|
||||
@@ -255,7 +253,7 @@ async def chat_with_memories(message: str, user_id: str = "default_user") -> str
|
||||
]
|
||||
|
||||
response = await async_openai_client.chat.completions.create(
|
||||
model="gpt-4.1-nano-2025-04-14",
|
||||
model="gpt-5-mini",
|
||||
messages=messages
|
||||
)
|
||||
|
||||
@@ -280,7 +278,7 @@ async def handle_initialization_errors():
|
||||
try:
|
||||
config = MemoryConfig(
|
||||
vector_store={"provider": "chroma", "config": {"path": "./chroma_db"}},
|
||||
llm={"provider": "openai", "config": {"model": "gpt-4.1-nano-2025-04-14"}}
|
||||
llm={"provider": "openai", "config": {"model": "gpt-5-mini"}}
|
||||
)
|
||||
AsyncMemory(config=config)
|
||||
print("AsyncMemory initialized successfully")
|
||||
@@ -297,7 +295,7 @@ async def handle_memory_operation_errors():
|
||||
print(f"Invalid memory ID: {err}")
|
||||
|
||||
try:
|
||||
await memory.search(query="", user_id="alice")
|
||||
await memory.search(query="", filters={"user_id": "alice"})
|
||||
except ValueError as err:
|
||||
print(f"Invalid search query: {err}")
|
||||
```
|
||||
@@ -326,7 +324,7 @@ async def add_memory(messages: list, user_id: str):
|
||||
@app.get("/memories/search")
|
||||
async def search_memories(query: str, user_id: str, limit: int = 10):
|
||||
try:
|
||||
result = await memory.search(query=query, user_id=user_id, limit=limit)
|
||||
result = await memory.search(query=query, filters={"user_id": user_id}, top_k=limit)
|
||||
return {"status": "success", "data": result}
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc))
|
||||
|
||||
+18
-14
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: Custom Fact Extraction Prompt
|
||||
title: Custom Instructions
|
||||
description: Tailor fact extraction so Mem0 stores only the details you care about.
|
||||
icon: "wand-magic-sparkles"
|
||||
---
|
||||
|
||||
Custom fact extraction prompts let you decide exactly which facts Mem0 records from a conversation. Define a focused prompt, give a few examples, and Mem0 will add only the memories that match your use case.
|
||||
Custom instructions let you decide exactly which facts Mem0 records from a conversation. Define a focused prompt, give a few examples, and Mem0 will add only the memories that match your use case.
|
||||
|
||||
<Info>
|
||||
**You’ll use this when…**
|
||||
**You'll use this when...**
|
||||
- A project needs domain-specific facts (order numbers, customer info) without storing casual chatter.
|
||||
- You already have a clear schema for memories and want the LLM to follow it.
|
||||
- You must prevent irrelevant details from entering long-term storage.
|
||||
@@ -17,6 +17,10 @@ Custom fact extraction prompts let you decide exactly which facts Mem0 records f
|
||||
Prompts that are too broad cause unrelated facts to slip through. Keep instructions tight and test them with real transcripts.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
The `custom_fact_extraction_prompt` parameter has been renamed to `custom_instructions`. If you are upgrading from an older version, update your configuration accordingly.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Feature anatomy
|
||||
@@ -24,13 +28,13 @@ Custom fact extraction prompts let you decide exactly which facts Mem0 records f
|
||||
- **Prompt instructions:** Describe which entities or phrases to keep. Specific guidance keeps the extractor focused.
|
||||
- **Few-shot examples:** Show positive and negative cases so the model copies the right format.
|
||||
- **Structured output:** Responses return JSON with a `facts` array that Mem0 converts into individual memories.
|
||||
- **LLM configuration:** `custom_fact_extraction_prompt` (Python) or `customPrompt` (TypeScript) lives alongside your model settings.
|
||||
- **LLM configuration:** `custom_instructions` (Python) or `customInstructions` (TypeScript) lives alongside your model settings.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Prompt blueprint">
|
||||
1. State the allowed fact types.
|
||||
2. Include short examples that mirror production messages.
|
||||
3. Show both empty (`[]`) and populated outputs.
|
||||
1. State the allowed fact types.
|
||||
2. Include short examples that mirror production messages.
|
||||
3. Show both empty (`[]`) and populated outputs.
|
||||
4. Remind the model to return JSON with a `facts` key only.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -43,8 +47,8 @@ Custom fact extraction prompts let you decide exactly which facts Mem0 records f
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
custom_fact_extraction_prompt = """
|
||||
Please only extract entities containing customer support information, order details, and user information.
|
||||
custom_instructions = """
|
||||
Please only extract entities containing customer support information, order details, and user information.
|
||||
Here are some few shot examples:
|
||||
|
||||
Input: Hi.
|
||||
@@ -67,8 +71,8 @@ Return the facts and customer information in a json format as shown above.
|
||||
```
|
||||
|
||||
```ts TypeScript
|
||||
const customPrompt = `
|
||||
Please only extract entities containing customer support information, order details, and user information.
|
||||
const customInstructions = `
|
||||
Please only extract entities containing customer support information, order details, and user information.
|
||||
Here are some few shot examples:
|
||||
|
||||
Input: Hi.
|
||||
@@ -105,12 +109,12 @@ config = {
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4.1-nano-2025-04-14",
|
||||
"model": "gpt-5-mini",
|
||||
"temperature": 0.2,
|
||||
"max_tokens": 2000,
|
||||
}
|
||||
},
|
||||
"custom_fact_extraction_prompt": custom_fact_extraction_prompt,
|
||||
"custom_instructions": custom_instructions,
|
||||
"version": "v1.1"
|
||||
}
|
||||
|
||||
@@ -131,7 +135,7 @@ const config = {
|
||||
maxTokens: 1500,
|
||||
},
|
||||
},
|
||||
customPrompt: customPrompt,
|
||||
customInstructions: customInstructions,
|
||||
};
|
||||
|
||||
const memory = new Memory(config);
|
||||
@@ -1,306 +0,0 @@
|
||||
---
|
||||
title: Custom Update Memory Prompt
|
||||
description: Decide how Mem0 adds, updates, or deletes memories using your own rules.
|
||||
icon: "arrows-rotate"
|
||||
---
|
||||
|
||||
The custom update memory prompt tells Mem0 how to handle changes when new facts arrive. Craft the prompt so the LLM can compare incoming facts with existing memories and choose the right action.
|
||||
|
||||
<Info>
|
||||
**You’ll use this when…**
|
||||
- Stored memories need to stay consistent as users change preferences or correct past statements.
|
||||
- Your product has clear rules for when to add, update, delete, or leave a memory untouched.
|
||||
- You want traceable decisions (ADD, UPDATE, DELETE, NONE) for auditing or compliance.
|
||||
</Info>
|
||||
|
||||
<Warning>
|
||||
Prompts that mix instructions or omit examples can lead to wrong actions like deleting valid memories. Keep the language simple and test each action path.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Feature anatomy
|
||||
|
||||
- **Action verbs:** The prompt teaches the model to return `ADD`, `UPDATE`, `DELETE`, or `NONE` for every memory entry.
|
||||
- **ID retention:** Updates reuse the original memory ID so downstream systems maintain history.
|
||||
- **Old vs. new text:** Updates include `old_memory` so you can track what changed.
|
||||
- **Decision table:** Your prompt should explain when to use each action and show concrete examples.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Decision guide">
|
||||
| Action | When to choose it | Output details |
|
||||
| --- | --- | --- |
|
||||
| `ADD` | Fact is new and not stored yet | Generate a new ID and set `event: "ADD"`. |
|
||||
| `UPDATE` | Fact replaces older info about the same topic | Keep the original ID, include `old_memory`. |
|
||||
| `DELETE` | Fact contradicts the stored memory or you explicitly remove it | Keep ID, set `event: "DELETE"`. |
|
||||
| `NONE` | Fact matches existing memory or is irrelevant | Keep ID with `event: "NONE"`. |
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
---
|
||||
|
||||
## Configure it
|
||||
|
||||
### Author the prompt
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
UPDATE_MEMORY_PROMPT = """You are a smart memory manager which controls the memory of a system.
|
||||
You can perform four operations: (1) add into the memory, (2) update the memory, (3) delete from the memory, and (4) no change.
|
||||
|
||||
Based on the above four operations, the memory will change.
|
||||
|
||||
Compare newly retrieved facts with the existing memory. For each new fact, decide whether to:
|
||||
- ADD: Add it to the memory as a new element
|
||||
- UPDATE: Update an existing memory element
|
||||
- DELETE: Delete an existing memory element
|
||||
- NONE: Make no change (if the fact is already present or irrelevant)
|
||||
|
||||
There are specific guidelines to select which operation to perform:
|
||||
|
||||
1. **Add**: If the retrieved facts contain new information not present in the memory, then you have to add it by generating a new ID in the id field.
|
||||
- **Example**:
|
||||
- Old Memory:
|
||||
[
|
||||
{
|
||||
"id" : "0",
|
||||
"text" : "User is a software engineer"
|
||||
}
|
||||
]
|
||||
- Retrieved facts: ["Name is John"]
|
||||
- New Memory:
|
||||
{
|
||||
"memory" : [
|
||||
{
|
||||
"id" : "0",
|
||||
"text" : "User is a software engineer",
|
||||
"event" : "NONE"
|
||||
},
|
||||
{
|
||||
"id" : "1",
|
||||
"text" : "Name is John",
|
||||
"event" : "ADD"
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
2. **Update**: If the retrieved facts contain information that is already present in the memory but the information is totally different, then you have to update it.
|
||||
If the retrieved fact contains information that conveys the same thing as the elements present in the memory, then you have to keep the fact which has the most information.
|
||||
Example (a) -- if the memory contains "User likes to play cricket" and the retrieved fact is "Loves to play cricket with friends", then update the memory with the retrieved facts.
|
||||
Example (b) -- if the memory contains "Likes cheese pizza" and the retrieved fact is "Loves cheese pizza", then you do not need to update it because they convey the same information.
|
||||
If the direction is to update the memory, then you have to update it.
|
||||
Please keep in mind while updating you have to keep the same ID.
|
||||
Please note to return the IDs in the output from the input IDs only and do not generate any new ID.
|
||||
- **Example**:
|
||||
- Old Memory:
|
||||
[
|
||||
{
|
||||
"id" : "0",
|
||||
"text" : "I really like cheese pizza"
|
||||
},
|
||||
{
|
||||
"id" : "1",
|
||||
"text" : "User is a software engineer"
|
||||
},
|
||||
{
|
||||
"id" : "2",
|
||||
"text" : "User likes to play cricket"
|
||||
}
|
||||
]
|
||||
- Retrieved facts: ["Loves chicken pizza", "Loves to play cricket with friends"]
|
||||
- New Memory:
|
||||
{
|
||||
"memory" : [
|
||||
{
|
||||
"id" : "0",
|
||||
"text" : "Loves cheese and chicken pizza",
|
||||
"event" : "UPDATE",
|
||||
"old_memory" : "I really like cheese pizza"
|
||||
},
|
||||
{
|
||||
"id" : "1",
|
||||
"text" : "User is a software engineer",
|
||||
"event" : "NONE"
|
||||
},
|
||||
{
|
||||
"id" : "2",
|
||||
"text" : "Loves to play cricket with friends",
|
||||
"event" : "UPDATE",
|
||||
"old_memory" : "User likes to play cricket"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
3. **Delete**: If the retrieved facts contain information that contradicts the information present in the memory, then you have to delete it. Or if the direction is to delete the memory, then you have to delete it.
|
||||
Please note to return the IDs in the output from the input IDs only and do not generate any new ID.
|
||||
- **Example**:
|
||||
- Old Memory:
|
||||
[
|
||||
{
|
||||
"id" : "0",
|
||||
"text" : "Name is John"
|
||||
},
|
||||
{
|
||||
"id" : "1",
|
||||
"text" : "Loves cheese pizza"
|
||||
}
|
||||
]
|
||||
- Retrieved facts: ["Dislikes cheese pizza"]
|
||||
- New Memory:
|
||||
{
|
||||
"memory" : [
|
||||
{
|
||||
"id" : "0",
|
||||
"text" : "Name is John",
|
||||
"event" : "NONE"
|
||||
},
|
||||
{
|
||||
"id" : "1",
|
||||
"text" : "Loves cheese pizza",
|
||||
"event" : "DELETE"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
4. **No Change**: If the retrieved facts contain information that is already present in the memory, then you do not need to make any changes.
|
||||
- **Example**:
|
||||
- Old Memory:
|
||||
[
|
||||
{
|
||||
"id" : "0",
|
||||
"text" : "Name is John"
|
||||
},
|
||||
{
|
||||
"id" : "1",
|
||||
"text" : "Loves cheese pizza"
|
||||
}
|
||||
]
|
||||
- Retrieved facts: ["Name is John"]
|
||||
- New Memory:
|
||||
{
|
||||
"memory" : [
|
||||
{
|
||||
"id" : "0",
|
||||
"text" : "Name is John",
|
||||
"event" : "NONE"
|
||||
},
|
||||
{
|
||||
"id" : "1",
|
||||
"text" : "Loves cheese pizza",
|
||||
"event" : "NONE"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Define the expected output format
|
||||
|
||||
<CodeGroup>
|
||||
```json Add
|
||||
{
|
||||
"memory": [
|
||||
{
|
||||
"id": "0",
|
||||
"text": "This information is new",
|
||||
"event": "ADD"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json Update
|
||||
{
|
||||
"memory": [
|
||||
{
|
||||
"id": "0",
|
||||
"text": "This information replaces the old information",
|
||||
"event": "UPDATE",
|
||||
"old_memory": "Old information"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json Delete
|
||||
{
|
||||
"memory": [
|
||||
{
|
||||
"id": "0",
|
||||
"text": "This information will be deleted",
|
||||
"event": "DELETE"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json No Change
|
||||
{
|
||||
"memory": [
|
||||
{
|
||||
"id": "0",
|
||||
"text": "No changes for this information",
|
||||
"event": "NONE"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Info icon="check">
|
||||
Consistent JSON structure makes it easy to parse decisions downstream or log them for auditing.
|
||||
</Info>
|
||||
|
||||
---
|
||||
|
||||
## See it in action
|
||||
|
||||
- Run reconciliation jobs that compare retrieved facts to existing memories.
|
||||
- Feed both sources into the custom prompt, then apply the returned actions (add new entries, update text, delete outdated facts).
|
||||
- Log each decision so product teams can review why a change happened.
|
||||
|
||||
<Note>
|
||||
The prompt works alongside `custom_fact_extraction_prompt`—fact extraction identifies candidate facts, and the update prompt decides how to merge them into long-term storage.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Verify the feature is working
|
||||
|
||||
- Test all four actions with targeted examples, including edge cases where facts differ only slightly.
|
||||
- Confirm update responses keep the original IDs and include `old_memory`.
|
||||
- Ensure delete actions only trigger when contradictions appear or when you explicitly request removal.
|
||||
|
||||
---
|
||||
|
||||
## Best practices
|
||||
|
||||
1. **Keep instructions brief:** Remove redundant wording so the LLM focuses on the decision logic.
|
||||
2. **Document your schema:** Share the prompt and examples with your team so everyone knows how memories evolve.
|
||||
3. **Track prompt versions:** When rules change, bump a version number and archive the prior prompt.
|
||||
4. **Review outputs regularly:** Skim audit logs weekly to spot drift or repeated mistakes.
|
||||
5. **Pair with monitoring:** Visualize counts of each action to detect spikes in deletes or updates.
|
||||
|
||||
---
|
||||
|
||||
## Compare prompts
|
||||
|
||||
| Feature | `custom_update_memory_prompt` | `custom_fact_extraction_prompt` |
|
||||
| --- | --- | --- |
|
||||
| Primary job | Decide memory actions (ADD/UPDATE/DELETE/NONE) | Pull facts from user and assistant messages |
|
||||
| Inputs | Retrieved facts + existing memory entries | Raw conversation turns |
|
||||
| Output | Structured memory array with events | Array of extracted facts |
|
||||
|
||||
---
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Design Fact Extraction" icon="sparkles" href="/open-source/features/custom-fact-extraction-prompt">
|
||||
Coordinate both prompts so fact extraction feeds clean inputs into the update flow.
|
||||
</Card>
|
||||
<Card title="Build Email Automations" icon="inbox" href="/cookbooks/operations/email-automation">
|
||||
See how update prompts keep customer profiles current in a working automation.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,435 +0,0 @@
|
||||
---
|
||||
title: Graph Memory
|
||||
description: "Layer relationships onto Mem0 search so agents remember who did what, when, and with whom."
|
||||
icon: "network-wired"
|
||||
---
|
||||
|
||||
Graph Memory extends Mem0 by persisting nodes and edges alongside embeddings, so recalls stitch together people, places, and events instead of just keywords.
|
||||
|
||||
<Info icon="sparkles">
|
||||
**You’ll use this when…**
|
||||
- Conversation history mixes multiple actors and objects that vectors alone blur together
|
||||
- Compliance or auditing demands a graph of who said what and when
|
||||
- Agent teams need shared context without duplicating every memory in each run
|
||||
</Info>
|
||||
|
||||
## How Graph Memory Maps Context
|
||||
|
||||
Mem0 extracts entities and relationships from every memory write, stores embeddings in your vector database, and mirrors relationships in a graph backend. On retrieval, vector search narrows candidates while the graph returns related context alongside the results.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Conversation] --> B(Extraction LLM)
|
||||
B --> C[Vector Store]
|
||||
B --> D[Graph Store]
|
||||
E[Query] --> C
|
||||
C --> F[Candidate Memories]
|
||||
F --> D
|
||||
D --> G[Contextual Recall]
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
<Steps>
|
||||
<Step title="Extract people, places, and facts">
|
||||
Mem0’s extraction LLM identifies entities, relationships, and timestamps from the conversation payload you send to `memory.add`.
|
||||
</Step>
|
||||
<Step title="Store vectors and edges together">
|
||||
Embeddings land in your configured vector database while nodes and edges flow into a graph backend (Neo4j, Memgraph, Neptune, Kuzu, or Apache AGE).
|
||||
</Step>
|
||||
<Step title="Expose graph context at search time">
|
||||
`memory.search` performs vector similarity (optionally reranked by your configured reranker) and returns the results list. Graph Memory runs in parallel and adds related entities in the `relations` array—it does not reorder the vector hits automatically.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Quickstart (Neo4j Aura)
|
||||
|
||||
<Info icon="clock">
|
||||
**Time to implement:** ~10 minutes · **Prerequisites:** Python 3.10+, Node.js 18+, Neo4j Aura DB (free tier)
|
||||
</Info>
|
||||
|
||||
Provision a free [Neo4j Aura](https://neo4j.com/product/auradb/) instance, copy the Bolt URI, username, and password, then follow the language tab that matches your stack.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Python">
|
||||
<Steps>
|
||||
<Step title="Install Mem0 with graph extras">
|
||||
```bash
|
||||
pip install "mem0ai[graph]"
|
||||
```
|
||||
</Step>
|
||||
<Step title="Export Neo4j credentials">
|
||||
```bash
|
||||
export NEO4J_URL="neo4j+s://<your-instance>.databases.neo4j.io"
|
||||
export NEO4J_USERNAME="neo4j"
|
||||
export NEO4J_PASSWORD="your-password"
|
||||
```
|
||||
</Step>
|
||||
<Step title="Add and recall a relationship">
|
||||
```python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
config = {
|
||||
"graph_store": {
|
||||
"provider": "neo4j",
|
||||
"config": {
|
||||
"url": os.environ["NEO4J_URL"],
|
||||
"username": os.environ["NEO4J_USERNAME"],
|
||||
"password": os.environ["NEO4J_PASSWORD"],
|
||||
"database": "neo4j",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memory = Memory.from_config(config)
|
||||
|
||||
conversation = [
|
||||
{"role": "user", "content": "Alice met Bob at GraphConf 2025 in San Francisco."},
|
||||
{"role": "assistant", "content": "Great! Logging that connection."},
|
||||
]
|
||||
|
||||
memory.add(conversation, user_id="demo-user")
|
||||
|
||||
results = memory.search(
|
||||
"Who did Alice meet at GraphConf?",
|
||||
user_id="demo-user",
|
||||
limit=3,
|
||||
rerank=True,
|
||||
)
|
||||
|
||||
for hit in results["results"]:
|
||||
print(hit["memory"])
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
<Tab title="TypeScript">
|
||||
<Steps>
|
||||
<Step title="Install the OSS SDK">
|
||||
```bash
|
||||
npm install mem0ai
|
||||
```
|
||||
</Step>
|
||||
<Step title="Load Neo4j credentials">
|
||||
```bash
|
||||
export NEO4J_URL="neo4j+s://<your-instance>.databases.neo4j.io"
|
||||
export NEO4J_USERNAME="neo4j"
|
||||
export NEO4J_PASSWORD="your-password"
|
||||
```
|
||||
</Step>
|
||||
<Step title="Enable graph memory and query it">
|
||||
```typescript
|
||||
import { Memory } from "mem0ai/oss";
|
||||
|
||||
const config = {
|
||||
enableGraph: true,
|
||||
graphStore: {
|
||||
provider: "neo4j",
|
||||
config: {
|
||||
url: process.env.NEO4J_URL!,
|
||||
username: process.env.NEO4J_USERNAME!,
|
||||
password: process.env.NEO4J_PASSWORD!,
|
||||
database: "neo4j",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const memory = new Memory(config);
|
||||
|
||||
const conversation = [
|
||||
{ role: "user", content: "Alice met Bob at GraphConf 2025 in San Francisco." },
|
||||
{ role: "assistant", content: "Great! Logging that connection." },
|
||||
];
|
||||
|
||||
await memory.add(conversation, { userId: "demo-user" });
|
||||
|
||||
const results = await memory.search(
|
||||
"Who did Alice meet at GraphConf?",
|
||||
{ userId: "demo-user", limit: 3, rerank: true }
|
||||
);
|
||||
|
||||
results.results.forEach((hit) => {
|
||||
console.log(hit.memory);
|
||||
});
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Info icon="check">
|
||||
Expect to see **Alice met Bob at GraphConf 2025** in the output. In Neo4j Browser run `MATCH (p:Person)-[r]->(q:Person) RETURN p,r,q LIMIT 5;` to confirm the edge exists.
|
||||
</Info>
|
||||
|
||||
<Note>
|
||||
Graph Memory enriches responses by adding related entities in the `relations` key. The ordering of `results` always comes from vector search (plus any reranker you configure); graph edges do not reorder those hits automatically.
|
||||
</Note>
|
||||
|
||||
## Operate Graph Memory Day-to-Day
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Refine extraction prompts">
|
||||
Guide which relationships become nodes and edges.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
config = {
|
||||
"graph_store": {
|
||||
"provider": "neo4j",
|
||||
"config": {
|
||||
"url": os.environ["NEO4J_URL"],
|
||||
"username": os.environ["NEO4J_USERNAME"],
|
||||
"password": os.environ["NEO4J_PASSWORD"],
|
||||
},
|
||||
"custom_prompt": "Please only capture people, organisations, and project links.",
|
||||
}
|
||||
}
|
||||
|
||||
memory = Memory.from_config(config_dict=config)
|
||||
```
|
||||
|
||||
```typescript TypeScript
|
||||
import { Memory } from "mem0ai/oss";
|
||||
|
||||
const config = {
|
||||
enableGraph: true,
|
||||
graphStore: {
|
||||
provider: "neo4j",
|
||||
config: {
|
||||
url: process.env.NEO4J_URL!,
|
||||
username: process.env.NEO4J_USERNAME!,
|
||||
password: process.env.NEO4J_PASSWORD!,
|
||||
},
|
||||
customPrompt: "Please only capture people, organisations, and project links.",
|
||||
}
|
||||
};
|
||||
|
||||
const memory = new Memory(config);
|
||||
```
|
||||
</CodeGroup>
|
||||
</Accordion>
|
||||
<Accordion title="Raise the confidence threshold">
|
||||
Keep noisy edges out of the graph by demanding higher extraction confidence.
|
||||
|
||||
```python
|
||||
config["graph_store"]["config"]["threshold"] = 0.75
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Toggle graph writes per request">
|
||||
Disable graph writes or reads when you only want vector behaviour.
|
||||
|
||||
```python
|
||||
memory.add(messages, user_id="demo-user", enable_graph=False)
|
||||
results = memory.search("marketing partners", user_id="demo-user", enable_graph=False)
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Organize multi-agent graphs">
|
||||
Separate or share context across agents and sessions with `user_id`, `agent_id`, and `run_id`.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
memory.add("I prefer Italian cuisine", { userId: "bob", agentId: "food-assistant" });
|
||||
memory.add("I'm allergic to peanuts", { userId: "bob", agentId: "health-assistant" });
|
||||
memory.add("I live in Seattle", { userId: "bob" });
|
||||
|
||||
const food = await memory.search("What food do I like?", { userId: "bob", agentId: "food-assistant" });
|
||||
const allergies = await memory.search("What are my allergies?", { userId: "bob", agentId: "health-assistant" });
|
||||
const location = await memory.search("Where do I live?", { userId: "bob" });
|
||||
```
|
||||
</CodeGroup>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Note>
|
||||
Monitor graph growth, especially on free tiers, by periodically cleaning dormant nodes: `MATCH (n) WHERE n.lastSeen < date() - duration('P90D') DETACH DELETE n`.
|
||||
</Note>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Neo4j connection refused">
|
||||
Confirm Bolt connectivity is enabled, credentials match Aura, and your IP is allow-listed. Retry after confirming the URI format is `neo4j+s://...`.
|
||||
</Accordion>
|
||||
<Accordion title="Neptune Analytics rejects requests">
|
||||
Ensure the graph identifier matches the vector dimension used by your embedder and that the IAM role allows `neptune-graph:*DataViaQuery` actions.
|
||||
</Accordion>
|
||||
<Accordion title="Graph store outage fallback">
|
||||
Catch the provider error and retry with `enable_graph=False` so vector-only search keeps serving responses while the graph backend recovers.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Decision Points
|
||||
|
||||
- Select the graph store that fits your deployment (managed Aura vs. self-hosted Neo4j vs. AWS Neptune vs. local Kuzu vs. Apache AGE on PostgreSQL).
|
||||
- Decide when to enable graph writes per request; routine conversations may stay vector-only to save latency.
|
||||
- Set a policy for pruning stale relationships so your graph stays fast and affordable.
|
||||
|
||||
## Provider setup
|
||||
|
||||
Choose your backend and expand the matching panel for configuration details and links.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Neo4j Aura or self-hosted">
|
||||
Install the APOC plugin for self-hosted deployments, then configure Mem0:
|
||||
|
||||
```typescript
|
||||
import { Memory } from "mem0ai/oss";
|
||||
|
||||
const config = {
|
||||
enableGraph: true,
|
||||
graphStore: {
|
||||
provider: "neo4j",
|
||||
config: {
|
||||
url: "neo4j+s://<HOST>",
|
||||
username: "neo4j",
|
||||
password: "<PASSWORD>",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const memory = new Memory(config);
|
||||
```
|
||||
|
||||
Additional docs: [Neo4j Aura Quickstart](https://neo4j.com/docs/aura/), [APOC installation](https://neo4j.com/docs/apoc/current/installation/).
|
||||
</Accordion>
|
||||
<Accordion title="Memgraph (Docker)">
|
||||
Run Memgraph Mage locally with schema introspection enabled:
|
||||
|
||||
```bash
|
||||
docker run -p 7687:7687 memgraph/memgraph-mage:latest --schema-info-enabled=True
|
||||
```
|
||||
|
||||
Then point Mem0 at the instance:
|
||||
|
||||
```python
|
||||
from mem0 import Memory
|
||||
|
||||
config = {
|
||||
"graph_store": {
|
||||
"provider": "memgraph",
|
||||
"config": {
|
||||
"url": "bolt://localhost:7687",
|
||||
"username": "memgraph",
|
||||
"password": "your-password",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
m = Memory.from_config(config_dict=config)
|
||||
```
|
||||
|
||||
Learn more: [Memgraph Docs](https://memgraph.com/docs).
|
||||
</Accordion>
|
||||
<Accordion title="Amazon Neptune Analytics">
|
||||
Match vector dimensions between Neptune and your embedder, enable public connectivity (if needed), and grant IAM permissions:
|
||||
|
||||
```python
|
||||
from mem0 import Memory
|
||||
|
||||
config = {
|
||||
"graph_store": {
|
||||
"provider": "neptune",
|
||||
"config": {
|
||||
"endpoint": "neptune-graph://<GRAPH_ID>",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
m = Memory.from_config(config_dict=config)
|
||||
```
|
||||
|
||||
Reference: [Neptune Analytics Guide](https://docs.aws.amazon.com/neptune/latest/analytics/).
|
||||
</Accordion>
|
||||
<Accordion title="Amazon Neptune DB (with external vectors)">
|
||||
Create a Neptune cluster, enable the public endpoint if you operate outside the VPC, and point Mem0 at the host:
|
||||
|
||||
```python
|
||||
from mem0 import Memory
|
||||
|
||||
config = {
|
||||
"graph_store": {
|
||||
"provider": "neptunedb",
|
||||
"config": {
|
||||
"collection_name": "<VECTOR_COLLECTION_NAME>",
|
||||
"endpoint": "neptune-graph://<HOST_ENDPOINT>",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
m = Memory.from_config(config_dict=config)
|
||||
```
|
||||
|
||||
Reference: [Accessing Data in Neptune DB](https://docs.aws.amazon.com/neptune/latest/userguide/).
|
||||
</Accordion>
|
||||
<Accordion title="Kuzu (embedded)">
|
||||
Kuzu runs in-process, so supply a path (or `:memory:`) for the database file:
|
||||
|
||||
```python
|
||||
config = {
|
||||
"graph_store": {
|
||||
"provider": "kuzu",
|
||||
"config": {
|
||||
"db": "/tmp/mem0-example.kuzu"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Kuzu will clear its state when using `:memory:` once the process exits. See the [Kuzu documentation](https://kuzudb.com/docs/) for advanced settings.
|
||||
</Accordion>
|
||||
<Accordion title="Apache AGE (PostgreSQL extension)">
|
||||
[Apache AGE](https://age.apache.org/) adds graph database capabilities to PostgreSQL, letting you run Cypher queries alongside SQL on the same server. Start AGE via Docker, then configure Mem0:
|
||||
|
||||
```bash
|
||||
docker run --name age-postgres \
|
||||
-e POSTGRES_DB=mem0_db \
|
||||
-e POSTGRES_USER=mem0_user \
|
||||
-e POSTGRES_PASSWORD=mem0_pass \
|
||||
-p 5432:5432 \
|
||||
-d apache/age
|
||||
```
|
||||
|
||||
```python
|
||||
from mem0 import Memory
|
||||
|
||||
config = {
|
||||
"graph_store": {
|
||||
"provider": "apache_age",
|
||||
"config": {
|
||||
"host": "localhost",
|
||||
"port": 5432,
|
||||
"database": "mem0_db",
|
||||
"username": "mem0_user",
|
||||
"password": "mem0_pass",
|
||||
"graph_name": "mem0_graph",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
m = Memory.from_config(config_dict=config)
|
||||
```
|
||||
|
||||
Apache AGE does not have a built-in vector index, so similarity search is computed client-side. This works well for moderate graph sizes; for very large graphs consider pairing AGE with pgvector for the vector store.
|
||||
|
||||
Reference: [Apache AGE documentation](https://age.apache.org/age-manual/master/index.html).
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Enhanced Metadata Filtering"
|
||||
description="Blend field-level filters with graph context to zero in on the right memories."
|
||||
icon="funnel"
|
||||
href="/open-source/features/metadata-filtering"
|
||||
/>
|
||||
<Card
|
||||
title="Reranker-Enhanced Search"
|
||||
description="Layer rerankers on top of vectors and graphs for the cleanest results."
|
||||
icon="sparkles"
|
||||
href="/open-source/features/reranker-search"
|
||||
/>
|
||||
</CardGroup>
|
||||
@@ -81,7 +81,7 @@ const messages = [
|
||||
}
|
||||
];
|
||||
|
||||
await client.add(messages, { user_id: "alice" });
|
||||
await client.add(messages, { userId: "alice" });
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -148,7 +148,7 @@ const messages = [
|
||||
}
|
||||
];
|
||||
|
||||
await client.add(messages, { user_id: "alice" });
|
||||
await client.add(messages, { userId: "alice" });
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -264,7 +264,7 @@ try {
|
||||
}
|
||||
}];
|
||||
|
||||
await client.add(messages, { user_id: "user123" });
|
||||
await client.add(messages, { userId: "user123" });
|
||||
console.log("Image processed successfully");
|
||||
} catch (error: any) {
|
||||
if (error.type === "invalid_image") {
|
||||
|
||||
@@ -41,7 +41,7 @@ messages = [
|
||||
|
||||
chat_completion = client.chat.completions.create(
|
||||
messages=messages,
|
||||
model="gpt-4.1-nano-2025-04-14",
|
||||
model="gpt-5-mini",
|
||||
user_id="alice"
|
||||
)
|
||||
```
|
||||
@@ -69,7 +69,7 @@ client = Mem0(config=config)
|
||||
|
||||
chat_completion = client.chat.completions.create(
|
||||
messages=[{"role": "user", "content": "What's the capital of France?"}],
|
||||
model="gpt-4.1-nano-2025-04-14"
|
||||
model="gpt-5-mini"
|
||||
)
|
||||
```
|
||||
|
||||
@@ -85,14 +85,14 @@ client = Mem0(api_key="m0-xxx")
|
||||
# Store preferences
|
||||
client.chat.completions.create(
|
||||
messages=[{"role": "user", "content": "I love Indian food but I'm allergic to cheese."}],
|
||||
model="gpt-4.1-nano-2025-04-14",
|
||||
model="gpt-5-mini",
|
||||
user_id="alice"
|
||||
)
|
||||
|
||||
# Later conversation reuses the memory
|
||||
response = client.chat.completions.create(
|
||||
messages=[{"role": "user", "content": "Suggest dinner options in San Francisco."}],
|
||||
model="gpt-4.1-nano-2025-04-14",
|
||||
model="gpt-5-mini",
|
||||
user_id="alice"
|
||||
)
|
||||
|
||||
@@ -131,7 +131,7 @@ print(response.choices[0].message.content)
|
||||
| `run_id` | `str` | Optional session/run identifier for short-lived flows. |
|
||||
| `metadata` | `dict` | Store extra fields alongside each memory entry. |
|
||||
| `filters` | `dict` | Restrict retrieval to specific memories while responding. |
|
||||
| `limit` | `int` | Cap how many memories Mem0 pulls into the context (default 10). |
|
||||
| `top_k` | `int` | Cap how many memories Mem0 pulls into the context (default 10). |
|
||||
|
||||
Other request fields mirror OpenAI’s chat completion API.
|
||||
|
||||
|
||||
@@ -15,9 +15,6 @@ Mem0 Open Source ships with capabilities that adapt memory behavior for producti
|
||||
## Choose your path
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Graph Memory" icon="network-wired" href="/open-source/features/graph-memory">
|
||||
Store entity relationships for multi-hop recall.
|
||||
</Card>
|
||||
<Card title="Advanced Metadata Filtering" icon="filter" href="/open-source/features/metadata-filtering">
|
||||
Query with logical operators and nested conditions.
|
||||
</Card>
|
||||
@@ -30,15 +27,12 @@ Mem0 Open Source ships with capabilities that adapt memory behavior for producti
|
||||
<Card title="Multimodal Support" icon="image" href="/open-source/features/multimodal-support">
|
||||
Process images, audio, and video memories.
|
||||
</Card>
|
||||
<Card title="Custom Fact Extraction" icon="wand-magic-sparkles" href="/open-source/features/custom-fact-extraction-prompt">
|
||||
<Card title="Custom Instructions" icon="wand-magic-sparkles" href="/open-source/features/custom-instructions">
|
||||
Tailor how facts are extracted from text.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Custom Memory Updates" icon="arrows-rotate" href="/open-source/features/custom-update-memory-prompt">
|
||||
Control memory refinement with custom instructions.
|
||||
</Card>
|
||||
<CardGroup cols={2}>
|
||||
<Card title="REST API" icon="code" href="/open-source/features/rest-api">
|
||||
HTTP endpoints for language-agnostic integrations.
|
||||
</Card>
|
||||
|
||||
@@ -222,7 +222,7 @@ config = {
|
||||
def smart_search(query, user_id, use_rerank=None):
|
||||
if use_rerank is None:
|
||||
use_rerank = len(query.split()) > 3
|
||||
return m.search(query, user_id=user_id, rerank=use_rerank)
|
||||
return m.search(query, filters={"user_id": user_id}, rerank=use_rerank)
|
||||
```
|
||||
|
||||
<Tip>
|
||||
@@ -233,10 +233,10 @@ def smart_search(query, user_id, use_rerank=None):
|
||||
|
||||
```python
|
||||
try:
|
||||
results = m.search("test query", user_id="alice", rerank=True)
|
||||
results = m.search("test query", filters={"user_id": "alice"}, rerank=True)
|
||||
except Exception as exc:
|
||||
print(f"Reranking failed: {exc}")
|
||||
results = m.search("test query", user_id="alice", rerank=False)
|
||||
results = m.search("test query", filters={"user_id": "alice"}, rerank=False)
|
||||
```
|
||||
|
||||
<Warning>
|
||||
@@ -247,7 +247,7 @@ except Exception as exc:
|
||||
|
||||
```python
|
||||
# Before: basic vector search
|
||||
results = m.search("query", user_id="alice")
|
||||
results = m.search("query", filters={"user_id": "alice"})
|
||||
|
||||
# After: same API with reranking enabled via config
|
||||
config = {
|
||||
@@ -260,7 +260,7 @@ config = {
|
||||
}
|
||||
|
||||
m = Memory.from_config(config)
|
||||
results = m.search("query", user_id="alice")
|
||||
results = m.search("query", filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
---
|
||||
@@ -321,7 +321,7 @@ results = m.search(
|
||||
]
|
||||
},
|
||||
rerank=True,
|
||||
limit=20
|
||||
top_k=20
|
||||
)
|
||||
```
|
||||
|
||||
@@ -366,7 +366,7 @@ results = m.search(
|
||||
user_id="reader123",
|
||||
filters={"content_type": "book_recommendation"},
|
||||
rerank=True,
|
||||
limit=10
|
||||
top_k=10
|
||||
)
|
||||
|
||||
for result in results["results"]:
|
||||
|
||||
@@ -43,7 +43,7 @@ await memory.add(messages, { userId: "alice", metadata: { category: "movie_recom
|
||||
|
||||
<Step title="Search memories">
|
||||
```ts
|
||||
const results = await memory.search("What do you know about me?", { userId: "alice" });
|
||||
const results = await memory.search("What do you know about me?", { filters: { userId: "alice" } });
|
||||
console.log(results);
|
||||
```
|
||||
|
||||
@@ -67,7 +67,7 @@ console.log(results);
|
||||
</Steps>
|
||||
|
||||
<Note>
|
||||
By default the Node SDK uses local-friendly settings (OpenAI `gpt-4.1-nano-2025-04-14`, `text-embedding-3-small`, in-memory vector store, and SQLite history). Swap components by passing a config as shown below.
|
||||
By default the Node SDK uses local-friendly settings (OpenAI `gpt-5-mini`, `text-embedding-3-small`, in-memory vector store, and SQLite history). Swap components by passing a config as shown below.
|
||||
</Note>
|
||||
|
||||
## Configure for production
|
||||
@@ -106,7 +106,7 @@ const memory = new Memory({
|
||||
|
||||
<CodeGroup>
|
||||
```ts Get all memories
|
||||
const allMemories = await memory.getAll({ userId: "alice" });
|
||||
const allMemories = await memory.getAll({ filters: { userId: "alice" } });
|
||||
console.log(allMemories);
|
||||
```
|
||||
|
||||
@@ -116,7 +116,7 @@ console.log(singleMemory);
|
||||
```
|
||||
|
||||
```ts Search memories
|
||||
const result = await memory.search("What do you know about me?", { userId: "alice" });
|
||||
const result = await memory.search("What do you know about me?", { filters: { userId: "alice" } });
|
||||
console.log(result);
|
||||
```
|
||||
|
||||
@@ -209,14 +209,6 @@ Mem0 offers granular configuration across vector stores, LLMs, embedders, and hi
|
||||
| `topP` | Probability threshold | All |
|
||||
| `topK` | Token count to keep | All |
|
||||
| `openaiBaseUrl` | Base URL override | OpenAI |
|
||||
</Accordion>
|
||||
<Accordion title="Graph store">
|
||||
| Parameter | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| `provider` | Graph store provider (e.g., `"neo4j"`) | `"neo4j"` |
|
||||
| `url` | Connection URL | `process.env.NEO4J_URL` |
|
||||
| `username` | Username | `process.env.NEO4J_USERNAME` |
|
||||
| `password` | Password | `process.env.NEO4J_PASSWORD` |
|
||||
</Accordion>
|
||||
<Accordion title="Embedder">
|
||||
| Parameter | Description | Default |
|
||||
@@ -230,7 +222,7 @@ Mem0 offers granular configuration across vector stores, LLMs, embedders, and hi
|
||||
| --- | --- | --- |
|
||||
| `historyDbPath` | Path to history database | `"{mem0_dir}/history.db"` |
|
||||
| `version` | API version | `"v1.0"` |
|
||||
| `customPrompt` | Custom processing prompt | `undefined` |
|
||||
| `customInstructions` | Custom processing prompt | `undefined` |
|
||||
</Accordion>
|
||||
<Accordion title="History store">
|
||||
| Parameter | Description | Default |
|
||||
@@ -273,7 +265,7 @@ const config = {
|
||||
}
|
||||
},
|
||||
disableHistory: false,
|
||||
customPrompt: "I'm a virtual assistant. I'm here to help you with your queries."
|
||||
customInstructions: "I'm a virtual assistant. I'm here to help you with your queries."
|
||||
};
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
@@ -37,12 +37,12 @@ Mem0 Open Source delivers the same adaptive memory engine as the platform, but p
|
||||
<Card title="Configure Components" icon="sliders" href="/open-source/configuration">
|
||||
LLM, embedder, vector store, reranker setup.
|
||||
</Card>
|
||||
<Card title="Graph Memory Capability" icon="network-wired" href="/open-source/features/graph-memory">
|
||||
Relationship-aware recall with Neo4j, Memgraph.
|
||||
</Card>
|
||||
<Card title="Tune Retrieval & Rerankers" icon="sparkles" href="/open-source/features/reranker-search">
|
||||
Hybrid retrieval and reranker controls.
|
||||
</Card>
|
||||
<Card title="Memory Evaluation" icon="chart-line" href="/core-concepts/memory-evaluation">
|
||||
Benchmarks and how Mem0 is tested.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
@@ -75,7 +75,7 @@ Mem0 Open Source delivers the same adaptive memory engine as the platform, but p
|
||||
|
||||
<Note>
|
||||
Mem0 OSS works out of the box with sensible defaults:
|
||||
- LLM: OpenAI `gpt-4.1-nano-2025-04-14` (via `OPENAI_API_KEY`)
|
||||
- LLM: OpenAI `gpt-5-mini` (via `OPENAI_API_KEY`)
|
||||
- Embeddings: OpenAI `text-embedding-3-small`
|
||||
- Vector store: Local Qdrant instance storing data at `/tmp/qdrant`
|
||||
- History store: SQLite database at `~/.mem0/history.db`
|
||||
|
||||
@@ -52,7 +52,7 @@ m.add(messages, user_id="alex")
|
||||
|
||||
<Step title="Search memories">
|
||||
```python
|
||||
results = m.search("What do you know about me?", user_id="alex")
|
||||
results = m.search("What do you know about me?", filters={"user_id": "alex"})
|
||||
print(results)
|
||||
```
|
||||
|
||||
@@ -79,7 +79,7 @@ print(results)
|
||||
|
||||
<Note>
|
||||
By default `Memory()` wires up:
|
||||
- OpenAI `gpt-4.1-nano-2025-04-14` for fact extraction and updates
|
||||
- OpenAI `gpt-5-mini` for fact extraction and updates
|
||||
- OpenAI `text-embedding-3-small` embeddings (1536 dimensions)
|
||||
- Qdrant vector store with on-disk data at `/tmp/qdrant`
|
||||
- SQLite history at `~/.mem0/history.db`
|
||||
|
||||
+532
-33
@@ -183,7 +183,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\nusers = client.users()\nprint(users)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\nusers = client.users()\nprint(users)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -717,7 +717,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\njson_schema = {pydantic_json_schema}\nfilters = {\n \"AND\": [\n {\"user_id\": \"alex\"}\n ]\n}\n\nresponse = client.create_memory_export(\n schema=json_schema,\n filters=filters\n)\nprint(response)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your_api_key\")\n\njson_schema = {pydantic_json_schema}\nfilters = {\n \"AND\": [\n {\"user_id\": \"alex\"}\n ]\n}\n\nresponse = client.create_memory_export(\n schema=json_schema,\n filters=filters\n)\nprint(response)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -845,7 +845,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"project_id\")\n\nmemory_export_id = \"<memory_export_id>\"\n\nresponse = client.get_memory_export(memory_export_id=memory_export_id)\nprint(response)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your_api_key\")\n\nmemory_export_id = \"<memory_export_id>\"\n\nresponse = client.get_memory_export(memory_export_id=memory_export_id)\nprint(response)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -1048,8 +1048,8 @@
|
||||
},
|
||||
"expiration_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD.",
|
||||
"format": "date",
|
||||
"description": "The date when the memory will expire. Format: YYYY-MM-DD.",
|
||||
"title": "Expiration date",
|
||||
"nullable": true,
|
||||
"default": null
|
||||
@@ -1096,7 +1096,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\n# Retrieve memories for a specific user\nuser_memories = client.get_all(user_id=\"<user_id>\")\n\nprint(user_memories)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\n# Retrieve memories for a specific user\nuser_memories = client.get_all(filters={\"user_id\": \"<user_id>\"})\n\nprint(user_memories)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -1203,11 +1203,11 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\nmessages = [\n {\"role\": \"user\", \"content\": \"<user-message>\"},\n {\"role\": \"assistant\", \"content\": \"<assistant-response>\"}\n]\n\nclient.add(messages, user_id=\"<user-id>\", version=\"v2\")"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your_api_key\")\n\nmessages = [\n {\"role\": \"user\", \"content\": \"<user-message>\"},\n {\"role\": \"assistant\", \"content\": \"<assistant-response>\"}\n]\n\nclient.add(messages, user_id=\"<user-id>\")"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst messages = [\n { role: \"user\", content: \"Hi, I'm Alex. I'm a vegetarian and I'm allergic to nuts.\" },\n { role: \"assistant\", content: \"Hello Alex! I've noted that you're a vegetarian and have a nut allergy. I'll keep this in mind for any food-related recommendations or discussions.\" }\n];\n\nclient.add(messages, { user_id: \"<user_id>\", version: \"v2\" })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst messages = [\n { role: \"user\", content: \"Hi, I'm Alex. I'm a vegetarian and I'm allergic to nuts.\" },\n { role: \"assistant\", content: \"Hello Alex! I've noted that you're a vegetarian and have a nut allergy. I'll keep this in mind for any food-related recommendations or discussions.\" }\n];\n\nclient.add(messages, { user_id: \"<user_id>\" })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
@@ -1233,7 +1233,7 @@
|
||||
"memories"
|
||||
],
|
||||
"description": "Delete memories by filter. At least one filter is required — previously omitting all filters silently deleted everything; now it returns a validation error.",
|
||||
"operationId": "memories_delete",
|
||||
"operationId": "memories_delete_all",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
@@ -1315,7 +1315,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\n# Delete all memories for a specific user\nclient.delete_all(user_id=\"<user_id>\")\n\n# Delete all memories for every user in the project (wildcard)\nclient.delete_all(user_id=\"*\")\n\n# Full project wipe — all four filters must be explicitly set to \"*\"\nclient.delete_all(user_id=\"*\", agent_id=\"*\", app_id=\"*\", run_id=\"*\")\n\n# NOTE: Calling delete_all() with no filters raises a validation error.\n# At least one filter is required to prevent accidental data loss."
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\n# Delete all memories for a specific user\nclient.delete_all(user_id=\"<user_id>\")\n\n# Delete all memories for every user in the project (wildcard)\nclient.delete_all(user_id=\"*\")\n\n# Full project wipe — all four filters must be explicitly set to \"*\"\nclient.delete_all(user_id=\"*\", agent_id=\"*\", app_id=\"*\", run_id=\"*\")\n\n# NOTE: Calling delete_all() with no filters raises a validation error.\n# At least one filter is required to prevent accidental data loss."
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -1393,8 +1393,8 @@
|
||||
},
|
||||
"expiration_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD.",
|
||||
"format": "date",
|
||||
"description": "The date when the memory will expire. Format: YYYY-MM-DD.",
|
||||
"title": "Expiration date",
|
||||
"nullable": true,
|
||||
"default": null
|
||||
@@ -1441,11 +1441,11 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\n# Retrieve memories with filters\nmemories = client.get_all(\n filters={\n \"AND\": [\n {\n \"user_id\": \"alex\"\n },\n {\n \"created_at\": {\n \"gte\": \"2024-07-01\",\n \"lte\": \"2024-07-31\"\n }\n }\n ]\n },\n version=\"v2\"\n)\n\nprint(memories)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\n# Retrieve memories with filters\nmemories = client.get_all(\n filters={\n \"AND\": [\n {\n \"user_id\": \"alex\"\n },\n {\n \"created_at\": {\n \"gte\": \"2024-07-01\",\n \"lte\": \"2024-07-31\"\n }\n }\n ]\n }\n)\n\nprint(memories)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst filters = {\n AND: [\n { user_id: 'alex' },\n { created_at: { gte: '2024-07-01', lte: '2024-07-31' } }\n ]\n};\n\nclient.getAll({ filters, api_version: 'v2' })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst filters = {\n AND: [\n { user_id: 'alex' },\n { created_at: { gte: '2024-07-01', lte: '2024-07-31' } }\n ]\n};\n\nclient.getAll({ filters })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
@@ -1540,8 +1540,8 @@
|
||||
},
|
||||
"expiration_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD.",
|
||||
"format": "date",
|
||||
"description": "The date when the memory will expire. Format: YYYY-MM-DD.",
|
||||
"title": "Expiration date",
|
||||
"nullable": true,
|
||||
"default": null
|
||||
@@ -1589,11 +1589,11 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\nquery = \"Your search query here\"\n\nresults = client.search(query, user_id=\"<user_id>\", output_format=\"v1.1\")\nprint(results)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\nquery = \"Your search query here\"\n\nresults = client.search(query, filters={\"user_id\": \"<user_id>\"})\nprint(results)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst query = \"Your search query here\";\n\nclient.search(query, { user_id: \"<user_id>\", output_format: \"v1.1\" })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst query = \"Your search query here\";\n\nclient.search(query, { user_id: \"<user_id>\" })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
@@ -1675,8 +1675,8 @@
|
||||
},
|
||||
"expiration_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD.",
|
||||
"format": "date",
|
||||
"description": "The date when the memory will expire. Format: YYYY-MM-DD.",
|
||||
"title": "Expiration date",
|
||||
"nullable": true,
|
||||
"default": null
|
||||
@@ -1708,11 +1708,11 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\nquery = \"What do you know about me?\"\nfilters = {\n \"OR\":[\n {\n \"user_id\":\"alex\"\n },\n {\n \"agent_id\":{\n \"in\":[\n \"travel-assistant\",\n \"customer-support\"\n ]\n }\n }\n ]\n}\nclient.search(query, version=\"v2\", filters=filters)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\nquery = \"What do you know about me?\"\nfilters = {\n \"OR\":[\n {\n \"user_id\":\"alex\"\n },\n {\n \"agent_id\":{\n \"in\":[\n \"travel-assistant\",\n \"customer-support\"\n ]\n }\n }\n ]\n}\nclient.search(query, filters=filters)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst query = \"What do you know about me?\";\nconst filters = {\n OR: [\n { user_id: \"alex\" },\n { agent_id: { in: [\"travel-assistant\", \"customer-support\"] } }\n ]\n};\n\nclient.search(query, { api_version: \"v2\", filters })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst query = \"What do you know about me?\";\nconst filters = {\n OR: [\n { user_id: \"alex\" },\n { agent_id: { in: [\"travel-assistant\", \"customer-support\"] } }\n ]\n};\n\nclient.search(query, { filters })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
@@ -1734,12 +1734,510 @@
|
||||
"x-codegen-request-body-name": "data"
|
||||
}
|
||||
},
|
||||
"/v3/memories/": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"memories"
|
||||
],
|
||||
"summary": "Get all memories (V3, paginated)",
|
||||
"description": "List memories scoped by filters, paginated. Entity IDs **must** be passed inside the `filters` object — top-level `user_id` / `agent_id` / `run_id` are rejected with 400. `filters` supports the same operator set as V2 search (`AND`, `OR`, `NOT`, `in`, `gte`, `lte`, etc.). Response is a paginated envelope; pass `page` and `page_size` as query parameters to step through results.",
|
||||
"operationId": "memories_list_v3",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "page",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"default": 1
|
||||
},
|
||||
"description": "1-indexed page number."
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "page_size",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 200,
|
||||
"default": 100
|
||||
},
|
||||
"description": "Results per page."
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"filters"
|
||||
],
|
||||
"properties": {
|
||||
"filters": {
|
||||
"type": "object",
|
||||
"description": "Entity and metadata filters. Must include at least one entity ID (`user_id`, `agent_id`, `app_id`, or `run_id`).",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"example": {
|
||||
"filters": {
|
||||
"user_id": "alice"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Paginated envelope of memories.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"count": {
|
||||
"type": "integer",
|
||||
"description": "Total number of memories matching the filters."
|
||||
},
|
||||
"next": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"nullable": true,
|
||||
"description": "URL for the next page, or `null` if this is the last page."
|
||||
},
|
||||
"previous": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"nullable": true,
|
||||
"description": "URL for the previous page, or `null` if this is the first page."
|
||||
},
|
||||
"results": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Unique memory identifier."
|
||||
},
|
||||
"memory": {
|
||||
"type": "string",
|
||||
"description": "The extracted memory fact."
|
||||
},
|
||||
"score": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"description": "Combined multi-signal relevance score in [0, 1] (search responses only)."
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "User-supplied metadata attached to the memory."
|
||||
},
|
||||
"categories": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"memory",
|
||||
"created_at"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"count",
|
||||
"next",
|
||||
"previous",
|
||||
"results"
|
||||
]
|
||||
},
|
||||
"example": {
|
||||
"count": 123,
|
||||
"next": "https://api.mem0.ai/v3/memories/?page=2&page_size=100",
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": "mem-uuid",
|
||||
"memory": "User moved to San Francisco from New York in January 2026",
|
||||
"metadata": {},
|
||||
"categories": [
|
||||
"location"
|
||||
],
|
||||
"created_at": "2026-01-15T10:30:00Z",
|
||||
"updated_at": "2026-01-15T10:30:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Validation error — e.g. empty `filters` or no positively-scoped entity ID."
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized — missing or invalid API key."
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"tokenAuth": []
|
||||
}
|
||||
],
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl -X POST 'https://api.mem0.ai/v3/memories/?page=1&page_size=50' \\\n -H \"Authorization: Token <api-key>\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"filters\": {\"user_id\": \"alice\"}}'"
|
||||
},
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "from mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your-api-key\")\n\npage = client.get_all(filters={\"user_id\": \"alice\"}, page=1, page_size=50)\n# page == {\"count\": 123, \"next\": \"...\", \"previous\": None, \"results\": [...]}\nprint(page[\"count\"], len(page[\"results\"]))"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "import MemoryClient from \"mem0ai\";\n\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst page = await client.getAll({\n filters: { userId: \"alice\" },\n page: 1,\n pageSize: 50,\n});\nconsole.log(page.count, page.results.length);"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v3/memories/add/": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"memories"
|
||||
],
|
||||
"summary": "Add memories (V3)",
|
||||
"description": "Extract and store memories from a conversation using the V3 additive pipeline. Entity IDs (`user_id` / `agent_id` / `run_id`) are accepted at the top level. At least one entity ID is required so the memory is scoped to a session.",
|
||||
"operationId": "memories_add_v3",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"messages"
|
||||
],
|
||||
"properties": {
|
||||
"messages": {
|
||||
"type": "array",
|
||||
"description": "Conversation messages to extract memories from.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"user",
|
||||
"assistant",
|
||||
"system"
|
||||
]
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"role",
|
||||
"content"
|
||||
]
|
||||
}
|
||||
},
|
||||
"user_id": {
|
||||
"type": "string",
|
||||
"description": "Scope memories to this user."
|
||||
},
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"description": "Scope memories to this agent."
|
||||
},
|
||||
"run_id": {
|
||||
"type": "string",
|
||||
"description": "Scope memories to this session / run."
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "User-supplied metadata to attach to each extracted memory."
|
||||
},
|
||||
"custom_instructions": {
|
||||
"type": "string",
|
||||
"description": "Project-level instructions that guide extraction for this call."
|
||||
},
|
||||
"infer": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "When `false`, stores each message verbatim without running the extraction LLM."
|
||||
}
|
||||
}
|
||||
},
|
||||
"example": {
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I just moved to San Francisco from New York."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Got it — I'll update your location."
|
||||
}
|
||||
],
|
||||
"user_id": "alice"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Memory addition queued; returns an event identifier clients can poll via `GET /v1/event/{event_id}/`.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"PENDING",
|
||||
"SUCCEEDED",
|
||||
"FAILED"
|
||||
]
|
||||
},
|
||||
"event_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
},
|
||||
"example": {
|
||||
"message": "Memory processing has been queued for background execution",
|
||||
"status": "PENDING",
|
||||
"event_id": "2c4d1f44-4f7b-4b2f-9f6e-7b5b4f5a1234"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Validation error — e.g. missing `messages` or no entity ID supplied."
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized — missing or invalid API key."
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"tokenAuth": []
|
||||
}
|
||||
],
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl -X POST https://api.mem0.ai/v3/memories/add/ \\\n -H \"Authorization: Token <api-key>\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"messages\": [\n {\"role\": \"user\", \"content\": \"I just moved to San Francisco from New York.\"},\n {\"role\": \"assistant\", \"content\": \"Got it — I\\u0027ll update your location.\"}\n ],\n \"user_id\": \"alice\"\n }'"
|
||||
},
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "from mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your-api-key\")\n\nresult = client.add(\n messages=[\n {\"role\": \"user\", \"content\": \"I just moved to San Francisco from New York.\"},\n {\"role\": \"assistant\", \"content\": \"Got it — I'll update your location.\"}\n ],\n user_id=\"alice\",\n)\nprint(result)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "import MemoryClient from \"mem0ai\";\n\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst result = await client.add(\n [\n { role: \"user\", content: \"I just moved to San Francisco from New York.\" },\n { role: \"assistant\", content: \"Got it — I'll update your location.\" },\n ],\n { userId: \"alice\" }\n);\nconsole.log(result);"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v3/memories/search/": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"memories"
|
||||
],
|
||||
"summary": "Search memories (V3)",
|
||||
"description": "Relevance-ranked search across stored memories. V3 uses hybrid retrieval — the returned `score` is a combined `[0, 1]` value; per-signal component scores are not exposed on the response. Entity IDs **must** be passed inside the `filters` object — top-level `user_id` / `agent_id` / `run_id` are rejected with 400. At least one entity ID is required.",
|
||||
"operationId": "memories_search_v3",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"query",
|
||||
"filters"
|
||||
],
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Natural-language search query."
|
||||
},
|
||||
"filters": {
|
||||
"type": "object",
|
||||
"description": "Entity and metadata filters. Must include at least one entity ID (`user_id`, `agent_id`, `app_id`, or `run_id`). Supports `AND`, `OR`, `NOT`, and comparison operators (`in`, `gte`, `lte`, `gt`, `lt`, `contains`, `icontains`, `ne`).",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"top_k": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 1000,
|
||||
"default": 10,
|
||||
"description": "Number of results to return."
|
||||
},
|
||||
"threshold": {
|
||||
"type": "number",
|
||||
"minimum": 0.0,
|
||||
"maximum": 1.0,
|
||||
"default": 0.1,
|
||||
"description": "Minimum semantic relevance score. Pass `0.0` to disable filtering."
|
||||
},
|
||||
"rerank": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Apply the managed reranker for better ordering (adds latency)."
|
||||
}
|
||||
}
|
||||
},
|
||||
"example": {
|
||||
"query": "where does the user live?",
|
||||
"filters": {
|
||||
"user_id": "alice"
|
||||
},
|
||||
"top_k": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ranked search results.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"results": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Unique memory identifier."
|
||||
},
|
||||
"memory": {
|
||||
"type": "string",
|
||||
"description": "The extracted memory fact."
|
||||
},
|
||||
"score": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"description": "Combined multi-signal relevance score in [0, 1] (search responses only)."
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "User-supplied metadata attached to the memory."
|
||||
},
|
||||
"categories": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"memory",
|
||||
"created_at"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"results"
|
||||
]
|
||||
},
|
||||
"example": {
|
||||
"results": [
|
||||
{
|
||||
"id": "mem-uuid",
|
||||
"memory": "User moved to San Francisco from New York in January 2026",
|
||||
"score": 0.82,
|
||||
"metadata": {},
|
||||
"categories": [
|
||||
"location"
|
||||
],
|
||||
"created_at": "2026-01-15T10:30:00Z",
|
||||
"updated_at": "2026-01-15T10:30:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Validation error — e.g. empty `query`, missing `filters`, or no positively-scoped entity ID."
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized — missing or invalid API key."
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"tokenAuth": []
|
||||
}
|
||||
],
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl -X POST https://api.mem0.ai/v3/memories/search/ \\\n -H \"Authorization: Token <api-key>\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"query\": \"where does the user live?\",\n \"filters\": {\"user_id\": \"alice\"},\n \"top_k\": 10\n }'"
|
||||
},
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "from mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your-api-key\")\n\nresults = client.search(\n \"where does the user live?\",\n filters={\"user_id\": \"alice\"},\n top_k=10,\n)\nfor r in results[\"results\"]:\n print(r[\"memory\"], r[\"score\"])"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "import MemoryClient from \"mem0ai\";\n\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst results = await client.search(\"where does the user live?\", {\n filters: { userId: \"alice\" },\n topK: 10,\n});\nfor (const r of results.results) {\n console.log(r.memory, r.score);\n}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/memories/{entity_type}/{entity_id}/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"memories"
|
||||
],
|
||||
"operationId": "memories_read",
|
||||
"operationId": "memories_entity_read",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successfully retrieved memories.",
|
||||
@@ -1864,7 +2362,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\nmemory = client.get(memory_id=\"<memory_id>\")"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\nmemory = client.get(memory_id=\"<memory_id>\")"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -1989,7 +2487,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\n# Update a memory\nmemory_id = \"<memory_id>\"\nclient.update(\n memory_id=memory_id,\n text=\"Your updated memory message here\",\n metadata={\"category\": \"example\"}\n)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\n# Update a memory\nmemory_id = \"<memory_id>\"\nclient.update(\n memory_id=memory_id,\n text=\"Your updated memory message here\",\n metadata={\"category\": \"example\"}\n)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -2053,7 +2551,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\nmemory_id = \"<memory_id>\"\nclient.delete(memory_id=memory_id)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\nmemory_id = \"<memory_id>\"\nclient.delete(memory_id=memory_id)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -2199,7 +2697,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\n# Add some message to create history\nmessages = [{\"role\": \"user\", \"content\": \"<user-message>\"}]\nclient.add(messages, user_id=\"<user-id>\")\n\n# Add second message to update history\nmessages.append({\"role\": \"user\", \"content\": \"<user-message>\"})\nclient.add(messages, user_id=\"<user-id>\")\n\n# Get history of how memory changed over time\nmemory_id = \"<memory-id-here>\"\nhistory = client.history(memory_id)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\n# Add some message to create history\nmessages = [{\"role\": \"user\", \"content\": \"<user-message>\"}]\nclient.add(messages, user_id=\"<user-id>\")\n\n# Add second message to update history\nmessages.append({\"role\": \"user\", \"content\": \"<user-message>\"})\nclient.add(messages, user_id=\"<user-id>\")\n\n# Get history of how memory changed over time\nmemory_id = \"<memory-id-here>\"\nhistory = client.history(memory_id)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -3608,7 +4106,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\nresponse = client.get_project()\nprint(response)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your_api_key\")\n\nresponse = client.get_project()\nprint(response)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -4411,7 +4909,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\nupdate_memories = [\n {\n \"memory_id\": \"285ed74b-6e05-4043-b16b-3abd5b533496\",\n \"text\": \"Watches football\"\n },\n {\n \"memory_id\": \"2c9bd859-d1b7-4d33-a6b8-94e0147c4f07\",\n \"text\": \"Likes to travel\"\n }\n]\n\nresponse = client.batch_update(update_memories)\nprint(response)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\nupdate_memories = [\n {\n \"memory_id\": \"285ed74b-6e05-4043-b16b-3abd5b533496\",\n \"text\": \"Watches football\"\n },\n {\n \"memory_id\": \"2c9bd859-d1b7-4d33-a6b8-94e0147c4f07\",\n \"text\": \"Likes to travel\"\n }\n]\n\nresponse = client.batch_update(update_memories)\nprint(response)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -4490,7 +4988,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\ndelete_memories = [\n {\"memory_id\": \"285ed74b-6e05-4043-b16b-3abd5b533496\"},\n {\"memory_id\": \"2c9bd859-d1b7-4d33-a6b8-94e0147c4f07\"}\n]\n\nresponse = client.batch_delete(delete_memories)\nprint(response)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\ndelete_memories = [\n {\"memory_id\": \"285ed74b-6e05-4043-b16b-3abd5b533496\"},\n {\"memory_id\": \"2c9bd859-d1b7-4d33-a6b8-94e0147c4f07\"}\n]\n\nresponse = client.batch_delete(delete_memories)\nprint(response)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -4758,7 +5256,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\n# Create a webhook\nwebhook = client.create_webhook(\n url=\"https://your-webhook-url.com\",\n name=\"My Webhook\",\n project_id=\"your_project_id\",\n event_types=[\"memory:add\", \"memory:categorize\"]\n)\nprint(webhook)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\n# Create a webhook\nwebhook = client.create_webhook(\n url=\"https://your-webhook-url.com\",\n name=\"My Webhook\",\n project_id=\"your_project_id\",\n event_types=[\"memory:add\", \"memory:categorize\"]\n)\nprint(webhook)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -4998,7 +5496,7 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\n# Delete a webhook\nresponse = client.delete_webhook(webhook_id=\"your_webhook_id\")\nprint(response)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\")\n\n# Delete a webhook\nresponse = client.delete_webhook(webhook_id=\"your_webhook_id\")\nprint(response)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
@@ -5176,9 +5674,10 @@
|
||||
"nullable": true
|
||||
},
|
||||
"expiration_date": {
|
||||
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD",
|
||||
"description": "The date when the memory will expire. Format: YYYY-MM-DD",
|
||||
"title": "Expiration date",
|
||||
"type": "string",
|
||||
"format": "date",
|
||||
"nullable": true
|
||||
},
|
||||
"org_id": {
|
||||
|
||||
@@ -81,7 +81,6 @@ result = await memory.add(
|
||||
conversation,
|
||||
user_id="traveler-42",
|
||||
metadata={"trip": "japan-2025", "preferences": ["boutique", "no-shellfish"]},
|
||||
enable_graph=True,
|
||||
run_id="planning-call-1",
|
||||
)
|
||||
```
|
||||
@@ -101,7 +100,6 @@ const conversation = [
|
||||
const result = await memory.add(conversation, {
|
||||
userId: "traveler-42",
|
||||
metadata: { trip: "japan-2025", preferences: ["boutique", "no-shellfish"] },
|
||||
enableGraph: true,
|
||||
runId: "planning-call-1",
|
||||
});
|
||||
```
|
||||
@@ -163,10 +161,6 @@ await memory.update(matches.results[0].id, {
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Tip>
|
||||
Need to pause graph writes on a per-request basis? Pass `enableGraph: false` (TypeScript) or `enable_graph=False` (Python) when latency matters more than relationship building.
|
||||
</Tip>
|
||||
|
||||
## Clean up
|
||||
|
||||
<Tabs>
|
||||
@@ -192,8 +186,6 @@ await memory.deleteAll({ userId: "traveler-42", runId: "planning-call-1" });
|
||||
|
||||
## Quick recovery
|
||||
|
||||
- `Missing required key enableGraph`: update the SDK to `mem0ai>=0.4.0`.
|
||||
- `Graph backend unavailable`: retry with `enableGraph=False` and inspect your graph provider status.
|
||||
- Empty results with filters: log `filters` values and confirm metadata keys match (case-sensitive).
|
||||
|
||||
<Warning>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Advanced Retrieval
|
||||
description: "Advanced memory search with keyword expansion, intelligent reranking, and precision filtering"
|
||||
description: "Advanced memory search with intelligent reranking for precise results"
|
||||
---
|
||||
|
||||
## What is Advanced Retrieval?
|
||||
@@ -9,40 +9,6 @@ Advanced Retrieval gives you precise control over how memories are found and ran
|
||||
|
||||
## Search Enhancement Options
|
||||
|
||||
### Keyword Search
|
||||
|
||||
Expands results to include memories with specific terms, names, and technical keywords.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="When to Use">
|
||||
- Searching for specific entities, names, or technical terms
|
||||
- Need comprehensive coverage of a topic
|
||||
- Want broader recall even if some results are less relevant
|
||||
- Working with domain-specific terminology
|
||||
</Tab>
|
||||
<Tab title="How it Works">
|
||||
```python Python
|
||||
# Find memories containing specific food-related terms
|
||||
results = client.search(
|
||||
query="What foods should I avoid?",
|
||||
keyword_search=True,
|
||||
user_id="user123"
|
||||
)
|
||||
|
||||
# Results might include:
|
||||
# ✓ "Allergic to peanuts and shellfish"
|
||||
# ✓ "Lactose intolerant - avoid dairy"
|
||||
# ✓ "Mentioned avoiding gluten last week"
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Performance">
|
||||
- **Latency**: ~10ms additional
|
||||
- **Recall**: Significantly increased
|
||||
- **Precision**: Slightly decreased
|
||||
- **Best for**: Entity search, comprehensive coverage
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Reranking
|
||||
|
||||
Reorders results using deep semantic understanding to put the most relevant memories first.
|
||||
@@ -77,41 +43,6 @@ results = client.search(
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Memory Filtering
|
||||
|
||||
Filters results to keep only the most precisely relevant memories.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="When to Use">
|
||||
- Need highly specific, focused results
|
||||
- Working with large datasets where noise is problematic
|
||||
- Quality over quantity is essential
|
||||
- Building production or safety-critical applications
|
||||
</Tab>
|
||||
<Tab title="How it Works">
|
||||
```python Python
|
||||
# Get only the most relevant dietary restrictions
|
||||
results = client.search(
|
||||
query="What are my dietary restrictions?",
|
||||
filter_memories=True,
|
||||
user_id="user123"
|
||||
)
|
||||
|
||||
# Before filtering: After filtering:
|
||||
# • "Allergic to nuts" → • "Allergic to nuts"
|
||||
# • "Likes Italian food" → • "Vegetarian diet"
|
||||
# • "Vegetarian diet" →
|
||||
# • "Eats dinner at 7pm" →
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Performance">
|
||||
- **Latency**: 200-300ms additional
|
||||
- **Precision**: Maximized
|
||||
- **Recall**: May be reduced
|
||||
- **Best for**: Focused queries, production systems
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Real-World Use Cases
|
||||
|
||||
<Tabs>
|
||||
@@ -120,7 +51,6 @@ results = client.search(
|
||||
# Smart home assistant finding device preferences
|
||||
results = client.search(
|
||||
query="How do I like my bedroom temperature?",
|
||||
keyword_search=True, # Find specific temperature mentions
|
||||
rerank=True, # Get most recent preferences first
|
||||
user_id="user123"
|
||||
)
|
||||
@@ -133,8 +63,6 @@ results = client.search(
|
||||
# Find specific product issues with high precision
|
||||
results = client.search(
|
||||
query="Problems with premium subscription billing",
|
||||
keyword_search=True, # Find "premium", "billing", "subscription"
|
||||
filter_memories=True, # Only billing-related issues
|
||||
user_id="customer456"
|
||||
)
|
||||
|
||||
@@ -147,11 +75,10 @@ results = client.search(
|
||||
results = client.search(
|
||||
query="Patient allergies and contraindications",
|
||||
rerank=True, # Most important info first
|
||||
filter_memories=True, # Only medical restrictions
|
||||
user_id="patient789"
|
||||
)
|
||||
|
||||
# Ensures critical allergy info appears first and filters out non-medical data
|
||||
# Ensures critical allergy info appears first
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Learning Platform">
|
||||
@@ -159,7 +86,6 @@ results = client.search(
|
||||
# Find learning progress for specific topics
|
||||
results = client.search(
|
||||
query="Python programming progress and difficulties",
|
||||
keyword_search=True, # Find "Python", "programming", specific concepts
|
||||
rerank=True, # Recent progress first
|
||||
user_id="student123"
|
||||
)
|
||||
@@ -169,63 +95,57 @@ results = client.search(
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Choosing the Right Combination
|
||||
## Choosing the Right Configuration
|
||||
|
||||
### Recommended Configurations
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
# Fast and broad - good for exploration
|
||||
# Basic search - good for exploration
|
||||
def quick_search(query, user_id):
|
||||
return client.search(
|
||||
query=query,
|
||||
keyword_search=True,
|
||||
user_id=user_id
|
||||
)
|
||||
|
||||
# Balanced - good for most applications
|
||||
# Reranked search - good for most applications
|
||||
def standard_search(query, user_id):
|
||||
return client.search(
|
||||
query=query,
|
||||
keyword_search=True,
|
||||
rerank=True,
|
||||
user_id=user_id
|
||||
)
|
||||
|
||||
# High precision - good for critical applications
|
||||
# Reranked search - good for critical applications
|
||||
def precise_search(query, user_id):
|
||||
return client.search(
|
||||
query=query,
|
||||
rerank=True,
|
||||
filter_memories=True,
|
||||
user_id=user_id
|
||||
)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
// Fast and broad - good for exploration
|
||||
// Basic search - good for exploration
|
||||
function quickSearch(query, userId) {
|
||||
return client.search(query, {
|
||||
user_id: userId,
|
||||
keyword_search: true
|
||||
user_id: userId
|
||||
});
|
||||
}
|
||||
|
||||
// Balanced - good for most applications
|
||||
// Reranked search - good for most applications
|
||||
function standardSearch(query, userId) {
|
||||
return client.search(query, {
|
||||
user_id: userId,
|
||||
keyword_search: true,
|
||||
rerank: true
|
||||
});
|
||||
}
|
||||
|
||||
// High precision - good for critical applications
|
||||
// Reranked search - good for critical applications
|
||||
function preciseSearch(query, userId) {
|
||||
return client.search(query, {
|
||||
user_id: userId,
|
||||
rerank: true,
|
||||
filter_memories: true
|
||||
rerank: true
|
||||
});
|
||||
}
|
||||
```
|
||||
@@ -235,19 +155,15 @@ function preciseSearch(query, userId) {
|
||||
|
||||
### Do
|
||||
|
||||
- Start simple with just one enhancement and measure impact
|
||||
- Use keyword search for entity-heavy queries (names, places, technical terms)
|
||||
- Start simple with basic search and measure impact before enabling reranking
|
||||
- Use reranking when the top result quality matters most
|
||||
- Use filtering for production systems where precision is critical
|
||||
- Handle empty results gracefully when filtering is too aggressive
|
||||
- Monitor latency and adjust based on your application's needs
|
||||
- Handle empty results gracefully
|
||||
|
||||
### Don't
|
||||
|
||||
- Enable all options by default without measuring necessity
|
||||
- Use filtering for broad exploratory queries
|
||||
- Enable reranking by default without measuring necessity
|
||||
- Ignore latency impact in real-time applications
|
||||
- Forget to handle cases where filtering returns no results
|
||||
- Use advanced retrieval for simple, fast lookup scenarios
|
||||
|
||||
## Performance Guidelines
|
||||
@@ -261,20 +177,18 @@ import time
|
||||
start_time = time.time()
|
||||
results = client.search(
|
||||
query="user preferences",
|
||||
keyword_search=True, # +10ms
|
||||
rerank=True, # +150ms
|
||||
filter_memories=True, # +250ms
|
||||
user_id="user123"
|
||||
)
|
||||
latency = time.time() - start_time
|
||||
print(f"Search completed in {latency:.2f}s") # ~0.41s expected
|
||||
print(f"Search completed in {latency:.2f}s")
|
||||
```
|
||||
|
||||
### Optimization Tips
|
||||
|
||||
1. **Cache frequent queries** to avoid repeated advanced processing
|
||||
2. **Use session-specific search** with `run_id` to reduce search space
|
||||
3. **Implement fallback logic** when filtering returns empty results
|
||||
3. **Implement fallback logic** when search returns empty results
|
||||
4. **Monitor and alert** on search latency patterns
|
||||
|
||||
<Snippet file="get-help.mdx" />
|
||||
|
||||
@@ -50,7 +50,7 @@ const messages = [
|
||||
{"role": "user", "content": "Alice loves playing badminton"},
|
||||
{"role": "assistant", "content": "That's great! Alice is a fitness freak"},
|
||||
];
|
||||
await client.add(messages, { user_id: "alice" });
|
||||
await client.add(messages, { userId: "alice" });
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -62,11 +62,11 @@ Search for memories based on a query asynchronously.
|
||||
<CodeGroup>
|
||||
|
||||
```python Python
|
||||
await client.search("What is Alice's favorite sport?", user_id="alice")
|
||||
await client.search("What is Alice's favorite sport?", filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
await client.search("What is Alice's favorite sport?", { user_id: "alice" });
|
||||
await client.search("What is Alice's favorite sport?", { filters: { userId: "alice" } });
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -118,7 +118,7 @@ await client.delete_all(user_id="alice")
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
await client.deleteAll({ user_id: "alice" });
|
||||
await client.deleteAll({ userId: "alice" });
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
---
|
||||
title: Async Mode Default Change
|
||||
description: "The async_mode parameter now defaults to true for all memory additions, changing from synchronous processing."
|
||||
---
|
||||
|
||||
<Note type="warning">
|
||||
**Important Change**
|
||||
|
||||
The `async_mode` parameter defaults to `true` for all memory additions, changing the default API behavior to asynchronous processing.
|
||||
</Note>
|
||||
|
||||
## Overview
|
||||
|
||||
The Memory Addition API processes all memory additions asynchronously by default. This change improves performance and scalability by queuing memory operations in the background, allowing your application to continue without waiting for memory processing to complete.
|
||||
|
||||
## What's Changing
|
||||
|
||||
The parameter `async_mode` will default to `true` instead of `false`.
|
||||
|
||||
This means memory additions will be **processed asynchronously** by default - queued for background execution instead of waiting for processing to complete.
|
||||
|
||||
## Behavior Comparison
|
||||
|
||||
### Old Default Behavior (async_mode = false)
|
||||
|
||||
When `async_mode` was set to `false`, the API returned fully processed memory objects immediately:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"id": "de0ee948-af6a-436c-835c-efb6705207de",
|
||||
"event": "ADD",
|
||||
"memory": "User Order #1234 was for a 'Nova 2000'",
|
||||
"structured_attributes": {
|
||||
"day": 13,
|
||||
"hour": 16,
|
||||
"year": 2025,
|
||||
"month": 10,
|
||||
"minute": 59,
|
||||
"quarter": 4,
|
||||
"is_weekend": false,
|
||||
"day_of_week": "monday",
|
||||
"day_of_year": 286,
|
||||
"week_of_year": 42
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### New Default Behavior (async_mode = true)
|
||||
|
||||
With `async_mode` defaulting to `true`, memory processing is queued in the background and the API returns immediately:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"message": "Memory processing has been queued for background execution",
|
||||
"status": "PENDING",
|
||||
"event_id": "d7b5282a-0031-4cc2-98ba-5a02d8531e17"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### If You Need Synchronous Processing
|
||||
|
||||
If your integration relies on receiving the processed memory object immediately, you can explicitly set `async_mode` to `false` in your requests:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-api-key")
|
||||
|
||||
# Explicitly set async_mode=False to preserve synchronous behavior
|
||||
messages = [
|
||||
{"role": "user", "content": "I ordered a Nova 2000"}
|
||||
]
|
||||
|
||||
result = client.add(
|
||||
messages,
|
||||
user_id="user-123",
|
||||
async_mode=False # This ensures synchronous processing
|
||||
)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
const { MemoryClient } = require('mem0ai');
|
||||
|
||||
const client = new MemoryClient({ apiKey: 'your-api-key' });
|
||||
|
||||
// Explicitly set async_mode: false to preserve synchronous behavior
|
||||
const messages = [
|
||||
{ role: "user", content: "I ordered a Nova 2000" }
|
||||
];
|
||||
|
||||
const result = await client.add(messages, {
|
||||
user_id: "user-123",
|
||||
async_mode: false // This ensures synchronous processing
|
||||
});
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X POST https://api.mem0.ai/v1/memories/ \
|
||||
-H "Authorization: Token your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"messages": [
|
||||
{"role": "user", "content": "I ordered a Nova 2000"}
|
||||
],
|
||||
"user_id": "user-123",
|
||||
"async_mode": false
|
||||
}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
### If You Want to Adopt Asynchronous Processing
|
||||
|
||||
If you want to benefit from the improved performance of asynchronous processing:
|
||||
|
||||
1. **Remove** any explicit `async_mode=False` parameters from your code
|
||||
2. **Use webhooks** to receive notifications when memory processing completes
|
||||
|
||||
<Note>
|
||||
Learn more about [Webhooks](/platform/features/webhooks) for real-time notifications about memory events.
|
||||
</Note>
|
||||
|
||||
## Benefits of Asynchronous Processing
|
||||
|
||||
Switching to asynchronous processing provides several advantages:
|
||||
|
||||
- **Faster API Response Times**: Your application doesn't wait for memory processing
|
||||
- **Better Scalability**: Handle more memory additions concurrently
|
||||
- **Improved User Experience**: Reduced latency in your application
|
||||
- **Resource Efficiency**: Background processing optimizes server resources
|
||||
|
||||
## Important Notes
|
||||
|
||||
- The default behavior is now `async_mode=true` for asynchronous processing
|
||||
- Explicitly set `async_mode=false` if you need synchronous behavior
|
||||
- Use webhooks to receive notifications when memories are processed
|
||||
|
||||
## Monitoring Memory Processing
|
||||
|
||||
When using asynchronous mode, use webhooks to receive notifications about memory events:
|
||||
|
||||
<Card title="Configure Webhooks" icon="webhook" href="/platform/features/webhooks">
|
||||
Learn how to set up webhooks for memory processing events
|
||||
</Card>
|
||||
|
||||
You can also retrieve all processed memories at any time:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Python
|
||||
# Retrieve all memories for a user
|
||||
# Note: get_all now requires filters
|
||||
memories = client.get_all(filters={"AND": [{"user_id": "user-123"}]})
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
// Retrieve all memories for a user
|
||||
// Note: getAll now requires filters
|
||||
const memories = await client.getAll({ filters: {"AND": [{"user_id": "user-123"}]} });
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Need Help?
|
||||
|
||||
If you have questions about this change or need assistance updating your integration:
|
||||
|
||||
<Snippet file="get-help.mdx" />
|
||||
|
||||
## Related Documentation
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Async Client" icon="bolt" href="/platform/features/async-client">
|
||||
Learn about the asynchronous client for Mem0
|
||||
</Card>
|
||||
<Card title="Add Memories API" icon="plus" href="/api-reference/memory/add-memories">
|
||||
View the complete API reference for adding memories
|
||||
</Card>
|
||||
<Card title="Webhooks" icon="webhook" href="/platform/features/webhooks">
|
||||
Configure webhooks for memory processing events
|
||||
</Card>
|
||||
<Card title="Memory Operations" icon="gear" href="/core-concepts/memory-operations/add">
|
||||
Understand memory addition operations
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -25,7 +25,7 @@ const messages = [
|
||||
{"role": "assistant", "content": "Great! I'll remember your preference for Italian cuisine."}
|
||||
];
|
||||
|
||||
await client.add(messages, { user_id: "user123", version: "v2" });
|
||||
await client.add(messages, { userId: "user123", version: "v2" });
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -65,14 +65,14 @@ const messages1 = [
|
||||
{"role": "user", "content": "Hi, I'm Sarah from New York"},
|
||||
{"role": "assistant", "content": "Hello Sarah! Nice to meet you."}
|
||||
];
|
||||
await client.add(messages1, { user_id: "sarah", version: "v2" });
|
||||
await client.add(messages1, { userId: "sarah", version: "v2" });
|
||||
|
||||
// Later interaction - just send new messages
|
||||
const messages2 = [
|
||||
{"role": "user", "content": "I'm planning a trip to Italy next month"},
|
||||
{"role": "assistant", "content": "How exciting! Italy is beautiful this time of year."}
|
||||
];
|
||||
await client.add(messages2, { user_id: "sarah", version: "v2" });
|
||||
await client.add(messages2, { userId: "sarah", version: "v2" });
|
||||
// Mem0 automatically knows Sarah is from New York and can use this context
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -104,7 +104,7 @@ const messages = [
|
||||
{"role": "assistant", "content": "I've noted your allergies for future reference."}
|
||||
];
|
||||
|
||||
await client.add(messages, { user_id: "user123", version: "v2" });
|
||||
await client.add(messages, { userId: "user123", version: "v2" });
|
||||
// This allergy info will be available in ALL future interactions
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -143,21 +143,21 @@ const messages1 = [
|
||||
{"role": "user", "content": "I want to plan a 5-day trip to Tokyo"},
|
||||
{"role": "assistant", "content": "Perfect! Let's plan your Tokyo adventure."}
|
||||
];
|
||||
await client.add(messages1, { user_id: "user123", run_id: "tokyo-trip-2024", version: "v2" });
|
||||
await client.add(messages1, { userId: "user123", runId: "tokyo-trip-2024", version: "v2" });
|
||||
|
||||
// Later in the same trip planning session
|
||||
const messages2 = [
|
||||
{"role": "user", "content": "I prefer staying near Shibuya"},
|
||||
{"role": "assistant", "content": "Great choice! Shibuya is very convenient."}
|
||||
];
|
||||
await client.add(messages2, { user_id: "user123", run_id: "tokyo-trip-2024", version: "v2" });
|
||||
await client.add(messages2, { userId: "user123", runId: "tokyo-trip-2024", version: "v2" });
|
||||
|
||||
// Different session for work project (separate context)
|
||||
const workMessages = [
|
||||
{"role": "user", "content": "Let's discuss the Q4 marketing strategy"},
|
||||
{"role": "assistant", "content": "Sure! What are your main goals for Q4?"}
|
||||
];
|
||||
await client.add(workMessages, { user_id: "user123", run_id: "q4-marketing", version: "v2" });
|
||||
await client.add(workMessages, { userId: "user123", runId: "q4-marketing", version: "v2" });
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
@@ -38,11 +38,7 @@ Before defining any criteria, make sure to initialize the `MemoryClient` with yo
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(
|
||||
api_key="your_mem0_api_key",
|
||||
org_id="your_organization_id",
|
||||
project_id="your_project_id"
|
||||
)
|
||||
client = MemoryClient(api_key="your_mem0_api_key")
|
||||
```
|
||||
|
||||
### Define Your Criteria
|
||||
|
||||
@@ -98,7 +98,7 @@ messages = [
|
||||
]
|
||||
|
||||
# Add memories with project-level custom categories
|
||||
client.add(messages, user_id="alice", async_mode=False)
|
||||
client.add(messages, user_id="alice")
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -187,7 +187,7 @@ messages = [
|
||||
]
|
||||
|
||||
# Add memories with default categories
|
||||
client.add(messages, user_id='alice', async_mode=False)
|
||||
client.add(messages, user_id='alice')
|
||||
```
|
||||
|
||||
```python Memories with categories
|
||||
|
||||
@@ -33,7 +33,7 @@ Extract only health and wellness information:
|
||||
Exclude: Personal identifiers, financial data
|
||||
`;
|
||||
|
||||
await client.project.update({ custom_instructions: prompt });
|
||||
await client.project.update({ customInstructions: prompt });
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -60,11 +60,11 @@ print(response["custom_instructions"])
|
||||
|
||||
```javascript JavaScript
|
||||
// Set instructions for your project
|
||||
await client.project.update({ custom_instructions: "Your guidelines here..." });
|
||||
await client.project.update({ customInstructions: "Your guidelines here..." });
|
||||
|
||||
// Retrieve current instructions
|
||||
const response = await client.project.get({ fields: ["custom_instructions"] });
|
||||
console.log(response.custom_instructions);
|
||||
const response = await client.project.get({ fields: ["customInstructions"] });
|
||||
console.log(response.customInstructions);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -145,7 +145,7 @@ Extract customer service information for better support:
|
||||
Exclude: Payment card numbers, passwords, personal identifiers.
|
||||
`;
|
||||
|
||||
await client.project.update({ custom_instructions: instructions });
|
||||
await client.project.update({ customInstructions: instructions });
|
||||
```
|
||||
</CodeGroup>
|
||||
</Tab>
|
||||
@@ -198,7 +198,7 @@ Extract learning-related information for personalized education:
|
||||
Exclude: Specific grades, personal identifiers, financial information.
|
||||
`;
|
||||
|
||||
await client.project.update({ custom_instructions: educationPrompt });
|
||||
await client.project.update({ customInstructions: educationPrompt });
|
||||
```
|
||||
</CodeGroup>
|
||||
</Tab>
|
||||
@@ -251,7 +251,7 @@ Extract financial planning information for advisory services:
|
||||
Exclude: Account numbers, SSNs, passwords, specific financial amounts.
|
||||
`;
|
||||
|
||||
await client.project.update({ custom_instructions: financePrompt });
|
||||
await client.project.update({ customInstructions: financePrompt });
|
||||
```
|
||||
</CodeGroup>
|
||||
</Tab>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user