Compare commits

..
5 changed files with 70 additions and 19 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 178 KiB

+8 -6
View File
@@ -4539,7 +4539,7 @@
"x-code-samples": [ "x-code-samples": [
{ {
"lang": "Python", "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\")\n\n# Get all webhooks\nwebhooks = client.get_webhooks(project_id=\"your_project_id\")\nprint(webhooks)\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\"]\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# Get all webhooks\nwebhooks = client.get_webhooks(project_id=\"your_project_id\")\nprint(webhooks)\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", "lang": "JavaScript",
@@ -4606,7 +4606,8 @@
"enum": [ "enum": [
"memory:add", "memory:add",
"memory:update", "memory:update",
"memory:delete" "memory:delete",
"memory:categorize"
] ]
}, },
"description": "List of event types to subscribe to." "description": "List of event types to subscribe to."
@@ -4701,7 +4702,7 @@
"x-code-samples": [ "x-code-samples": [
{ {
"lang": "Python", "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\"]\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\", 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)"
}, },
{ {
"lang": "JavaScript", "lang": "JavaScript",
@@ -4767,7 +4768,8 @@
"enum": [ "enum": [
"memory:add", "memory:add",
"memory:update", "memory:update",
"memory:delete" "memory:delete",
"memory:categorize"
] ]
}, },
"description": "New list of event types to subscribe to" "description": "New list of event types to subscribe to"
@@ -4845,11 +4847,11 @@
"x-code-samples": [ "x-code-samples": [
{ {
"lang": "Python", "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\")\n\n# Update a webhook\nwebhook = client.update_webhook(\n webhook_id=\"your_webhook_id\",\n name=\"Updated Webhook\",\n url=\"https://new-webhook-url.com\",\n event_types=[\"memory:add\"]\n)\nprint(webhook)\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# Update a webhook\nwebhook = client.update_webhook(\n webhook_id=\"your_webhook_id\",\n name=\"Updated Webhook\",\n url=\"https://new-webhook-url.com\",\n event_types=[\"memory:add\", \"memory:categorize\"]\n)\nprint(webhook)\n\n# Delete a webhook\nresponse = client.delete_webhook(webhook_id=\"your_webhook_id\")\nprint(response)"
}, },
{ {
"lang": "JavaScript", "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\n// Update a webhook\nclient.updateWebhook('your_webhook_id', {\n name: 'Updated Webhook',\n url: 'https://new-webhook-url.com',\n event_types: ['memory:add']\n})\n .then(webhook => console.log(webhook))\n .catch(err => console.error(err));\n\n// Delete a webhook\nclient.deleteWebhook('your_webhook_id')\n .then(response => console.log(response))\n .catch(err => console.error(err));" "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\n// Update a webhook\nclient.updateWebhook('your_webhook_id', {\n name: 'Updated Webhook',\n url: 'https://new-webhook-url.com',\n event_types: ['memory:add', 'memory:categorize']\n})\n .then(webhook => console.log(webhook))\n .catch(err => console.error(err));\n\n// Delete a webhook\nclient.deleteWebhook('your_webhook_id')\n .then(response => console.log(response))\n .catch(err => console.error(err));"
}, },
{ {
"lang": "cURL", "lang": "cURL",
+16 -3
View File
@@ -5,7 +5,7 @@ description: 'Configure and manage webhooks to receive real-time notifications a
## Overview ## Overview
Webhooks enable real-time notifications for memory events in your Mem0 project. Webhooks are configured at the project level, meaning each webhook is tied to a specific project and receives events solely from that project. You can configure webhooks to send HTTP POST requests to your specified URLs whenever memories are created, updated, or deleted. Webhooks enable real-time notifications for memory events in your Mem0 project. Webhooks are configured at the project level, meaning each webhook is tied to a specific project and receives events solely from that project. You can configure webhooks to send HTTP POST requests to your specified URLs whenever memories are created, updated, deleted, or categorized.
## Managing Webhooks ## Managing Webhooks
@@ -27,7 +27,7 @@ webhook = client.create_webhook(
url="https://your-app.com/webhook", url="https://your-app.com/webhook",
name="Memory Logger", name="Memory Logger",
project_id="proj_123", project_id="proj_123",
event_types=["memory_add"] event_types=["memory_add", "memory_categorize"]
) )
print(webhook) print(webhook)
``` ```
@@ -41,7 +41,7 @@ const webhook = await client.createWebhook({
url: "https://your-app.com/webhook", url: "https://your-app.com/webhook",
name: "Memory Logger", name: "Memory Logger",
projectId: "proj_123", projectId: "proj_123",
eventTypes: ["memory_add"] eventTypes: ["memory_add", "memory_categorize"]
}); });
console.log(webhook); console.log(webhook);
``` ```
@@ -167,11 +167,13 @@ Mem0 supports the following event types for webhooks:
- `memory_add`: Triggered when a memory is added. - `memory_add`: Triggered when a memory is added.
- `memory_update`: Triggered when an existing memory is updated. - `memory_update`: Triggered when an existing memory is updated.
- `memory_delete`: Triggered when a memory is deleted. - `memory_delete`: Triggered when a memory is deleted.
- `memory_categorize`: Triggered when a memory is categorized.
## Webhook Payload ## Webhook Payload
When a memory event occurs, Mem0 sends an HTTP POST request to your webhook URL with the following payload: When a memory event occurs, Mem0 sends an HTTP POST request to your webhook URL with the following payload:
**Memory add/update/delete payload:**
```json ```json
{ {
"event_details": { "event_details": {
@@ -184,6 +186,17 @@ When a memory event occurs, Mem0 sends an HTTP POST request to your webhook URL
} }
``` ```
**Memory categorize payload:**
```json
{
"event_details": {
"event": "CATEGORIZE",
"memory_id": "a1b2c3d4-e5f6-4g7h-8i9j-k0l1m2n3o4p5",
"categories": ["hobbies", "travel"]
}
}
```
## Best Practices ## Best Practices
1. **Implement Retry Logic**: Ensure your webhook endpoint can handle temporary failures. 1. **Implement Retry Logic**: Ensure your webhook endpoint can handle temporary failures.
+45 -9
View File
@@ -56,6 +56,7 @@ interface AddOptions {
custom_categories?: Array<Record<string, string>>; custom_categories?: Array<Record<string, string>>;
enable_graph?: boolean; enable_graph?: boolean;
output_format?: string; output_format?: string;
source?: string;
} }
interface SearchOptions { interface SearchOptions {
@@ -66,12 +67,14 @@ interface SearchOptions {
limit?: number; limit?: number;
keyword_search?: boolean; keyword_search?: boolean;
reranking?: boolean; reranking?: boolean;
source?: string;
} }
interface ListOptions { interface ListOptions {
user_id: string; user_id: string;
run_id?: string; run_id?: string;
page_size?: number; page_size?: number;
source?: string;
} }
interface MemoryItem { interface MemoryItem {
@@ -152,6 +155,7 @@ class PlatformProvider implements Mem0Provider {
opts.custom_categories = options.custom_categories; opts.custom_categories = options.custom_categories;
if (options.enable_graph) opts.enable_graph = options.enable_graph; if (options.enable_graph) opts.enable_graph = options.enable_graph;
if (options.output_format) opts.output_format = options.output_format; if (options.output_format) opts.output_format = options.output_format;
if (options.source) opts.source = options.source;
const result = await this.client.add(messages, opts); const result = await this.client.add(messages, opts);
return normalizeAddResult(result); return normalizeAddResult(result);
@@ -165,6 +169,7 @@ class PlatformProvider implements Mem0Provider {
if (options.threshold != null) opts.threshold = options.threshold; if (options.threshold != null) opts.threshold = options.threshold;
if (options.keyword_search != null) opts.keyword_search = options.keyword_search; if (options.keyword_search != null) opts.keyword_search = options.keyword_search;
if (options.reranking != null) opts.reranking = options.reranking; if (options.reranking != null) opts.reranking = options.reranking;
if (options.source) opts.source = options.source;
const results = await this.client.search(query, opts); const results = await this.client.search(query, opts);
return normalizeSearchResults(results); return normalizeSearchResults(results);
@@ -181,6 +186,7 @@ class PlatformProvider implements Mem0Provider {
const opts: Record<string, unknown> = { user_id: options.user_id }; const opts: Record<string, unknown> = { user_id: options.user_id };
if (options.run_id) opts.run_id = options.run_id; if (options.run_id) opts.run_id = options.run_id;
if (options.page_size != null) opts.page_size = options.page_size; if (options.page_size != null) opts.page_size = options.page_size;
if (options.source) opts.source = options.source;
const results = await this.client.getAll(opts); const results = await this.client.getAll(opts);
if (Array.isArray(results)) return results.map(normalizeMemoryItem); if (Array.isArray(results)) return results.map(normalizeMemoryItem);
@@ -247,6 +253,7 @@ class OSSProvider implements Mem0Provider {
// OSS SDK uses camelCase: userId/runId, not user_id/run_id // OSS SDK uses camelCase: userId/runId, not user_id/run_id
const addOpts: Record<string, unknown> = { userId: options.user_id }; const addOpts: Record<string, unknown> = { userId: options.user_id };
if (options.run_id) addOpts.runId = options.run_id; if (options.run_id) addOpts.runId = options.run_id;
if (options.source) addOpts.source = options.source;
const result = await this.memory.add(messages, addOpts); const result = await this.memory.add(messages, addOpts);
return normalizeAddResult(result); return normalizeAddResult(result);
} }
@@ -260,6 +267,7 @@ class OSSProvider implements Mem0Provider {
else if (options.top_k != null) opts.limit = options.top_k; else if (options.top_k != null) opts.limit = options.top_k;
if (options.keyword_search != null) opts.keyword_search = options.keyword_search; if (options.keyword_search != null) opts.keyword_search = options.keyword_search;
if (options.reranking != null) opts.reranking = options.reranking; if (options.reranking != null) opts.reranking = options.reranking;
if (options.source) opts.source = options.source;
const results = await this.memory.search(query, opts); const results = await this.memory.search(query, opts);
return normalizeSearchResults(results); return normalizeSearchResults(results);
@@ -276,6 +284,7 @@ class OSSProvider implements Mem0Provider {
// OSS SDK uses camelCase: userId/runId, not user_id/run_id // OSS SDK uses camelCase: userId/runId, not user_id/run_id
const getAllOpts: Record<string, unknown> = { userId: options.user_id }; const getAllOpts: Record<string, unknown> = { userId: options.user_id };
if (options.run_id) getAllOpts.runId = options.run_id; if (options.run_id) getAllOpts.runId = options.run_id;
if (options.source) getAllOpts.source = options.source;
const results = await this.memory.getAll(getAllOpts); const results = await this.memory.getAll(getAllOpts);
if (Array.isArray(results)) return results.map(normalizeMemoryItem); if (Array.isArray(results)) return results.map(normalizeMemoryItem);
if (results?.results && Array.isArray(results.results)) if (results?.results && Array.isArray(results.results))
@@ -607,6 +616,22 @@ const memoryPlugin = {
// Track current session ID for tool-level session scoping // Track current session ID for tool-level session scoping
let currentSessionId: string | undefined; let currentSessionId: string | undefined;
// Track current agent ID for per-agent memory isolation
let currentAgentId: string | undefined;
// Helper: extract agentId from session key (pattern: agent:<agentId>:...)
// Returns undefined for the "main" agent to preserve default behavior.
function extractAgentId(sessionKey: string): string | undefined {
const match = sessionKey.match(/^agent:([^:]+):/);
return match && match[1] !== "main" ? match[1] : undefined;
}
// Helper: get effective userId for Mem0 operations.
// Precedence: explicit override > current agentId (non-main) > configured userId.
function effectiveUserId(userIdOverride?: string): string {
return userIdOverride || currentAgentId || cfg.userId;
}
api.logger.info( api.logger.info(
`openclaw-mem0: registered (mode: ${cfg.mode}, user: ${cfg.userId}, graph: ${cfg.enableGraph}, autoRecall: ${cfg.autoRecall}, autoCapture: ${cfg.autoCapture})`, `openclaw-mem0: registered (mode: ${cfg.mode}, user: ${cfg.userId}, graph: ${cfg.enableGraph}, autoRecall: ${cfg.autoRecall}, autoCapture: ${cfg.autoCapture})`,
); );
@@ -614,7 +639,8 @@ const memoryPlugin = {
// Helper: build add options // Helper: build add options
function buildAddOptions(userIdOverride?: string, runId?: string): AddOptions { function buildAddOptions(userIdOverride?: string, runId?: string): AddOptions {
const opts: AddOptions = { const opts: AddOptions = {
user_id: userIdOverride || cfg.userId, user_id: effectiveUserId(userIdOverride),
source: "OPENCLAW",
}; };
if (runId) opts.run_id = runId; if (runId) opts.run_id = runId;
if (cfg.mode === "platform") { if (cfg.mode === "platform") {
@@ -633,12 +659,13 @@ const memoryPlugin = {
runId?: string, runId?: string,
): SearchOptions { ): SearchOptions {
const opts: SearchOptions = { const opts: SearchOptions = {
user_id: userIdOverride || cfg.userId, user_id: effectiveUserId(userIdOverride),
top_k: limit ?? cfg.topK, top_k: limit ?? cfg.topK,
limit: limit ?? cfg.topK, limit: limit ?? cfg.topK,
threshold: cfg.searchThreshold, threshold: cfg.searchThreshold,
keyword_search: true, keyword_search: true,
reranking: true, reranking: true,
source: "OPENCLAW",
}; };
if (runId) opts.run_id = runId; if (runId) opts.run_id = runId;
return opts; return opts;
@@ -924,25 +951,27 @@ const memoryPlugin = {
try { try {
let memories: MemoryItem[] = []; let memories: MemoryItem[] = [];
const uid = userId || cfg.userId; const uid = effectiveUserId(userId);
if (scope === "session") { if (scope === "session") {
if (currentSessionId) { if (currentSessionId) {
memories = await provider.getAll({ memories = await provider.getAll({
user_id: uid, user_id: uid,
run_id: currentSessionId, run_id: currentSessionId,
source: "OPENCLAW",
}); });
} }
} else if (scope === "long-term") { } else if (scope === "long-term") {
memories = await provider.getAll({ user_id: uid }); memories = await provider.getAll({ user_id: uid, source: "OPENCLAW" });
} else { } else {
// "all" — combine both scopes // "all" — combine both scopes
const longTerm = await provider.getAll({ user_id: uid }); const longTerm = await provider.getAll({ user_id: uid, source: "OPENCLAW" });
let session: MemoryItem[] = []; let session: MemoryItem[] = [];
if (currentSessionId) { if (currentSessionId) {
session = await provider.getAll({ session = await provider.getAll({
user_id: uid, user_id: uid,
run_id: currentSessionId, run_id: currentSessionId,
source: "OPENCLAW",
}); });
} }
const seen = new Set(longTerm.map((r) => r.id)); const seen = new Set(longTerm.map((r) => r.id));
@@ -1195,6 +1224,7 @@ const memoryPlugin = {
try { try {
const memories = await provider.getAll({ const memories = await provider.getAll({
user_id: cfg.userId, user_id: cfg.userId,
source: "OPENCLAW",
}); });
console.log(`Mode: ${cfg.mode}`); console.log(`Mode: ${cfg.mode}`);
console.log(`User: ${cfg.userId}`); console.log(`User: ${cfg.userId}`);
@@ -1222,9 +1252,12 @@ const memoryPlugin = {
api.on("before_agent_start", async (event, ctx) => { api.on("before_agent_start", async (event, ctx) => {
if (!event.prompt || event.prompt.length < 5) return; if (!event.prompt || event.prompt.length < 5) return;
// Track session ID // Track session ID and agent ID
const sessionId = (ctx as any)?.sessionKey ?? undefined; const sessionId = (ctx as any)?.sessionKey ?? undefined;
if (sessionId) currentSessionId = sessionId; if (sessionId) {
currentSessionId = sessionId;
currentAgentId = extractAgentId(sessionId);
}
try { try {
// Search long-term memories (user-scoped) // Search long-term memories (user-scoped)
@@ -1289,9 +1322,12 @@ const memoryPlugin = {
return; return;
} }
// Track session ID // Track session ID and agent ID
const sessionId = (ctx as any)?.sessionKey ?? undefined; const sessionId = (ctx as any)?.sessionKey ?? undefined;
if (sessionId) currentSessionId = sessionId; if (sessionId) {
currentSessionId = sessionId;
currentAgentId = extractAgentId(sessionId);
}
try { try {
// Extract messages, limiting to last 10 // Extract messages, limiting to last 10
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@mem0/openclaw-mem0", "name": "@mem0/openclaw-mem0",
"version": "0.1.0", "version": "0.1.2",
"type": "module", "type": "module",
"description": "Mem0 memory backend for OpenClaw — platform or self-hosted open-source", "description": "Mem0 memory backend for OpenClaw — platform or self-hosted open-source",
"license": "Apache-2.0", "license": "Apache-2.0",