Compare commits

..

4 Commits

Author SHA1 Message Date
kartik-mem0 0c49e1aa0b chore: remove integration page from the docs 2026-04-20 21:15:23 +05:30
Kartik 4e611e8dba docs: update memory tool list, CLI usage, and config file reading logic (#4861)
Co-authored-by: Livia Ellen <liviaellen@msn.com>
2026-04-20 20:09:45 +05:30
Kartik 5520226b5b fix: updating docs with v3 integrations updates (#4898) 2026-04-20 18:54:21 +05:30
Saket Aryan 00695e3113 ci(sdk): require changelog entry on version bump + harden TS telemetry (#4900) 2026-04-20 18:09:03 +05:30
60 changed files with 1709 additions and 1453 deletions
+40
View File
@@ -14,8 +14,48 @@ on:
- 'mem0/**'
- 'tests/**'
- 'embedchain/**'
- 'pyproject.toml'
jobs:
changelog_check:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Require CHANGELOG entry when Python SDK version changes
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
extract_version() {
python3 -c "import sys, re; m = re.search(r'^\s*version\s*=\s*\"([^\"]+)\"', sys.stdin.read(), re.M); print(m.group(1) if m else '')"
}
base_version=$(git show "$BASE_SHA:pyproject.toml" 2>/dev/null | extract_version || echo "")
head_version=$(extract_version < pyproject.toml)
echo "Base version: ${base_version:-<unknown>}"
echo "Head version: $head_version"
if [ -z "$base_version" ] || [ "$base_version" = "$head_version" ]; then
echo "pyproject.toml version unchanged — no CHANGELOG entry required."
exit 0
fi
echo "Detected version bump ${base_version} -> ${head_version}. Checking docs/changelog/sdk.mdx…"
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- docs/changelog/sdk.mdx | grep -q .; then
echo "Changelog update present in docs/changelog/sdk.mdx ✅"
else
echo "::error file=pyproject.toml::pyproject.toml version changed from ${base_version} to ${head_version} but docs/changelog/sdk.mdx was not updated in this PR. Add a new <Update> entry under the Python tab for v${head_version}."
exit 1
fi
check_changes:
runs-on: ubuntu-latest
outputs:
+36
View File
@@ -24,6 +24,42 @@ jobs:
ts_sdk:
- 'mem0-ts/**'
changelog_check:
needs: check_changes
if: github.event_name == 'pull_request' && needs.check_changes.outputs.ts_sdk_changed == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Require CHANGELOG entry when SDK version changes
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
base_version=$(git show "$BASE_SHA:mem0-ts/package.json" 2>/dev/null | jq -r .version || echo "")
head_version=$(jq -r .version mem0-ts/package.json)
echo "Base version: ${base_version:-<unknown>}"
echo "Head version: $head_version"
if [ -z "$base_version" ] || [ "$base_version" = "$head_version" ]; then
echo "mem0-ts/package.json version unchanged — no CHANGELOG entry required."
exit 0
fi
echo "Detected version bump ${base_version} -> ${head_version}. Checking docs/changelog/sdk.mdx…"
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- docs/changelog/sdk.mdx | grep -q .; then
echo "Changelog update present in docs/changelog/sdk.mdx ✅"
else
echo "::error file=mem0-ts/package.json::mem0-ts/package.json version changed from ${base_version} to ${head_version} but docs/changelog/sdk.mdx was not updated in this PR. Add a new <Update> entry under the TypeScript tab for v${head_version}."
exit 1
fi
build_ts_sdk:
needs: check_changes
if: needs.check_changes.outputs.ts_sdk_changed == 'true'
+13
View File
@@ -4,6 +4,19 @@ description: "Release notes for the OpenClaw plugin and agent harness."
mode: "wide"
---
<Update label="2026-04-20" description="v1.0.7">
**New Features:**
- **Chat-Based Setup:** Added chat-based Platform setup flow — users can now configure the plugin conversationally instead of editing config files manually
- **Installation Docs Rewrite:** Rewrote README and integration docs with chat-first setup, numbered manual steps.
**Improvements:**
- **SDK Upgrade:** Bumped `mem0ai` dependency to 3.0.1 for V3 API compatibility
- **Config Cleanup:** Dropped deprecated `orgId`, `projectId`, `enableGraph` config options; updated CLI prompts ([#4734](https://github.com/mem0ai/mem0/pull/4734), [#4764](https://github.com/mem0ai/mem0/pull/4764))
- **Noise Filtering:** Expanded noise patterns in memory add tool; handle leading text in JSON extraction
</Update>
<Update label="2026-04-11" description="v1.0.6">
**Bug Fixes:**
+7
View File
@@ -893,6 +893,13 @@ See the [OSS v1 to v2 migration guide](https://docs.mem0.ai/migration/oss-v1-to-
</Tab>
<Tab title="TypeScript">
<Update label="2026-04-20" description="v3.0.1">
**Bug Fixes:**
- **Telemetry:** SDK version is now injected into telemetry at build time via esbuild's `define`, replacing the two hardcoded version strings in `src/client/telemetry.ts` and `src/oss/src/utils/telemetry.ts`. Previously these were stuck at `2.1.36` and `2.1.34` while the published package was on `3.x`, so every telemetry event was reporting the wrong `client_version`. The placeholder is substituted with a string literal at bundle time — no runtime `require("./package.json")` in the shipped bundle ([#4897](https://github.com/mem0ai/mem0/pull/4897)).
</Update>
<Update label="2026-04-14" description="v3.0.0">
**Major Release** — TypeScript SDK with V3 memory pipeline, camelCase parameters, and cleaned-up API surface.
@@ -54,7 +54,6 @@ config = {
"embedding_model_dims": 3072,
}
},
"version": "v1.1",
}
class PersonalTravelAssistant:
@@ -154,7 +153,7 @@ 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.get('results', [])]
def search_memories(self, query, user_id):
+1 -1
View File
@@ -42,7 +42,7 @@ This sets up Mem0 with:
```python
import boto3
from opensearchpy import RequestsHttpConnection, AWSV4SignerAuth
from mem0.memory.main import Memory
from mem0 import Memory
region = 'us-west-2'
service = 'aoss'
+29 -8
View File
@@ -399,8 +399,6 @@
"integrations/langgraph",
"integrations/llama-index",
"integrations/crewai",
"integrations/autogen",
"integrations/agno",
"integrations/camel-ai",
"integrations/openai-agents-sdk",
"integrations/google-ai-adk",
@@ -429,12 +427,7 @@
"group": "Developer Tools",
"icon": "wrench",
"pages": [
"integrations/dify",
"integrations/flowise",
"integrations/langchain-tools",
"integrations/agentops",
"integrations/keywords",
"integrations/raycast"
"integrations/langchain-tools"
]
}
]
@@ -1101,6 +1094,34 @@
"source": "/v0x/faqs",
"destination": "/platform/faqs"
},
{
"source": "/integrations/raycast",
"destination": "/integrations"
},
{
"source": "/integrations/autogen",
"destination": "/integrations"
},
{
"source": "/integrations/keywords",
"destination": "/integrations"
},
{
"source": "/integrations/agentops",
"destination": "/integrations"
},
{
"source": "/integrations/flowise",
"destination": "/integrations"
},
{
"source": "/integrations/agno",
"destination": "/integrations"
},
{
"source": "/integrations/dify",
"destination": "/integrations"
},
{
"source": "/integrations/multion",
"destination": "/integrations"
-122
View File
@@ -20,23 +20,6 @@ Here are the available integrations for Mem0:
## Integrations
<CardGroup cols={2}>
<Card
title="AgentOps"
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
width="25"
height="26"
viewBox="0 0 30 36"
fill="none"
>
<path d="M10.4659 6.47277C10.45 6.37428 10.4381 6.27986 10.4303 6.18101L10.4285 6.16388C10.4212 6.09482 10.414 6.02566 10.4106 5.95626L1.18538 21.8752C0.505422 23.0493 0.323356 24.4208 0.675227 25.7289C0.849119 26.3869 1.14971 26.9859 1.55323 27.5098C1.95675 28.0338 2.46282 28.4751 3.05175 28.8143C3.83464 29.2675 4.70856 29.5 5.59028 29.5C6.03318 29.5 6.4798 29.4408 6.91899 29.3226C8.23581 28.972 9.3349 28.1326 10.0152 26.9545L15.9268 16.749V16.7449L16.5001 15.7637L17.6431 13.7936L16.5001 11.8234L15.9309 10.8381L15.9268 10.8341L13.7836 7.13406C13.6651 6.933 13.5741 6.72418 13.5109 6.51165C13.2817 5.80223 13.3292 5.04172 13.6097 4.37599L13.8115 4.02535C14.3532 3.09155 15.31 2.53987 16.3184 2.47692C16.3738 2.46915 16.4251 2.46915 16.4804 2.46915C16.5421 2.46915 16.6038 2.47257 16.6654 2.47599L16.6822 2.47692C17.6906 2.53987 18.6474 3.09155 19.1892 4.02535L21.2216 7.52838L21.8146 8.55289L21.8421 8.60399L30.1024 22.8601C30.5174 23.5814 30.6281 24.4167 30.4148 25.2205C30.1975 26.0244 29.6832 26.6942 28.9598 27.1081C28.2364 27.5258 27.3977 27.6361 26.5911 27.4195C25.7844 27.2066 25.1123 26.6905 24.6968 25.9696L18.2119 14.7788L17.069 16.7449L22.9847 26.9545C23.6646 28.1326 24.7641 28.972 26.0809 29.3226C26.5197 29.4408 26.9626 29.5 27.4096 29.5C28.2914 29.5 29.1612 29.2675 29.9482 28.8143C31.1264 28.1367 31.9728 27.0411 32.3247 25.7289C32.6766 24.4208 32.4949 23.0493 31.8145 21.8752L21.1261 3.43034C20.7029 2.51617 20.0033 1.72011 19.0621 1.18027C18.5281 0.877027 17.9708 0.675975 17.3975 0.581189C17.3027 0.565268 17.2076 0.549717 17.1129 0.537868C17.0099 0.52602 16.9074 0.518244 16.8045 0.510469C16.6027 0.498621 16.3972 0.494548 16.1914 0.510469C16.0885 0.518244 15.9859 0.52639 15.883 0.537868C15.795 0.54887 15.7067 0.563384 15.6187 0.577852L15.5984 0.581189C15.0291 0.675605 14.4673 0.876657 13.9375 1.18027C12.9885 1.72789 12.2766 2.53579 11.8537 3.46181C11.7742 3.63473 11.707 3.81282 11.6471 3.99314C11.6361 4.02668 11.6269 4.06051 11.6177 4.09435C11.612 4.11503 11.6064 4.13579 11.6003 4.15642C11.5624 4.28601 11.5275 4.41634 11.4996 4.54853C11.4885 4.60231 11.4794 4.65668 11.4703 4.71111L11.4666 4.73329C11.4443 4.86399 11.4264 4.99543 11.4145 5.12762C11.4093 5.18686 11.4045 5.24573 11.4012 5.30534C11.3934 5.44567 11.3923 5.58637 11.3963 5.72744C11.3969 5.74403 11.3962 5.76062 11.3956 5.7772C11.3949 5.79616 11.3942 5.81512 11.3952 5.83407C11.3952 5.86184 11.3952 5.88924 11.3993 5.92071C11.3998 5.9291 11.4006 5.93736 11.4014 5.94564C11.402 5.95125 11.4026 5.95687 11.403 5.96255C11.4045 5.98181 11.4064 6.00106 11.4082 6.02031C11.4097 6.03577 11.4109 6.05122 11.4122 6.06674C11.4142 6.09134 11.4163 6.11621 11.419 6.14139L11.4428 6.32282C11.4506 6.38983 11.4625 6.46092 11.4744 6.52757C11.5063 6.68863 11.5468 6.84896 11.5936 7.0078C11.5944 7.0102 11.5949 7.0127 11.5955 7.0152C11.5958 7.01662 11.5961 7.01804 11.5965 7.01944C11.5967 7.02051 11.597 7.02157 11.5974 7.02261C11.6483 7.19293 11.7081 7.36177 11.7787 7.52838C11.8619 7.72943 11.9607 7.92641 12.0715 8.11932L12.3245 8.5566V8.56067L12.4984 8.85614L12.7199 9.24232H12.7239L12.728 9.25417L14.7802 12.7927V12.7968L14.7883 12.805V12.809L15.3576 13.7943L14.7883 14.7796L8.30344 25.9703C7.88431 26.6912 7.21216 27.2077 6.40921 27.4202C6.14019 27.4913 5.86338 27.5306 5.59474 27.5306C5.053 27.5306 4.51906 27.3888 4.04085 27.1089C3.31705 26.6953 2.79909 26.0251 2.58581 25.2213C2.36845 24.4174 2.47917 23.5821 2.89829 22.8609L11.1585 8.60473L11.186 8.56141V8.55734C11.1266 8.45478 11.0753 8.35629 11.024 8.25409C11.0105 8.22496 10.9969 8.19611 10.9834 8.16739C10.9458 8.08735 10.9086 8.00836 10.8739 7.92715C10.8718 7.92504 10.8708 7.92194 10.8698 7.91887C10.8688 7.91602 10.8679 7.91319 10.8661 7.91123V7.90346C10.8423 7.8483 10.8186 7.79311 10.7989 7.73795C10.7476 7.60799 10.7041 7.47803 10.6644 7.3477C10.6012 7.15479 10.5536 6.96152 10.518 6.76861C10.4942 6.67012 10.4786 6.5757 10.4667 6.47684C10.4667 6.47684 10.47 6.47684 10.4659 6.47277Z" fill="currentColor"></path>
</svg>
}
href="/integrations/agentops"
>
Monitor and analyze Mem0 operations with comprehensive AI agent analytics and LLM observability.
</Card>
<Card
title="Camel AI"
href="/integrations/camel-ai"
@@ -103,27 +86,6 @@ Here are the available integrations for Mem0:
>
Build RAG applications with LlamaIndex and Mem0.
</Card>
<Card
title="AutoGen"
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 96 85"
fill="none"
>
<rect width="96" height="85" rx="6" fill="#2D2D2F" />
<path
d="M32.6484 28.7109L23.3672 57H15.8906L28.5703 22.875H33.3281L32.6484 28.7109ZM40.3594 57L31.0547 28.7109L30.3047 22.875H35.1094L47.8594 57H40.3594ZM39.9375 44.2969V49.8047H21.9141V44.2969H39.9375ZM77.6484 39.1641V52.6875C77.1172 53.3281 76.2969 54.0234 75.1875 54.7734C74.0781 55.5078 72.6484 56.1406 70.8984 56.6719C69.1484 57.2031 67.0312 57.4688 64.5469 57.4688C62.3438 57.4688 60.3359 57.1094 58.5234 56.3906C56.7109 55.6562 55.1484 54.5859 53.8359 53.1797C52.5391 51.7734 51.5391 50.0547 50.8359 48.0234C50.1328 45.9766 49.7812 43.6406 49.7812 41.0156V38.8828C49.7812 36.2578 50.1172 33.9219 50.7891 31.875C51.4766 29.8281 52.4531 28.1016 53.7188 26.6953C54.9844 25.2891 56.4922 24.2188 58.2422 23.4844C59.9922 22.75 61.9375 22.3828 64.0781 22.3828C67.0469 22.3828 69.4844 22.8672 71.3906 23.8359C73.2969 24.7891 74.75 26.1172 75.75 27.8203C76.7656 29.5078 77.3906 31.4453 77.625 33.6328H70.8047C70.6328 32.4766 70.3047 31.4688 69.8203 30.6094C69.3359 29.75 68.6406 29.0781 67.7344 28.5938C66.8438 28.1094 65.6875 27.8672 64.2656 27.8672C63.0938 27.8672 62.0469 28.1094 61.125 28.5938C60.2188 29.0625 59.4531 29.7578 58.8281 30.6797C58.2031 31.6016 57.7266 32.7422 57.3984 34.1016C57.0703 35.4609 56.9062 37.0391 56.9062 38.8359V41.0156C56.9062 42.7969 57.0781 44.375 57.4219 45.75C57.7656 47.1094 58.2734 48.2578 58.9453 49.1953C59.6328 50.1172 60.4766 50.8125 61.4766 51.2812C62.4766 51.75 63.6406 51.9844 64.9688 51.9844C66.0781 51.9844 67 51.8906 67.7344 51.7031C68.4844 51.5156 69.0859 51.2891 69.5391 51.0234C70.0078 50.7422 70.3672 50.4766 70.6172 50.2266V44.1797H64.1953V39.1641H77.6484Z"
fill="white"
/>
</svg>
}
href="/integrations/autogen"
>
Build multi-agent systems with persistent memory capabilities.
</Card>
<Card
title="CrewAI"
icon={
@@ -205,26 +167,6 @@ Here are the available integrations for Mem0:
>
Use Mem0 with LangChain Tools for enhanced agent capabilities.
</Card>
<Card
title="Dify"
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 200 200"
fill="none"
>
<path
d="M40 20 H120 C160 20, 160 180, 120 180 H40 V20"
fill="currentColor"
/>
</svg>
}
href="/integrations/dify"
>
Build AI applications with persistent memory using Dify and Mem0.
</Card>
<Card
title="Livekit"
icon={
@@ -290,63 +232,6 @@ Here are the available integrations for Mem0:
>
Build conversational AI agents with memory using Pipecat.
</Card>
<Card
title="Agno"
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
>
<path d="M8 4h8v12h8" stroke="currentColor" strokeWidth="2" fill="none" transform="rotate(15, 12, 12)"/>
</svg>
}
href="/integrations/agno"
>
Build autonomous agents with memory using Agno framework.
</Card>
<Card
title="Keywords AI"
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.07513 1.1863C9.21663 1.07722 9.39144 1.01009 9.56624 1.01009C9.83261 1.01009 10.0823 1.12756 10.2405 1.33734L15.0101 7.4964V12.4136L16.4335 13.8401C16.7582 14.1673 16.7582 14.7043 16.4335 15.0316C16.1089 15.3588 15.5762 15.3588 15.2515 15.0316L13.3453 13.1016V8.07538L8.92529 2.36944V2.36105C8.64228 2.00024 8.70887 1.4716 9.07513 1.1863ZM18.976 14.4133C18.8344 14.3778 18.7003 14.3042 18.5894 14.1925L16.9163 12.5059C16.7249 12.3129 16.6416 12.0528 16.6749 11.8094V6.88385H16.6499L11.8553 0.691225C11.7282 0.529117 11.6716 0.333133 11.6803 0.140562C11.134 0.0481292 10.5726 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20C13.9387 20 17.3456 17.7229 18.976 14.4133Z" fill="currentColor"></path>
</svg>
}
href="/integrations/keywords"
>
Build AI applications with persistent memory and comprehensive LLM observability.
</Card>
<Card
title="Raycast"
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M3 12L21 12M12 3L12 21M7.5 7.5L16.5 16.5M16.5 7.5L7.5 16.5"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
}
href="/integrations/raycast"
>
Mem0 Raycast extension for intelligent memory management and retrieval.
</Card>
<Card
title="Mastra"
icon={
@@ -395,13 +280,6 @@ Here are the available integrations for Mem0:
>
Integrate Mem0 with Google Agent Development Kit for persistent memory across multi-agent workflows.
</Card>
<Card
title="Flowise"
icon="diagram-project"
href="/integrations/flowise"
>
Add persistent Mem0 memory to Flowise chatflows for context-aware conversations in the low-code builder.
</Card>
<Card
title="AWS Bedrock"
icon="cloud"
-175
View File
@@ -1,175 +0,0 @@
---
title: AgentOps
description: "Integrate Mem0 with AgentOps for automatic monitoring, analytics, and real-time tracking of memory operations."
---
Integrate [**Mem0**](https://github.com/mem0ai/mem0) with [AgentOps](https://agentops.ai), a comprehensive monitoring and analytics platform for AI agents. This integration enables automatic tracking and analysis of memory operations, providing insights into agent performance and memory usage patterns.
## Overview
1. Automatic monitoring of Mem0 operations and performance metrics
2. Real-time tracking of memory add, search, and retrieval operations
3. Analytics dashboard with memory usage patterns and insights
4. Error tracking and debugging capabilities for memory operations
## Prerequisites
Before setting up Mem0 with AgentOps, ensure you have:
1. Installed the required packages:
```bash
pip install mem0ai agentops python-dotenv
```
2. Valid API keys:
- [AgentOps API Key](https://app.agentops.ai/dashboard/api-keys)
- OpenAI API Key (for LLM operations)
- <a href="https://app.mem0.ai/dashboard/api-keys" rel="nofollow">Mem0 API Key</a> (optional, for cloud operations)
## Basic Integration Example
The following example demonstrates how to integrate Mem0 with AgentOps monitoring for comprehensive memory operation tracking:
```python
#Import the required libraries for local memory management with Mem0
from mem0 import Memory, AsyncMemory
import os
import asyncio
import logging
from dotenv import load_dotenv
import agentops
import openai
load_dotenv()
#Set up environment variables for API keys
os.environ["AGENTOPS_API_KEY"] = os.getenv("AGENTOPS_API_KEY")
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
#Set up the configuration for local memory storage and define sample user data.
local_config = {
"llm": {
"provider": "openai",
"config": {
"model": "gpt-5-mini",
"temperature": 0.1,
"max_tokens": 2000,
},
}
}
user_id = "alice_demo"
agent_id = "assistant_demo"
run_id = "session_001"
sample_messages = [
{"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
{"role": "assistant", "content": "How about a thriller? 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.",
},
]
sample_preferences = [
"I prefer dark roast coffee over light roast",
"I exercise every morning at 6 AM",
"I'm vegetarian and avoid all meat products",
"I love reading science fiction novels",
"I work in software engineering",
]
#This function demonstrates sequential memory operations using the synchronous Memory class
def demonstrate_sync_memory(local_config, sample_messages, sample_preferences, user_id):
"""
Demonstrate synchronous Memory class operations.
"""
agentops.start_trace("mem0_memory_example", tags=["mem0_memory_example"])
try:
memory = Memory.from_config(local_config)
result = memory.add(
sample_messages, user_id=user_id, metadata={"category": "movie_preferences", "session": "demo"}
)
for i, preference in enumerate(sample_preferences):
result = memory.add(preference, user_id=user_id, metadata={"type": "preference", "index": i})
search_queries = [
"What movies does the user like?",
"What are the user's food preferences?",
"When does the user exercise?",
]
for query in search_queries:
results = memory.search(query, filters={"user_id": user_id})
if results and "results" in results:
for j, result in enumerate(results['results']):
print(f"Result {j+1}: {result.get('memory', 'N/A')}")
else:
print("No results found")
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'])}")
delete_all_result = memory.delete_all(user_id=user_id)
print(f"Delete all result: {delete_all_result}")
agentops.end_trace(end_state="success")
except Exception as e:
agentops.end_trace(end_state="error")
# Execute sync demonstrations
demonstrate_sync_memory(local_config, sample_messages, sample_preferences, user_id)
```
For detailed information on this integration, refer to the official [Agentops Mem0 integration documentation](https://docs.agentops.ai/v2/integrations/mem0).
## Key Features
### 1. Automatic Operation Tracking
AgentOps automatically monitors all Mem0 operations:
- **Memory Operations**: Track add, search, get_all, delete operations and much more
- **Performance Metrics**: Monitor response times and success rates
- **Error Tracking**: Capture and analyze operation failures
### 2. Real-time Analytics Dashboard
Access comprehensive analytics through the AgentOps dashboard:
- **Usage Patterns**: Visualize memory usage trends over time
- **User Behavior**: Analyze how different users interact with memory
- **Performance Insights**: Identify bottlenecks and optimization opportunities
### 3. Session Management
Organize your monitoring with structured sessions:
- **Session Tracking**: Group related operations into logical sessions
- **Success/Failure Rates**: Track session outcomes for reliability monitoring
- **Custom Metadata**: Add context to sessions for better analysis
## Best Practices
1. **Initialize Early**: Always initialize AgentOps before importing Mem0 classes
2. **Session Management**: Use meaningful session names and end sessions appropriately
3. **Error Handling**: Wrap operations in try-catch blocks and report failures
4. **Tagging**: Use tags to organize different types of memory operations
5. **Environment Separation**: Use different projects or tags for dev/staging/prod
<CardGroup cols={2}>
<Card title="CrewAI Integration" icon="users" href="/integrations/crewai">
Monitor multi-agent CrewAI systems
</Card>
<Card title="LangChain Integration" icon="link" href="/integrations/langchain">
Track LangChain agent performance
</Card>
</CardGroup>
-207
View File
@@ -1,207 +0,0 @@
---
title: Agno
description: "Add persistent multimodal memory to Agno-based agents using Mem0 for text and image interactions."
---
This integration of [**Mem0**](https://github.com/mem0ai/mem0) with [Agno](https://github.com/agno-agi/agno) enables persistent, multimodal memory for Agno-based agents - improving personalization, context awareness, and continuity across conversations.
## Overview
1. Store and retrieve memories from Mem0 within Agno agents
2. Support for multimodal interactions (text and images)
3. Semantic search for relevant past conversations
4. Personalized responses based on user history
5. One-line memory integration via `Mem0Tools`
## Prerequisites
Before setting up Mem0 with Agno, ensure you have:
1. Installed the required packages:
```bash
pip install agno mem0ai python-dotenv
```
2. Valid API keys:
- <a href="https://app.mem0.ai/dashboard/api-keys" rel="nofollow">Mem0 API Key</a>
- OpenAI API Key (for the agent model)
## Quick Integration (Using `Mem0Tools`)
The simplest way to integrate Mem0 with Agno Agents is to use Mem0 as a tool using built-in `Mem0Tools`:
```python
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools.mem0 import Mem0Tools
agent = Agent(
name="Memory Agent",
model=OpenAIChat(id="gpt-5-mini"),
tools=[Mem0Tools()],
description="An assistant that remembers and personalizes using Mem0 memory."
)
```
This enables memory functionality out of the box:
- **Persistent memory writing**: `Mem0Tools` uses `MemoryClient.add(...)` to store messages from user-agent interactions, including optional metadata such as user ID or session.
- **Contextual memory search**: Compatible queries use `MemoryClient.search(...)` to retrieve relevant past messages, improving contextual understanding.
- **Multimodal support**: Both text and image inputs are supported, allowing richer memory records.
> `Mem0Tools` uses the `MemoryClient` under the hood and requires no additional setup. You can customize its behavior by modifying your tools list or extending it in code.
## Full Manual Example
> Note: Mem0 can also be used with Agno Agents as a separate memory layer.
The following example demonstrates how to create an Agno agent with Mem0 memory integration, including support for image processing:
```python
import base64
from pathlib import Path
from typing import Optional
from agno.agent import Agent
from agno.media import Image
from agno.models.openai import OpenAIChat
from mem0 import MemoryClient
# Initialize the Mem0 client
client = MemoryClient()
# Define the agent
agent = Agent(
name="Personal Agent",
model=OpenAIChat(id="gpt-4"),
description="You are a helpful personal agent that helps me with day to day activities."
"You can process both text and images.",
markdown=True
)
def chat_user(
user_input: Optional[str] = None,
user_id: str = "alex",
image_path: Optional[str] = None
) -> str:
"""
Handle user input with memory integration, supporting both text and images.
Args:
user_input: The user's text input
user_id: Unique identifier for the user
image_path: Path to an image file if provided
Returns:
The agent's response as a string
"""
if image_path:
# Convert image to base64
with open(image_path, "rb") as image_file:
base64_image = base64.b64encode(image_file.read()).decode("utf-8")
# Create message objects for text and image
messages = []
if user_input:
messages.append({
"role": "user",
"content": user_input
})
messages.append({
"role": "user",
"content": {
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{base64_image}"
}
}
})
# Store messages in memory
client.add(messages, user_id=user_id)
print("✅ Image and text stored in memory.")
if user_input:
# Search for relevant memories
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
prompt = f"""
You are a helpful personal assistant who helps users with their day-to-day activities and keeps track of everything.
Your task is to:
1. Analyze the given image (if present) and extract meaningful details to answer the user's question.
2. Use your past memory of the user to personalize your answer.
3. Combine the image content and memory to generate a helpful, context-aware response.
Here is what I remember about the user:
{memory_context}
User question:
{user_input}
"""
# Get response from agent
if image_path:
response = agent.run(prompt, images=[Image(filepath=Path(image_path))])
else:
response = agent.run(prompt)
# Store the interaction in memory
interaction_message = [{"role": "user", "content": f"User: {user_input}\nAssistant: {response.content}"}]
client.add(interaction_message, user_id=user_id)
return response.content
return "No user input or image provided."
# Example Usage
if __name__ == "__main__":
response = chat_user(
"I like to travel and my favorite destination is London",
image_path="travel_items.jpeg",
user_id="alex"
)
print(response)
```
## Key Features
### 1. Multimodal Memory Storage
The integration supports storing both text and image data:
- **Text Storage**: Conversation history is saved in a structured format
- **Image Analysis**: Agents can analyze images and store visual information
- **Combined Context**: Memory retrieval combines both text and visual data
### 2. Personalized Agent Responses
Improve your agent's context awareness:
- **Memory Retrieval**: Semantic search finds relevant past interactions
- **User Preferences**: Personalize responses based on stored user information
- **Continuity**: Maintain conversation threads across multiple sessions
### 3. Flexible Configuration
Customize the integration to your needs:
- **Use `Mem0Tools()`** for drop-in memory support
- **Use `MemoryClient` directly** for advanced control
- **User Identification**: Organize memories by user ID
- **Memory Search**: Configure search relevance and result count
- **Memory Formatting**: Support for various OpenAI message formats
<CardGroup cols={2}>
<Card title="OpenAI Agents SDK" icon="cube" href="/integrations/openai-agents-sdk">
Build agents with OpenAI SDK and Mem0
</Card>
<Card title="Mastra Integration" icon="star" href="/integrations/mastra">
Create intelligent agents with Mastra framework
</Card>
</CardGroup>
-142
View File
@@ -1,142 +0,0 @@
---
title: AutoGen
description: "Build conversational AI agents with AutoGen and Mem0 for context-aware, personalized interactions."
---
Build conversational AI agents with memory capabilities. This integration combines AutoGen for creating AI agents with Mem0 for memory management, enabling context-aware and personalized interactions.
## Overview
This guide demonstrates creating a conversational AI system with memory. We'll build a customer service bot that can recall previous interactions and provide personalized responses.
## Setup and Configuration
Install necessary libraries:
```bash
pip install autogen mem0ai openai python-dotenv
```
First, we'll import the necessary libraries and set up our configurations.
<Note>Remember to get the Mem0 API key from <a href="https://app.mem0.ai" rel="nofollow">Mem0 Platform</a>.</Note>
```python
import os
from autogen import ConversableAgent
from mem0 import MemoryClient
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
# Configuration
# OPENAI_API_KEY = 'sk-xxx' # Replace with your actual OpenAI API key
# MEM0_API_KEY = 'your-mem0-key' # Replace with your actual Mem0 API key from https://app.mem0.ai
USER_ID = "alice"
# Set up OpenAI API key
OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY')
# os.environ['MEM0_API_KEY'] = MEM0_API_KEY
# Initialize Mem0 and AutoGen agents
memory_client = MemoryClient()
agent = ConversableAgent(
"chatbot",
llm_config={"config_list": [{"model": "gpt-4", "api_key": OPENAI_API_KEY}]},
code_execution_config=False,
human_input_mode="NEVER",
)
```
## Storing Conversations in Memory
Add conversation history to Mem0 for future reference:
```python
conversation = [
{"role": "assistant", "content": "Hi, I'm Best Buy's chatbot! How can I help you?"},
{"role": "user", "content": "I'm seeing horizontal lines on my TV."},
{"role": "assistant", "content": "I'm sorry to hear that. Can you provide your TV model?"},
{"role": "user", "content": "It's a Sony - 77\" Class BRAVIA XR A80K OLED 4K UHD Smart Google TV"},
{"role": "assistant", "content": "Thank you for the information. Let's troubleshoot this issue..."}
]
memory_client.add(messages=conversation, user_id=USER_ID)
print("Conversation added to memory.")
```
## Retrieving and Using Memory
Create a function to get context-aware responses based on user's question and previous interactions:
```python
def get_context_aware_response(question):
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:
Previous interactions:
{context}
Question: {question}
"""
reply = agent.generate_reply(messages=[{"content": prompt, "role": "user"}])
return reply
# Example usage
question = "What was the issue with my TV?"
answer = get_context_aware_response(question)
print("Context-aware answer:", answer)
```
## Multi-Agent Conversation
For more complex scenarios, you can create multiple agents:
```python
manager = ConversableAgent(
"manager",
system_message="You are a manager who helps in resolving complex customer issues.",
llm_config={"config_list": [{"model": "gpt-4", "api_key": OPENAI_API_KEY}]},
human_input_mode="NEVER"
)
def escalate_to_manager(question):
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"""
Context from previous interactions:
{context}
Customer question: {question}
As a manager, how would you address this issue?
"""
manager_response = manager.generate_reply(messages=[{"content": prompt, "role": "user"}])
return manager_response
# Example usage
complex_question = "I'm not satisfied with the troubleshooting steps. What else can be done?"
manager_answer = escalate_to_manager(complex_question)
print("Manager's response:", manager_answer)
```
## Conclusion
By integrating AutoGen with Mem0, you've created a conversational AI system with memory capabilities. This example demonstrates a customer service bot that can recall previous interactions and provide context-aware responses, with the ability to escalate complex issues to a manager agent.
This integration enables the creation of more intelligent and personalized AI agents for various applications, such as customer support, virtual assistants, and interactive chatbots.
<CardGroup cols={2}>
<Card title="CrewAI Integration" icon="users" href="/integrations/crewai">
Build multi-agent systems with CrewAI and Mem0
</Card>
<Card title="LangGraph Integration" icon="diagram-project" href="/integrations/langgraph">
Create stateful workflows with LangGraph
</Card>
</CardGroup>
+1 -1
View File
@@ -49,7 +49,7 @@ Import necessary modules and configure Mem0:
```python
import boto3
from opensearchpy import OpenSearch, RequestsHttpConnection, AWSV4SignerAuth
from mem0.memory.main import Memory
from mem0 import Memory
region = 'us-west-2'
service = 'aoss'
+2 -2
View File
@@ -237,7 +237,7 @@ By adding Mem0 as a memory store in ChatDev, your multi-agent workflows gain per
<Card title="CrewAI Integration" icon="users" href="/integrations/crewai">
Build multi-agent systems with CrewAI and Mem0
</Card>
<Card title="AutoGen Integration" icon="robot" href="/integrations/autogen">
Build conversational agents with AutoGen and Mem0
<Card title="OpenAI Agents SDK" icon="robot" href="/integrations/openai-agents-sdk">
Build conversational agents with OpenAI Agents SDK and Mem0
</Card>
</CardGroup>
+2 -2
View File
@@ -162,8 +162,8 @@ if __name__ == "__main__":
By combining CrewAI with Mem0, you can create sophisticated AI systems that maintain context and provide personalized experiences while leveraging the power of autonomous agents.
<CardGroup cols={2}>
<Card title="AutoGen Integration" icon="users" href="/integrations/autogen">
Build multi-agent systems with AutoGen and Mem0
<Card title="OpenAI Agents SDK" icon="users" href="/integrations/openai-agents-sdk">
Build multi-agent systems with OpenAI Agents SDK and Mem0
</Card>
<Card title="LangGraph Integration" icon="diagram-project" href="/integrations/langgraph">
Create stateful agent workflows with memory
-42
View File
@@ -1,42 +0,0 @@
---
title: Dify
description: "Integrate Mem0 as a plugin in Dify AI workflows for persistent conversation storage and retrieval."
---
# Integrating Mem0 with Dify AI
Mem0 brings a robust memory layer to Dify AI, empowering your AI agents with persistent conversation storage and retrieval capabilities. With Mem0, your Dify applications gain the ability to recall past interactions and maintain context, ensuring more natural and insightful conversations.
---
## How to Integrate Mem0 in Your Dify Workflow
1. **Install the Mem0 Plugin:**
Head to the [Dify Marketplace](https://marketplace.dify.ai/plugins/yevanchen/mem0) and install the Mem0 plugin. This is your first step toward adding intelligent memory to your AI applications.
2. **Create or Open Your Dify Project:**
Whether you're starting fresh or updating an existing project, simply create or open your Dify workspace.
3. **Add the Mem0 Plugin to Your Project:**
Within your project, add the Mem0 plugin. This integration connects Mem0’s memory management capabilities directly to your Dify application.
4. **Configure Your Mem0 Settings:**
Customize Mem0 to suit your needs—set preferences for how conversation history is stored, the search parameters, and any other context-aware features.
5. **Leverage Mem0 in Your Workflow:**
Use Mem0 to store every conversation turn and retrieve past interactions seamlessly. This integration ensures that your AI agents can refer back to important context, making multi-turn dialogues more effective and user-centric.
---
![Mem0 Dify Integration](/images/dify-mem0-integration.png)
Enhance your Dify-powered AI with Mem0 and transform your conversational experiences. Start integrating intelligent memory management today and give your agents the context they need to excel!
<CardGroup cols={2}>
<Card title="Flowise Integration" icon="share-nodes" href="/integrations/flowise">
Build visual AI workflows with Flowise
</Card>
<Card title="LangChain Integration" icon="link" href="/integrations/langchain">
Create LangChain-powered applications
</Card>
</CardGroup>
-127
View File
@@ -1,127 +0,0 @@
---
title: Flowise
description: "Add persistent Mem0 memory to Flowise chatflows for context-aware conversations in the low-code builder."
---
The [**Mem0 Memory**](https://github.com/mem0ai/mem0) integration with [Flowise](https://github.com/FlowiseAI/Flowise) enables persistent memory capabilities for your AI chatflows. [Flowise](https://flowiseai.com/) is an open-source low-code tool for developers to build customized LLM orchestration flows & AI agents using a drag & drop interface.
## Overview
1. Provides persistent memory storage for Flowise chatflows
2. Seamless integration with existing Flowise templates
3. Compatible with various LLM nodes in Flowise
4. Supports custom memory configurations
5. Easy to set up and manage
## Prerequisites
Before setting up Mem0 with Flowise, ensure you have:
1. [Flowise installed](https://github.com/FlowiseAI/Flowise#⚡quick-start) (NodeJS >= 18.15.0 required):
```bash
npm install -g flowise
npx flowise start
```
2. Access to the Flowise UI at http://localhost:3000
3. Basic familiarity with [Flowise's LLM orchestration](https://flowiseai.com/#features) concepts
## Setup and Configuration
### 1. Set Up Flowise
1. Open the Flowise application and create a new canvas, or select a template from the Flowise marketplace.
2. In this example, we use the **Conversation Chain** template.
3. Replace the default **Buffer Memory** with **Mem0 Memory**.
![Flowise Memory Integration](https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/flowise-flow.png)
### 2. Obtain Your Mem0 API Key
1. Navigate to the <a href="https://app.mem0.ai/dashboard/api-keys" rel="nofollow">Mem0 API Key dashboard</a>.
2. Generate or copy your existing Mem0 API Key.
![Mem0 API Key](https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/api-key.png)
### 3. Configure Mem0 Credentials
1. Enter the **Mem0 API Key** in the Mem0 Credentials section.
2. Configure additional settings as needed:
```typescript
{
"apiKey": "m0-xxx",
"userId": "user-123", // Optional: Specify user ID
"projectId": "proj-xxx", // Optional: Specify project ID
"orgId": "org-xxx" // Optional: Specify organization ID
}
```
<figure>
<img src="https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/creds.png" alt="Mem0 Credentials" />
<figcaption>Configure API Credentials</figcaption>
</figure>
## Memory Features
### 1. Basic Memory Storage
Test your memory configuration:
1. Save your Flowise configuration
2. Run a test chat and store some information
3. Verify the stored memories in the <a href="https://app.mem0.ai/dashboard/requests" rel="nofollow">Mem0 Dashboard</a>
![Flowise Test Chat](https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/flowise-chat-1.png)
### 2. Memory Retention
Validate memory persistence:
1. Clear the chat history in Flowise
2. Ask a question about previously stored information
3. Confirm that the AI remembers the context
![Testing Memory Retention](https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/flowise-chat-2.png)
## Advanced Configuration
### Memory Settings
![Mem0 Settings](https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/settings.png)
Available settings include:
1. **Search Only Mode**: Enable memory retrieval without creating new memories
2. **Mem0 Entities**: Configure identifiers:
- `user_id`: Unique identifier for each user
- `run_id`: Specific conversation session ID
- `app_id`: Application identifier
- `agent_id`: AI agent identifier
3. **Project ID**: Assign memories to specific projects
4. **Organization ID**: Organize memories by organization
### Platform Configuration
Additional settings available in <a href="https://app.mem0.ai/dashboard/project-settings" rel="nofollow">Mem0 Project Settings</a>:
1. **Custom Instructions**: Define memory extraction rules
2. **Expiration Date**: Set automatic memory cleanup periods
![Mem0 Project Settings](https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/mem0-settings.png)
## Best Practices
1. **User Identification**: Use consistent `user_id` values for reliable memory retrieval
2. **Memory Organization**: Utilize projects and organizations for better memory management
3. **Regular Maintenance**: Monitor and clean up unused memories periodically
<CardGroup cols={2}>
<Card title="LangChain Integration" icon="link" href="/integrations/langchain">
Build LangChain-powered flows with memory
</Card>
<Card title="Dify Integration" icon="blocks" href="/integrations/dify">
Create AI workflows with Dify platform
</Card>
</CardGroup>
-142
View File
@@ -1,142 +0,0 @@
---
title: Keywords AI
description: "Combine Mem0 persistent memory with Keywords AI observability for tracked, cost-optimized AI applications."
---
Build AI applications with persistent memory and comprehensive LLM observability by integrating Mem0 with Keywords AI.
## Overview
Mem0 is a self-improving memory layer for LLM applications, enabling personalized AI experiences that save costs and delight users. Keywords AI provides complete LLM observability.
Combining Mem0 with Keywords AI allows you to:
1. Add persistent memory to your AI applications
2. Track interactions across sessions
3. Monitor memory usage and retrieval with Keywords AI observability
4. Optimize token usage and reduce costs
<Note>
You can get your Mem0 API key from the <a href="https://app.mem0.ai/" rel="nofollow">Mem0 dashboard</a>.
</Note>
## Setup and Configuration
Install the necessary libraries:
```bash
pip install mem0 keywordsai-sdk
```
Set up your environment variables:
```python
import os
# Set your API keys
os.environ["MEM0_API_KEY"] = "your-mem0-api-key"
os.environ["KEYWORDSAI_API_KEY"] = "your-keywords-api-key"
os.environ["KEYWORDSAI_BASE_URL"] = "https://api.keywordsai.co/api/"
```
## Basic Integration Example
Here's a simple example of using Mem0 with Keywords AI:
```python
from mem0 import Memory
import os
# Configuration
api_key = os.getenv("MEM0_API_KEY")
keywordsai_api_key = os.getenv("KEYWORDSAI_API_KEY")
base_url = os.getenv("KEYWORDSAI_BASE_URL") # "https://api.keywordsai.co/api/"
# Set up Mem0 with Keywords AI as the LLM provider
config = {
"llm": {
"provider": "openai",
"config": {
"model": "gpt-5-mini",
"temperature": 0.0,
"api_key": keywordsai_api_key,
"openai_base_url": base_url,
},
}
}
# Initialize Memory
memory = Memory.from_config(config_dict=config)
# Add a memory
result = memory.add(
"I like to take long walks on weekends.",
user_id="alice",
metadata={"category": "hobbies"},
)
print(result)
```
## Advanced Integration with OpenAI SDK
For more advanced use cases, you can integrate Keywords AI with Mem0 through the OpenAI SDK:
```python
from openai import OpenAI
import os
import json
# Initialize client
client = OpenAI(
api_key=os.environ.get("KEYWORDSAI_API_KEY"),
base_url=os.environ.get("KEYWORDSAI_BASE_URL"),
)
# Sample conversation messages
messages = [
{"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
{"role": "assistant", "content": "How about 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."}
]
# Add memory and generate a response
response = client.chat.completions.create(
model="openai/gpt-4.1-nano",
messages=messages,
extra_body={
"mem0_params": {
"user_id": "test_user",
"api_key": os.environ.get("MEM0_API_KEY"),
"add_memories": {
"messages": messages,
},
}
},
)
print(json.dumps(response.model_dump(), indent=4))
```
For detailed information on this integration, refer to the official [Keywords AI Mem0 integration documentation](https://docs.keywordsai.co/integration/development-frameworks/mem0).
## Key Features
1. **Memory Integration**: Store and retrieve relevant information from past interactions
2. **LLM Observability**: Track memory usage and retrieval patterns with Keywords AI
3. **Session Persistence**: Maintain context across multiple user sessions
4. **Cost Optimization**: Reduce token usage through efficient memory retrieval
## Conclusion
Integrating Mem0 with Keywords AI provides a powerful combination for building AI applications with persistent memory and comprehensive observability. This integration enables more personalized user experiences while providing insights into your application's memory usage.
<CardGroup cols={2}>
<Card title="OpenAI Agents SDK" icon="cube" href="/integrations/openai-agents-sdk">
Build monitored agents with OpenAI SDK
</Card>
<Card title="AgentOps Integration" icon="chart-line" href="/integrations/agentops">
Monitor agent performance with AgentOps
</Card>
</CardGroup>
-1
View File
@@ -92,7 +92,6 @@ config = {
"provider": "openai",
"config": {"model": "text-embedding-3-small"},
},
"version": "v1.1",
}
```
+1 -1
View File
@@ -214,7 +214,7 @@ Customize memory behavior:
# Configure memory search
memories = mem0.search(
query="travel preferences",
user_id="alex",
filters={"user_id": "alex"},
top_k=5 # Number of memories to retrieve
)
+215 -42
View File
@@ -14,16 +14,37 @@ Add long-term memory to [OpenClaw](https://github.com/openclaw/openclaw) agents
The plugin provides:
1. **Auto-Recall** — Before the agent responds, memories matching the current message are injected into context
2. **Auto-Capture** — After the agent responds, the exchange is sent to Mem0 which decides what's worth keeping
3. **Agent Tools** — Five tools for explicit memory operations during conversations
3. **Agent Tools** — Eight tools for explicit memory operations during conversations
Both auto-recall and auto-capture run silently with no manual configuration required.
## Installation
## Requirements
Check your OpenClaw version:
```bash
openclaw plugins install @mem0/openclaw-mem0
openclaw --version
# OpenClaw 2026.4.15 (041266a)
```
| OpenClaw Version | Plugin Support |
|------------------|----------------|
| `>= 2026.4.15` | Fully supported |
## Installation
The fastest way is to install directly from your OpenClaw chat, no CLI or config editing needed.
**Copy and paste this into your OpenClaw chat**; Telegram, WhatsApp, default chat, or any channel where your agent lives:
```
Setup Mem0 from mem0.ai/claw-setup
```
OpenClaw installs the plugin, prompts you for your email, and connects your Mem0 account with OTP verification. See [Chat Setup](#option-1-chat-setup-recommended) below for the full walkthrough.
If you prefer the OpenClaw CLI, or are setting up self-hosted / open-source mode, see [Manual Config](#option-2-manual-config) and [Open-Source Mode](#open-source-mode-self-hosted) below.
## Setup and Configuration
### Understanding `userId`
@@ -36,37 +57,113 @@ Pick any stable, unique identifier for the user. Common choices:
- A UUID (e.g. `"550e8400-e29b-41d4-a716-446655440000"`)
- A simple username (e.g. `"alice"`)
All memories are scoped to this `userId` — different values create separate memory namespaces. If you don't set it, it defaults to `"default"`, which means all users share the same memory space.
All memories are scoped to this `userId` — different values create separate memory namespaces. If you don't set it, it defaults to your OS username.
<Tip>In a multi-user application, set `userId` dynamically per user (e.g. from your auth system) rather than hardcoding a single value.</Tip>
### Platform Mode (Mem0 Cloud)
<Note>Get your API key from <a href="https://app.mem0.ai" rel="nofollow">app.mem0.ai</a>.</Note>
There are two ways to set up `@mem0/openclaw-mem0` on the Mem0 platform:
Add to your `openclaw.json`:
- **Chat setup (recommended)** — run the setup inside any OpenClaw chat. No config editing, no API key handling.
- **Manual config** — edit `openclaw.json` directly.
```json5
// plugins.entries
"openclaw-mem0": {
"enabled": true,
"config": {
"apiKey": "${MEM0_API_KEY}",
"userId": "alice" // any unique identifier you choose for this user
}
}
```
#### Option 1: Chat Setup (Recommended)
You no longer need manual config editing to get started. Everything happens inside the OpenClaw chat itself.
<Steps>
<Step title="Send the setup command to your OpenClaw agent">
Open any OpenClaw channel — Telegram, WhatsApp, your default chat, wherever your agent lives. Paste and send this command:
```
Setup Mem0 from mem0.ai/claw-setup
```
OpenClaw responds with a Mem0 setup card and immediately asks:
> "What's your email address? I'll send you a verification code to connect your Mem0 account."
</Step>
<Step title="Enter your email">
Type your email address and send it. Mem0 sends back:
> "Check your email for a 6-digit code and paste it here."
</Step>
<Step title="Paste the OTP">
Copy the 6-digit code from your email inbox and paste it into the chat.
You'll see the confirmation:
> "Connected to Mem0."
</Step>
</Steps>
That's it. No API key, no config file editing, no environment variables. The plugin is now active and auto-capture and auto-recall are running on every turn.
<Note>The chat flow uses the same underlying config as manual setup — it writes `apiKey` and `userId` into `openclaw.json` for you. You can still open the file to inspect or override values afterward.</Note>
#### Option 2: Manual Config
<Steps>
<Step title="Install the plugin via the OpenClaw CLI">
```bash
openclaw plugins install @mem0/openclaw-mem0
```
</Step>
<Step title="Get your API key">
Get your API key from <a href="https://app.mem0.ai?utm_source=mem0-docs" rel="nofollow">app.mem0.ai</a>.
</Step>
<Step title="Select the plugin as your memory backend in `openclaw.json`">
Add the full config to your `openclaw.json`:
```json5
{
"plugins": {
"slots": {
"memory": "openclaw-mem0"
},
"entries": {
"openclaw-mem0": {
"enabled": true,
"config": {
"apiKey": "${MEM0_API_KEY}",
"userId": "alice" // any unique identifier you choose for this user
}
}
}
}
}
```
</Step>
</Steps>
<Warning>
OpenClaw treats memory plugins as an exclusive slot. Installing the plugin alone does **not** activate it — you must also set `plugins.slots.memory` as shown above.
</Warning>
### Open-Source Mode (Self-hosted)
No Mem0 key needed. Requires `OPENAI_API_KEY` for default embeddings/LLM.
```json5
"openclaw-mem0": {
"enabled": true,
"config": {
"mode": "open-source",
"userId": "alice" // any unique identifier you choose for this user
{
"plugins": {
"slots": {
"memory": "openclaw-mem0"
},
"entries": {
"openclaw-mem0": {
"enabled": true,
"config": {
"mode": "open-source",
"userId": "alice" // any unique identifier you choose for this user
}
}
}
}
}
```
@@ -74,13 +171,25 @@ No Mem0 key needed. Requires `OPENAI_API_KEY` for default embeddings/LLM.
Sensible defaults work out of the box. To customize the embedder, vector store, or LLM:
```json5
"config": {
"mode": "open-source",
"userId": "your-user-id",
"oss": {
"embedder": { "provider": "openai", "config": { "model": "text-embedding-3-small" } },
"vectorStore": { "provider": "qdrant", "config": { "host": "localhost", "port": 6333 } },
"llm": { "provider": "openai", "config": { "model": "gpt-4o" } }
{
"plugins": {
"slots": {
"memory": "openclaw-mem0"
},
"entries": {
"openclaw-mem0": {
"enabled": true,
"config": {
"mode": "open-source",
"userId": "your-user-id",
"oss": {
"embedder": { "provider": "openai", "config": { "model": "text-embedding-3-small" } },
"vectorStore": { "provider": "qdrant", "config": { "host": "localhost", "port": 6333 } },
"llm": { "provider": "openai", "config": { "model": "gpt-4o" } }
}
}
}
}
}
}
```
@@ -93,23 +202,26 @@ Memories are organized into two scopes:
- **Session (short-term)** — Auto-capture stores memories scoped to the current session via Mem0's `run_id` / `runId` parameter. These are contextual to the ongoing conversation.
- **User (long-term)** — The agent can explicitly store long-term memories using the `memory_store` tool (with `longTerm: true`, the default). These persist across all sessions for the user.
- **User (long-term)** — The agent can explicitly store long-term memories using the `memory_add` tool (with `longTerm: true`, the default). These persist across all sessions for the user.
During **auto-recall**, the plugin searches both scopes and presents them separately — long-term memories first, then session memories — so the agent has full context.
## Agent Tools
The agent gets five tools it can call during conversations:
The agent gets eight tools it can call during conversations:
| Tool | Description |
|------|-------------|
| `memory_search` | Search memories by natural language |
| `memory_list` | List all stored memories for a user |
| `memory_store` | Explicitly save a fact |
| `memory_get` | Retrieve a memory by ID |
| `memory_forget` | Delete by ID or by query |
| `memory_search` | Search memories by natural language query. Supports `scope`, `categories`, `filters`. |
| `memory_add` | Store facts. Accepts `text` or `facts` array, `category`, `importance`, `metadata`. |
| `memory_get` | Retrieve a single memory by ID |
| `memory_list` | List all memories. Filter by `userId`, `agentId`, `scope`. |
| `memory_update` | Update a memory's text in place. Preserves history. |
| `memory_delete` | Delete by `memoryId`, `query` (search-and-delete), or `all: true`. |
| `memory_event_list` | List recent background processing events (platform mode only). |
| `memory_event_status` | Get status of a specific event by ID (platform mode only). |
The `memory_search` and `memory_list` tools accept a `scope` parameter (`"session"`, `"long-term"`, or `"all"`) to control which memories are queried. The `memory_store` tool accepts a `longTerm` boolean (default: `true`) to choose where to store.
The `memory_search` and `memory_list` tools accept a `scope` parameter (`"session"`, `"long-term"`, or `"all"`) to control which memories are queried.
## CLI Commands
@@ -123,8 +235,9 @@ openclaw mem0 search "what languages does the user know" --scope long-term
# Search only session/short-term memories
openclaw mem0 search "what languages does the user know" --scope session
# View stats
openclaw mem0 stats
# List all memories
openclaw mem0 list
openclaw mem0 list --user-id alice --top-k 20
```
## Configuration Options
@@ -134,7 +247,7 @@ openclaw mem0 stats
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `mode` | `"platform"` \| `"open-source"` | `"platform"` | Which backend to use |
| `userId` | `string` | `"default"` | Scope memories per user |
| `userId` | `string` | OS username | Scope memories per user |
| `autoRecall` | `boolean` | `true` | Inject memories before each turn |
| `autoCapture` | `boolean` | `true` | Store facts after each turn |
| `topK` | `number` | `5` | Max memories per recall |
@@ -145,8 +258,6 @@ openclaw mem0 stats
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `apiKey` | `string` | — | **Required.** Mem0 API key (supports `${MEM0_API_KEY}`) |
| `orgId` | `string` | — | Organization ID |
| `projectId` | `string` | — | Project ID |
| `customInstructions` | `string` | *(built-in)* | Extraction rules — what to store, how to format |
| `customCategories` | `object` | *(12 defaults)* | Category name → description map for tagging |
@@ -162,15 +273,77 @@ openclaw mem0 stats
| `oss.llm.provider` | `string` | `"openai"` | LLM provider (`"openai"`, `"anthropic"`, `"ollama"`, etc.) |
| `oss.llm.config` | `object` | — | Provider config: `apiKey`, `model`, `baseURL`, `temperature` |
| `oss.historyDbPath` | `string` | — | SQLite path for memory edit history |
| `oss.disableHistory` | `boolean` | `false` | Disable memory edit history tracking |
Everything inside `oss` is optional — defaults use OpenAI embeddings (`text-embedding-3-small`), in-memory vector store, and OpenAI LLM.
## Plugin Management
### Updating the Plugin
```bash
openclaw plugins update @mem0/openclaw-mem0
```
<Note>Use the npm package name (`@mem0/openclaw-mem0`) for plugin management commands, not the plugin ID (`openclaw-mem0`).</Note>
### Checking Plugin Status
```bash
openclaw plugins list
openclaw plugins inspect openclaw-mem0
```
## Troubleshooting
### "plugins.allow excludes mem0" Error
If you see an error like:
```
[openclaw] Failed to start CLI: Error: The `openclaw mem0` command is unavailable
because `plugins.allow` excludes "mem0". Add "mem0" to `plugins.allow` if you want
that bundled plugin CLI surface.
```
Add `mem0` to your `plugins.allow` list in `openclaw.json`:
```json5
{
"plugins": {
"allow": ["mem0"],
"slots": {
"memory": "openclaw-mem0"
}
}
}
```
### Plugin Not Activating
If the plugin installs but doesn't work:
1. Verify `plugins.slots.memory` is set to `"openclaw-mem0"` (not the npm package name)
2. Check `openclaw plugins list --enabled` to confirm the plugin is loaded
3. Run `openclaw mem0 status` to verify configuration
### Plugin Update Not Working
If `openclaw plugins update` fails:
1. Use the full npm package name: `openclaw plugins update @mem0/openclaw-mem0`
2. If that fails, uninstall and reinstall:
```bash
openclaw plugins uninstall openclaw-mem0
openclaw plugins install @mem0/openclaw-mem0
```
## Key Features
1. **Zero Configuration** — Auto-recall and auto-capture work out of the box with no prompting required
2. **Dual Memory Scopes** — Session-scoped short-term and user-scoped long-term memories
3. **Flexible Backend** — Use Mem0 Cloud for managed service or self-host with open-source mode
4. **Rich Tool Suite** — Five agent tools for explicit memory operations when needed
4. **Rich Tool Suite** — Eight agent tools for explicit memory operations when needed
## Conclusion
-50
View File
@@ -1,50 +0,0 @@
---
title: "Raycast Extension"
description: "Mem0 Raycast extension for intelligent memory management"
---
Mem0 is a self-improving memory layer for LLM applications, enabling personalized AI experiences that save costs and delight users. This extension lets you store and retrieve text snippets using Mem0's intelligent memory system. Find Mem0 in [Raycast Store](https://www.raycast.com/dev_khant/mem0) for using it.
## Getting Started
**Get your API Key**: You'll need a Mem0 API key to use this extension:
a. Sign up at <a href="https://app.mem0.ai" rel="nofollow">app.mem0.ai</a>
b. Navigate to your API Keys page
c. Copy your API key
d. Enter this key in the extension preferences
**Basic Usage**:
- Store memories and text snippets
- Retrieve context-aware information
- Manage persistent user preferences
- Search through stored memories
## Features
**Remember Everything**: Never lose important information. Store notes, preferences, and conversations that your AI can recall later.
**Smart Connections**: Automatically links related topics, helping you discover useful connections.
**Cost Saver**: Spend less on AI usage by efficiently retrieving relevant information instead of regenerating responses.
## How This Helps You
**More Personal Experience**: Your AI remembers your preferences and past conversations, making interactions feel more natural.
**Learn Your Style**: Adapts to how you work and what you like, becoming more helpful over time.
**No More Repetition**: Stop explaining the same things repeatedly. Your AI remembers your context and preferences.
<CardGroup cols={2}>
<Card title="OpenAI Agents SDK" icon="cube" href="/integrations/openai-agents-sdk">
Build desktop AI agents with OpenAI SDK
</Card>
<Card title="Mastra Integration" icon="star" href="/integrations/mastra">
Create intelligent desktop workflows
</Card>
</CardGroup>
-8
View File
@@ -231,8 +231,6 @@ If the user is on a pre-current major (Python < 2, TS < 3, or Platform `output_f
- [LangChain Tools](https://docs.mem0.ai/integrations/langchain-tools) [Both]: Use when Mem0 should be exposed as a LangChain tool.
- [LlamaIndex](https://docs.mem0.ai/integrations/llama-index) [Both]: Use when layering memory on a LlamaIndex RAG app.
- [CrewAI](https://docs.mem0.ai/integrations/crewai) [Both]: Use when building CrewAI multi-agent systems.
- [AutoGen](https://docs.mem0.ai/integrations/autogen) [Both]: Use when the user is on Microsoft AutoGen.
- [Agno](https://docs.mem0.ai/integrations/agno) [Both]: Use when the user is on Agno.
- [Camel AI](https://docs.mem0.ai/integrations/camel-ai) [Both]: Use when the user is on Camel AI.
- [ChatDev](https://docs.mem0.ai/integrations/chatdev) [Both]: Use when the user is on ChatDev.
- [Hermes](https://docs.mem0.ai/integrations/hermes) [Both]: Use when the user is on Hermes.
@@ -255,12 +253,6 @@ If the user is on a pre-current major (Python < 2, TS < 3, or Platform `output_f
### Cloud & Infrastructure
- [AWS Bedrock](https://docs.mem0.ai/integrations/aws-bedrock) [Both]: Use when the user is on AWS Bedrock managed AI services.
### Developer Tools
- [Dify](https://docs.mem0.ai/integrations/dify) [Both]: Use when the user is on Dify LLMOps.
- [Flowise](https://docs.mem0.ai/integrations/flowise) [Both]: Use when the user is on Flowise no-code.
- [AgentOps](https://docs.mem0.ai/integrations/agentops) [Both]: Use when tracking agent observability with memory metadata.
- [Keywords AI](https://docs.mem0.ai/integrations/keywords) [Both]: Use when monitoring with Keywords AI.
- [Raycast](https://docs.mem0.ai/integrations/raycast) [Both]: Use when the user wants quick memory access via Raycast.
## Cookbooks
@@ -115,17 +115,15 @@ config = {
}
},
"custom_instructions": custom_instructions,
"version": "v1.1"
}
m = Memory.from_config(config_dict=config)
m = Memory.from_config(config)
```
```ts TypeScript
import { Memory } from "mem0ai/oss";
const config = {
version: "v1.1",
llm: {
provider: "openai",
config: {
@@ -51,8 +51,7 @@ m = Memory()
# Search with simple metadata filters
results = m.search(
"What are my preferences?",
user_id="alice",
filters={"category": "preferences"}
filters={"user_id": "alice", "category": "preferences"}
)
```
@@ -68,8 +67,8 @@ Layer greater-than/less-than comparisons to rank results by score, confidence, o
# Greater than / Less than
results = m.search(
"recent activities",
user_id="alice",
filters={
"user_id": "alice",
"score": {"gt": 0.8},
"priority": {"gte": 5},
"confidence": {"lt": 0.9},
@@ -80,8 +79,8 @@ results = m.search(
# Equality operators
results = m.search(
"specific content",
user_id="alice",
filters={
"user_id": "alice",
"status": {"eq": "active"},
"archived": {"ne": True}
}
@@ -96,8 +95,8 @@ Use `in` and `nin` when you want to pre-approve or exclude specific values witho
# In / Not in operators
results = m.search(
"multi-category search",
user_id="alice",
filters={
"user_id": "alice",
"category": {"in": ["food", "travel", "entertainment"]},
"status": {"nin": ["deleted", "archived"]}
}
@@ -116,8 +115,8 @@ results = m.search(
# Text matching operators
results = m.search(
"content search",
user_id="alice",
filters={
"user_id": "alice",
"title": {"contains": "meeting"},
"description": {"icontains": "important"},
"tags": {"contains": "urgent"}
@@ -133,8 +132,8 @@ Allow any value for a field while still requiring the field to exist—handy whe
# Match any value for a field
results = m.search(
"all with category",
user_id="alice",
filters={
"user_id": "alice",
"category": "*"
}
)
@@ -148,9 +147,9 @@ Combine filters with `AND`, `OR`, and `NOT` to express complex decision trees. N
# Logical AND
results = m.search(
"complex query",
user_id="alice",
filters={
"AND": [
{"user_id": "alice"},
{"category": "work"},
{"priority": {"gte": 7}},
{"status": {"ne": "completed"}}
@@ -161,12 +160,16 @@ results = m.search(
# Logical OR
results = m.search(
"flexible query",
user_id="alice",
filters={
"OR": [
{"category": "urgent"},
{"priority": {"gte": 9}},
{"deadline": {"contains": "today"}}
"AND": [
{"user_id": "alice"},
{
"OR": [
{"category": "urgent"},
{"priority": {"gte": 9}},
{"deadline": {"contains": "today"}}
]
}
]
}
)
@@ -174,11 +177,15 @@ results = m.search(
# Logical NOT
results = m.search(
"exclusion query",
user_id="alice",
filters={
"NOT": [
{"category": "archived"},
{"status": "deleted"}
"AND": [
{"user_id": "alice"},
{
"NOT": [
{"category": "archived"},
{"status": "deleted"}
]
}
]
}
)
@@ -186,9 +193,9 @@ results = m.search(
# Complex nested logic
results = m.search(
"advanced query",
user_id="alice",
filters={
"AND": [
{"user_id": "alice"},
{
"OR": [
{"category": "work"},
@@ -288,16 +295,15 @@ Vector store support varies. Confirm operator coverage before shipping:
# Before (v0.x) - simple key-value filtering only
results = m.search(
"query",
user_id="alice",
filters={"category": "work", "status": "active"}
filters={"user_id": "alice", "category": "work", "status": "active"}
)
# After (v1.0.0) - enhanced filtering with operators
results = m.search(
"query",
user_id="alice",
filters={
"AND": [
{"user_id": "alice"},
{"category": "work"},
{"status": {"ne": "archived"}},
{"priority": {"gte": 5}}
@@ -320,9 +326,9 @@ results = m.search(
# Find high-priority active tasks
results = m.search(
"What tasks need attention?",
user_id="project_manager",
filters={
"AND": [
{"user_id": "project_manager"},
{"project": {"in": ["alpha", ""]}},
{"priority": {"gte": 8}},
{"status": {"ne": "completed"}},
@@ -347,9 +353,9 @@ results = m.search(
# Find recent unresolved tickets
results = m.search(
"pending support issues",
agent_id="support_bot",
filters={
"AND": [
{"agent_id": "support_bot"},
{"ticket_status": {"ne": "resolved"}},
{"priority": {"in": ["high", "critical"]}},
{"created_date": {"gte": "2024-01-01"}},
@@ -364,7 +370,7 @@ results = m.search(
```
<Tip>
Pair `agent_id` filters with ticket-specific metadata so shared support bots return only the tickets they can act on in the current session.
Pair agent ID filters with ticket-specific metadata so shared support bots return only the tickets they can act on in the current session.
</Tip>
### Content recommendation filtering
@@ -373,9 +379,9 @@ results = m.search(
# Personalized content filtering
results = m.search(
"recommend content",
user_id="reader123",
filters={
"AND": [
{"user_id": "reader123"},
{
"OR": [
{"genre": {"in": ["sci-fi", "fantasy"]}},
@@ -400,8 +406,8 @@ results = m.search(
try:
results = m.search(
"test query",
user_id="alice",
filters={
"user_id": "alice",
"invalid_operator": {"unknown": "value"}
}
)
@@ -409,8 +415,7 @@ except ValueError as e:
print(f"Filter error: {e}")
results = m.search(
"test query",
user_id="alice",
filters={"category": "general"}
filters={"user_id": "alice", "category": "general"}
)
```
+8 -10
View File
@@ -189,7 +189,7 @@ async_memory = AsyncMemory.from_config(config)
async def search_with_rerank():
return await async_memory.search(
"What are my preferences?",
user_id="alice",
filters={"user_id": "alice"},
rerank=True
)
@@ -272,7 +272,7 @@ results = m.search("query", filters={"user_id": "alice"})
```python
results = m.search(
"What are my food preferences?",
user_id="alice"
filters={"user_id": "alice"}
)
for result in results["results"]:
@@ -289,13 +289,13 @@ for result in results["results"]:
```python
results_with_rerank = m.search(
"What movies do I like?",
user_id="alice",
filters={"user_id": "alice"},
rerank=True
)
results_without_rerank = m.search(
"What movies do I like?",
user_id="alice",
filters={"user_id": "alice"},
rerank=False
)
```
@@ -313,9 +313,9 @@ results_without_rerank = m.search(
```python
results = m.search(
"important work tasks",
user_id="alice",
filters={
"AND": [
{"user_id": "alice"},
{"category": "work"},
{"priority": {"gte": 7}}
]
@@ -348,8 +348,7 @@ m = Memory.from_config(config)
results = m.search(
"customer having login issues with mobile app",
agent_id="support_bot",
filters={"category": "technical_support"},
filters={"agent_id": "support_bot", "category": "technical_support"},
rerank=True
)
```
@@ -363,8 +362,7 @@ results = m.search(
```python
results = m.search(
"science fiction books with space exploration themes",
user_id="reader123",
filters={"content_type": "book_recommendation"},
filters={"user_id": "reader123", "content_type": "book_recommendation"},
rerank=True,
top_k=10
)
@@ -383,9 +381,9 @@ for result in results["results"]:
```python
results = m.search(
"What restaurants did I enjoy last month that had good vegetarian options?",
user_id="foodie_user",
filters={
"AND": [
{"user_id": "foodie_user"},
{"category": "dining"},
{"rating": {"gte": 4}},
{"date": {"gte": "2024-01-01"}}
-3
View File
@@ -76,7 +76,6 @@ By default the Node SDK uses local-friendly settings (OpenAI `gpt-5-mini`, `text
import { Memory } from "mem0ai/oss";
const memory = new Memory({
version: "v1.1",
embedder: {
provider: "openai",
config: {
@@ -221,7 +220,6 @@ Mem0 offers granular configuration across vector stores, LLMs, embedders, and hi
| Parameter | Description | Default |
| --- | --- | --- |
| `historyDbPath` | Path to history database | `"{mem0_dir}/history.db"` |
| `version` | API version | `"v1.0"` |
| `customInstructions` | Custom processing prompt | `undefined` |
</Accordion>
<Accordion title="History store">
@@ -234,7 +232,6 @@ Mem0 offers granular configuration across vector stores, LLMs, embedders, and hi
<Accordion title="Complete config example">
```ts
const config = {
version: "v1.1",
embedder: {
provider: "openai",
config: {
+5 -1
View File
@@ -1,7 +1,11 @@
// @ts-nocheck
import type { TelemetryClient, TelemetryOptions } from "./telemetry.types";
let version = __MEM0_SDK_VERSION__;
// __MEM0_SDK_VERSION__ is inlined by tsup/esbuild's `define` at build time from
// package.json. In unbundled environments (ts-jest, jest globalSetup) the
// identifier is not defined, so guard with typeof to fall back safely.
let version =
typeof __MEM0_SDK_VERSION__ !== "undefined" ? __MEM0_SDK_VERSION__ : "dev";
// Safely check for process.env in different environments
let MEM0_TELEMETRY = true;
@@ -4,15 +4,12 @@
* Runs a full project cleanup before any integration test starts,
* then waits 10 seconds for the async cleanup to propagate.
*/
import { MemoryClient } from "../../mem0";
export default async function globalSetup() {
const pkg = require("../../../../package.json");
(globalThis as any).__MEM0_SDK_VERSION__ = pkg.version;
const apiKey = process.env.MEM0_API_KEY;
if (!apiKey) return; // skip if no key — tests will be skipped too
const { MemoryClient } = await import("../../mem0");
const client = new MemoryClient({ apiKey });
await client.ping();
@@ -4,15 +4,12 @@
* Runs a full project cleanup after all integration tests complete
* so no test data is left behind.
*/
import { MemoryClient } from "../../mem0";
export default async function globalTeardown() {
const pkg = require("../../../../package.json");
(globalThis as any).__MEM0_SDK_VERSION__ = pkg.version;
const apiKey = process.env.MEM0_API_KEY;
if (!apiKey) return;
const { MemoryClient } = await import("../../mem0");
const client = new MemoryClient({ apiKey });
await client.ping();
+4 -1
View File
@@ -1 +1,4 @@
declare const __MEM0_SDK_VERSION__: string;
// Injected by tsup/esbuild's `define` at build time from package.json.
// May be undefined in unbundled environments (ts-jest, jest globalSetup),
// which is why consumers guard the reference with `typeof`.
declare const __MEM0_SDK_VERSION__: string | undefined;
+134 -12
View File
@@ -883,38 +883,160 @@ export function removeCodeBlocks(text: string): string {
/**
* Extracts a JSON object from text that may be wrapped in explanation text.
*
* Some LLMs (especially local models like Ollama/LM Studio) return JSON
* wrapped in conversational text without code fences, e.g.:
* Some LLMs (especially local models like Ollama/LM Studio, or OpenRouter)
* return JSON wrapped in conversational text without code fences, e.g.:
*
* "Here are the facts I extracted:\n{\"facts\": [\"fact1\"]}\nI hope this helps!"
*
* This function first tries `removeCodeBlocks` for code-fence-wrapped JSON,
* then falls back to locating the first `{` and last `}` to extract the
* outermost JSON object.
* This function:
* 1. Strips known noise tokens from OpenRouter and other providers
* 2. Removes code fences and <think> blocks
* 3. Tries to find a valid JSON object by testing each `{` as a starting point
* 4. Falls back to first/last brace matching if validation isn't possible
*
* @param text - The raw LLM response text
* @returns The extracted JSON string, or the original text if no JSON object
* boundaries are found
*/
export function extractJson(text: string): string {
// Step 1: Strip code fences if present
const cleaned = removeCodeBlocks(text);
// Step 1: Strip known noise tokens from OpenRouter/local models
let cleaned = text
.replace(/<\|end_of_text\|>/g, "")
.replace(/<\|eot_id\|>/g, "")
.replace(/<\|im_end\|>/g, "")
.replace(/<\|im_start\|>/g, "")
.replace(/<\|endoftext\|>/g, "");
// Step 2: Strip code fences and <think> blocks
cleaned = removeCodeBlocks(cleaned);
const trimmed = cleaned.trim();
// Step 2: Try to locate a JSON object by first `{` and last `}` boundaries
if (!trimmed) return "";
// Step 3: Try to find valid JSON object by testing each `{` as potential start
// This handles cases like "Here's the {formatted} output: {...actual json...}"
const braceIndices: number[] = [];
for (let i = 0; i < trimmed.length; i++) {
if (trimmed[i] === "{") braceIndices.push(i);
}
for (const start of braceIndices) {
// Find the matching closing brace by tracking depth
let depth = 0;
let inString = false;
let escapeNext = false;
for (let i = start; i < trimmed.length; i++) {
const char = trimmed[i];
if (escapeNext) {
escapeNext = false;
continue;
}
if (char === "\\") {
escapeNext = true;
continue;
}
if (char === '"' && !escapeNext) {
inString = !inString;
continue;
}
if (inString) continue;
if (char === "{") depth++;
else if (char === "}") {
depth--;
if (depth === 0) {
const candidate = trimmed.substring(start, i + 1);
try {
JSON.parse(candidate);
return candidate; // Valid JSON found
} catch {
// Not valid JSON, try next starting brace
break;
}
}
}
}
}
// Step 4: Fallback - try first/last brace (original behavior for edge cases)
// Only use this if it produces valid JSON
const firstBrace = trimmed.indexOf("{");
const lastBrace = trimmed.lastIndexOf("}");
if (firstBrace !== -1 && lastBrace > firstBrace) {
return trimmed.substring(firstBrace, lastBrace + 1);
const candidate = trimmed.substring(firstBrace, lastBrace + 1);
try {
JSON.parse(candidate);
return candidate;
} catch {
// Not valid JSON, continue to array extraction
}
}
// Step 3: Try to locate a JSON array by first `[` and last `]` boundaries
// Step 5: Try to locate a JSON array by testing each `[` as potential start
const bracketIndices: number[] = [];
for (let i = 0; i < trimmed.length; i++) {
if (trimmed[i] === "[") bracketIndices.push(i);
}
for (const start of bracketIndices) {
let depth = 0;
let inString = false;
let escapeNext = false;
for (let i = start; i < trimmed.length; i++) {
const char = trimmed[i];
if (escapeNext) {
escapeNext = false;
continue;
}
if (char === "\\") {
escapeNext = true;
continue;
}
if (char === '"' && !escapeNext) {
inString = !inString;
continue;
}
if (inString) continue;
if (char === "[") depth++;
else if (char === "]") {
depth--;
if (depth === 0) {
const candidate = trimmed.substring(start, i + 1);
try {
JSON.parse(candidate);
return candidate;
} catch {
break;
}
}
}
}
}
// Fallback for arrays - validate before returning
const firstBracket = trimmed.indexOf("[");
const lastBracket = trimmed.lastIndexOf("]");
if (firstBracket !== -1 && lastBracket > firstBracket) {
return trimmed.substring(firstBracket, lastBracket + 1);
const candidate = trimmed.substring(firstBracket, lastBracket + 1);
try {
JSON.parse(candidate);
return candidate;
} catch {
// Not valid JSON
}
}
// No JSON boundaries found — return as-is and let the caller handle the error
// No valid JSON found — return as-is and let the caller handle the error
return trimmed;
}
+5 -1
View File
@@ -4,7 +4,11 @@ import type {
TelemetryEventData,
} from "./telemetry.types";
let version = __MEM0_SDK_VERSION__;
// __MEM0_SDK_VERSION__ is inlined by tsup/esbuild's `define` at build time from
// package.json. In unbundled environments (ts-jest, jest globalSetup) the
// identifier is not defined, so guard with typeof to fall back safely.
let version =
typeof __MEM0_SDK_VERSION__ !== "undefined" ? __MEM0_SDK_VERSION__ : "dev";
// Safely check for process.env in different environments
let MEM0_TELEMETRY = true;
@@ -148,4 +148,85 @@ That's all I found.`;
const result = extractJson(input);
expect(JSON.parse(result)).toEqual({ facts: ["test"] });
});
it("strips <|end_of_text|> tokens from OpenRouter responses", () => {
const input = '{"facts": ["test"]}<|end_of_text|>';
const result = extractJson(input);
expect(JSON.parse(result)).toEqual({ facts: ["test"] });
});
it("strips <|eot_id|> tokens from OpenRouter responses", () => {
const input = '{"facts": ["hello"]}<|eot_id|>';
const result = extractJson(input);
expect(JSON.parse(result)).toEqual({ facts: ["hello"] });
});
it("strips <|im_end|> tokens from ChatML responses", () => {
const input = '{"memory": [{"text": "test"}]}<|im_end|>';
const result = extractJson(input);
expect(JSON.parse(result)).toEqual({ memory: [{ text: "test" }] });
});
it("strips multiple noise tokens", () => {
const input =
'<|im_start|>assistant\n{"facts": ["data"]}<|im_end|><|end_of_text|>';
const result = extractJson(input);
expect(JSON.parse(result)).toEqual({ facts: ["data"] });
});
// Issue #4737: Leading text with braces that aren't JSON
it("handles leading text containing braces before actual JSON", () => {
const input = 'Here\'s the {formatted} output: {"facts": ["real data"]}';
const result = extractJson(input);
expect(JSON.parse(result)).toEqual({ facts: ["real data"] });
});
it("handles multiple fake braces in leading text", () => {
const input =
"I'll format this {nicely} with {proper} structure:\n" +
'{"memory": [{"id": "1", "text": "actual memory"}]}';
const result = extractJson(input);
expect(JSON.parse(result)).toEqual({
memory: [{ id: "1", text: "actual memory" }],
});
});
it("handles incomplete JSON-like structures in leading text", () => {
const input =
"Based on {user preferences} I found:\n" +
'{"facts": ["User likes TypeScript"]}';
const result = extractJson(input);
expect(JSON.parse(result)).toEqual({ facts: ["User likes TypeScript"] });
});
it("validates JSON and skips malformed candidates", () => {
const input = 'The result is {broken and {"facts": ["valid"]} is here';
const result = extractJson(input);
expect(JSON.parse(result)).toEqual({ facts: ["valid"] });
});
it("handles deeply nested valid JSON after invalid starts", () => {
const input =
"Here {is some {context}} for you:\n" +
'{"memory": [{"nested": {"deep": "value"}}]}';
const result = extractJson(input);
expect(JSON.parse(result)).toEqual({
memory: [{ nested: { deep: "value" } }],
});
});
it("handles JSON with escaped quotes correctly", () => {
const input =
'Output: {"facts": ["User said \\"hello\\"", "Has a \\"test\\" project"]}';
const result = extractJson(input);
expect(JSON.parse(result)).toEqual({
facts: ['User said "hello"', 'Has a "test" project'],
});
});
it("handles arrays with leading brace-like text", () => {
const input = 'Here\'s {some context}. The array is: ["fact1", "fact2"]';
const result = extractJson(input);
expect(JSON.parse(result)).toEqual(["fact1", "fact2"]);
});
});
+2
View File
@@ -1,3 +1,5 @@
node_modules/
package-lock.json
*.db
.claude/
.claude-*/
+97 -21
View File
@@ -4,31 +4,98 @@ Long-term memory for [OpenClaw](https://github.com/openclaw/openclaw) agents, po
Your agent forgets everything between sessions. This plugin fixes that — it watches conversations, extracts what matters, and brings it back when relevant. Automatically.
## Quick Start
## Requirements
Check your OpenClaw version:
```bash
openclaw plugins install @mem0/openclaw-mem0
openclaw --version
# OpenClaw 2026.4.15 (041266a)
```
| OpenClaw Version | Plugin Support |
|------------------|----------------|
| `>= 2026.4.15` | Fully supported |
## Quick Start
The fastest way is to install directly from your OpenClaw chat — no CLI or config editing needed.
Copy and paste this into your OpenClaw chat (Telegram, WhatsApp, default chat, or any channel where your agent lives):
```
Setup Mem0 from mem0.ai/claw-setup
```
OpenClaw installs the plugin, prompts you for your email, and connects your Mem0 account with OTP verification. See [Chat Setup](#chat-setup-recommended) below for the full walkthrough.
If you prefer the OpenClaw CLI, or are setting up self-hosted / open-source mode, see [Manual Config](#manual-config) and [Open-Source (Self-hosted)](#open-source-self-hosted) below.
### Platform (Mem0 Cloud)
Get an API key from [app.mem0.ai](https://app.mem0.ai/dashboard/api-keys):
There are two ways to set up `@mem0/openclaw-mem0` on the Mem0 platform:
```bash
openclaw mem0 init --api-key <your-key> --user-id <your-user-id>
```
- **Chat setup (recommended)** — run the setup inside any OpenClaw chat. No config editing, no API key handling.
- **Manual config** — edit `openclaw.json` directly.
Or configure manually in `openclaw.json`:
#### Chat Setup (Recommended)
```json5
"openclaw-mem0": {
"enabled": true,
"config": {
"apiKey": "${MEM0_API_KEY}",
"userId": "alice"
}
}
```
You no longer need manual config editing to get started. Everything happens inside the OpenClaw chat itself.
1. **Send the setup command to your OpenClaw agent.** Open any OpenClaw channel and paste:
```
Setup Mem0 from mem0.ai/claw-setup
```
OpenClaw responds with a Mem0 setup card and asks: *"What's your email address? I'll send you a verification code to connect your Mem0 account."*
2. **Enter your email.** Type your email address and send it. Mem0 replies: *"Check your email for a 6-digit code and paste it here."*
3. **Paste the OTP.** Copy the 6-digit code from your email inbox and paste it into the chat. You'll see: *"Connected to Mem0."*
That's it. No API key, no config file editing, no environment variables. The plugin is now active and auto-capture and auto-recall are running on every turn.
> The chat flow uses the same underlying config as manual setup — it writes `apiKey` and `userId` into `openclaw.json` for you. You can still open the file to inspect or override values afterward.
#### Manual Config
1. **Install the plugin via the OpenClaw CLI:**
```bash
openclaw plugins install @mem0/openclaw-mem0
```
2. **Get your API key** from [app.mem0.ai](https://app.mem0.ai/dashboard/api-keys).
3. **Select the plugin as your memory backend in `openclaw.json`.** Either initialize via the CLI:
```bash
openclaw mem0 init --api-key <your-key> --user-id <your-user-id>
```
Or add the full config to your `openclaw.json`:
```json5
{
"plugins": {
"slots": {
"memory": "openclaw-mem0"
},
"entries": {
"openclaw-mem0": {
"enabled": true,
"config": {
"apiKey": "${MEM0_API_KEY}",
"userId": "alice"
}
}
}
}
}
```
> **Note:** OpenClaw memory plugins load through an exclusive slot, so install alone does not activate the plugin. You must set `plugins.slots.memory` as shown above.
### Open-Source (Self-hosted)
@@ -37,11 +104,20 @@ No Mem0 key needed. Requires `OPENAI_API_KEY` for default embeddings and LLM. Ve
Defaults: `text-embedding-3-small` for embeddings, `gpt-5.4` for fact extraction.
```json5
"openclaw-mem0": {
"enabled": true,
"config": {
"mode": "open-source",
"userId": "alice"
{
"plugins": {
"slots": {
"memory": "openclaw-mem0"
},
"entries": {
"openclaw-mem0": {
"enabled": true,
"config": {
"mode": "open-source",
"userId": "alice"
}
}
}
}
}
```
-3
View File
@@ -10,9 +10,7 @@ export interface AddOptions {
appId?: string;
runId?: string;
metadata?: Record<string, unknown>;
immutable?: boolean;
infer?: boolean;
expires?: string;
categories?: string[];
}
@@ -24,7 +22,6 @@ export interface SearchOptions {
topK?: number;
threshold?: number;
rerank?: boolean;
keyword?: boolean;
filters?: Record<string, unknown>;
fields?: string[];
}
-3
View File
@@ -107,9 +107,7 @@ export class PlatformBackend implements Backend {
if (opts.appId) payload.app_id = opts.appId;
if (opts.runId) payload.run_id = opts.runId;
if (opts.metadata) payload.metadata = opts.metadata;
if (opts.immutable) payload.immutable = true;
if (opts.infer === false) payload.infer = false;
if (opts.expires) payload.expiration_date = opts.expires;
if (opts.categories) payload.categories = opts.categories;
return (await this._request("POST", "/v1/memories/", {
@@ -168,7 +166,6 @@ export class PlatformBackend implements Backend {
});
if (apiFilters) payload.filters = apiFilters;
if (opts.rerank) payload.rerank = true;
if (opts.keyword) payload.keyword_search = true;
if (opts.fields) payload.fields = opts.fields;
const result = (await this._request("POST", "/v2/memories/search/", {
+125 -22
View File
@@ -19,6 +19,7 @@ export const OPENCLAW_CONFIG_FILE = join(OPENCLAW_CONFIG_DIR, "openclaw.json");
export const DEFAULT_BASE_URL = "https://api.mem0.ai";
const PLUGIN_ID = "openclaw-mem0";
const NPM_PACKAGE = "@mem0/openclaw-mem0";
// ============================================================================
// Types
@@ -41,16 +42,38 @@ export interface PluginAuthConfig {
// OpenClaw config read/write
// ============================================================================
/** Read the full ~/.openclaw/openclaw.json */
/**
* Read the full ~/.openclaw/openclaw.json.
*
* Returns {} only when the file doesn't exist (first-time setup).
* Throws on parse errors to prevent writes from destroying existing config.
*/
function readFullConfig(): Record<string, unknown> {
if (exists(OPENCLAW_CONFIG_FILE)) {
try {
return JSON.parse(readText(OPENCLAW_CONFIG_FILE));
} catch {
/* ignore parse errors */
}
if (!exists(OPENCLAW_CONFIG_FILE)) {
return {};
}
const text = readText(OPENCLAW_CONFIG_FILE);
// Handle empty or whitespace-only files as first-time setup
if (!text.trim()) {
return {};
}
try {
const parsed = JSON.parse(text);
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
throw new Error("Config is not a JSON object");
}
return parsed;
} catch (err) {
// Fail closed: throw so writes don't proceed with empty config
const msg = err instanceof Error ? err.message : String(err);
throw new Error(
`[openclaw-mem0] Failed to parse ${OPENCLAW_CONFIG_FILE}: ${msg}\n` +
`Fix the JSON syntax error manually before running config commands.`,
);
}
return {};
}
/** Write the full ~/.openclaw/openclaw.json (preserves all non-plugin config) */
@@ -87,15 +110,7 @@ export function readPluginAuth(): PluginAuthConfig {
export function writePluginAuth(auth: PluginAuthConfig): void {
const full = readFullConfig() as any;
// Ensure nested structure exists
if (!full.plugins) full.plugins = {};
if (!full.plugins.entries) full.plugins.entries = {};
if (!full.plugins.entries[PLUGIN_ID]) {
full.plugins.entries[PLUGIN_ID] = { enabled: true, config: {} };
}
if (!full.plugins.entries[PLUGIN_ID].config) {
full.plugins.entries[PLUGIN_ID].config = {};
}
ensurePluginStructure(full);
const cfg = full.plugins.entries[PLUGIN_ID].config;
@@ -107,12 +122,81 @@ export function writePluginAuth(auth: PluginAuthConfig): void {
writeFullConfig(full);
}
export function writePluginConfigField(
path: string[],
value: unknown,
): void {
const full = readFullConfig() as any;
/**
* Ensure the plugin has a valid install record and is in plugins.allow.
*
* OpenClaw's `plugins update` command requires a `plugins.installs.<id>`
* record with `source: "npm"` and `spec` to know how to update. Without
* this, `openclaw plugins update` prints "No install record" and skips.
*
* Similarly, if `plugins.allow` exists as an array, the plugin ID must
* be in it or OpenClaw treats the plugin as untrusted.
*
* This is safe to call multiple times — it only writes missing fields.
*/
export function ensureInstallRecord(): void {
try {
const full = readFullConfig() as any;
const entry = full?.plugins?.entries?.[PLUGIN_ID];
const record = full?.plugins?.installs?.[PLUGIN_ID];
const allow = full?.plugins?.allow;
if (
entry?.enabled === true &&
record?.source &&
record?.spec &&
Array.isArray(allow) &&
allow.includes(PLUGIN_ID)
) {
return;
}
ensurePluginStructure(full);
let changed = false;
// Ensure install record exists for `openclaw plugins update` support
if (!full.plugins.installs) full.plugins.installs = {};
if (!full.plugins.installs[PLUGIN_ID]) {
full.plugins.installs[PLUGIN_ID] = {
source: "npm",
spec: `${NPM_PACKAGE}@latest`,
resolvedName: NPM_PACKAGE,
installedAt: new Date().toISOString(),
};
changed = true;
} else {
const record = full.plugins.installs[PLUGIN_ID];
if (!record.source) {
record.source = "npm";
changed = true;
}
if (!record.spec) {
record.spec = `${NPM_PACKAGE}@latest`;
changed = true;
}
if (!record.resolvedName) {
record.resolvedName = NPM_PACKAGE;
changed = true;
}
}
if (!Array.isArray(full.plugins.allow)) {
full.plugins.allow = [PLUGIN_ID];
changed = true;
} else if (!full.plugins.allow.includes(PLUGIN_ID)) {
full.plugins.allow.push(PLUGIN_ID);
changed = true;
}
if (changed) writeFullConfig(full);
} catch {
// Best-effort — don't break plugin loading if config is unreadable
}
}
/** Ensure the nested plugin entry structure exists in the config object. */
function ensurePluginStructure(full: any): void {
if (!full.plugins) full.plugins = {};
if (!full.plugins.entries) full.plugins.entries = {};
if (!full.plugins.entries[PLUGIN_ID]) {
@@ -121,6 +205,15 @@ export function writePluginConfigField(
if (!full.plugins.entries[PLUGIN_ID].config) {
full.plugins.entries[PLUGIN_ID].config = {};
}
}
export function writePluginConfigField(
path: string[],
value: unknown,
): void {
const full = readFullConfig() as any;
ensurePluginStructure(full);
let target = full.plugins.entries[PLUGIN_ID].config;
for (let i = 0; i < path.length - 1; i++) {
@@ -139,3 +232,13 @@ export function getBaseUrl(): string {
const auth = readPluginAuth();
return auth.baseUrl || DEFAULT_BASE_URL;
}
/** Remove anonymousTelemetryId from config (after PostHog aliasing) */
export function clearAnonymousTelemetryId(): void {
const full = readFullConfig() as any;
const cfg = full?.plugins?.entries?.[PLUGIN_ID]?.config;
if (cfg && "anonymousTelemetryId" in cfg) {
delete cfg.anonymousTelemetryId;
writeFullConfig(full);
}
}
+4 -5
View File
@@ -233,20 +233,19 @@ export const mem0ConfigSchema = {
})(),
autoCapture: cfg.autoCapture !== false,
autoRecall: cfg.autoRecall !== false,
// v3.0.0: customPrompt renamed to customInstructions (backwards-compat: accept either)
customInstructions:
typeof cfg.customInstructions === "string"
? cfg.customInstructions
: DEFAULT_CUSTOM_INSTRUCTIONS,
: typeof cfg.customPrompt === "string"
? cfg.customPrompt
: DEFAULT_CUSTOM_INSTRUCTIONS,
customCategories:
cfg.customCategories &&
typeof cfg.customCategories === "object" &&
!Array.isArray(cfg.customCategories)
? (cfg.customCategories as Record<string, string>)
: DEFAULT_CUSTOM_CATEGORIES,
customPrompt:
typeof cfg.customPrompt === "string"
? cfg.customPrompt
: DEFAULT_CUSTOM_INSTRUCTIONS,
searchThreshold:
typeof cfg.searchThreshold === "number" ? cfg.searchThreshold : 0.5,
topK: typeof cfg.topK === "number" ? cfg.topK : 5,
+75 -2
View File
@@ -14,9 +14,41 @@ const NOISE_MESSAGE_PATTERNS: RegExp[] = [
/^(HEARTBEAT_OK|NO_REPLY)$/i,
/^Current time:.*\d{4}/,
/^Pre-compaction memory flush/i,
/^(ok|yes|no|sir|sure|thanks|done|good|nice|cool|got it|it's on|continue)$/i,
/^(ok|yes|no|sir|sure|thanks|done|good|nice|cool|got it|it's on|continue|alright|okay|yep|nope|uh-huh|mm-hmm|hmm)$/i,
/^System: \[.*\] (Slack message edited|Gateway restart|Exec (failed|completed))/,
/^System: \[.*\] ⚠️ Post-Compaction Audit:/,
// JSON-only messages (tool results, metadata)
/^[\s]*\{[\s\S]*\}[\s]*$/,
/^[\s]*\[[\s\S]*\][\s]*$/,
// Empty or whitespace-only after trimming
/^[\s\n\r]*$/,
// Technical noise patterns
/^(Error|Warning|Info|Debug):/i,
/^(Loading|Loaded|Fetching|Fetched|Processing|Processed)\b/i,
/^\[[\d:T\-\.Z]+\]/, // Timestamps like [2024-01-01T12:00:00.000Z]
/^(SUCCESS|FAILURE|PENDING|COMPLETED|FAILED)$/i,
// Tool/function call noise
/^(Calling|Called|Invoking|Invoked|Executing|Executed)\s+(function|tool|method)/i,
/^Tool (call|result|output):/i,
// Single emoji or very short messages
/^[\p{Emoji}\s]{1,5}$/u,
];
/** Patterns for session-specific technical content that should not be stored as memories. */
const SESSION_SPECIFIC_PATTERNS: RegExp[] = [
// Tool availability discussions
/tools?\s+(are|is)\s+(not\s+)?(exposed|available|accessible)/i,
/plugin\s+(does not|doesn't)\s+expose/i,
/I\s+(do not|don't)\s+(currently\s+)?see\s+.*tools?\s+exposed/i,
/memory_(search|get|add|update|delete|list)\s+(tool|is|are)/i,
// Session-specific capability statements
/in\s+this\s+session/i,
/my\s+(live\s+)?callable\s+tool\s+registry/i,
/tools?\s+I\s+(have|currently have)\s+access\s+to/i,
// Plugin/capability status statements
/openclaw-mem0\s+plugin/i,
/memory\s+wiki.*capability/i,
/workspace\s+memory\s+files/i,
];
/** Content fragments that should be stripped from otherwise-valid messages. */
@@ -50,6 +82,31 @@ const NOISE_CONTENT_PATTERNS: Array<{ pattern: RegExp; replacement: string }> =
/Replied message \(untrusted, for context\):\s*```json[\s\S]*?```/g,
replacement: "",
},
// Strip embedded JSON blocks that might contain metadata
{
pattern: /```json\s*\{[\s\S]*?\}\s*```/g,
replacement: "",
},
// Strip code blocks that are just tool outputs
{
pattern: /```(?:text|output|result|log)\s*[\s\S]*?```/gi,
replacement: "",
},
// Strip inline tool call IDs
{
pattern: /\[tool_call_id:[^\]]+\]/g,
replacement: "",
},
// Strip memory IDs from responses
{
pattern: /\(id:\s*[a-f0-9-]+\)/gi,
replacement: "",
},
// Strip session/run IDs
{
pattern: /(?:session|run|agent)[_-]?(?:id|key)?:\s*[a-zA-Z0-9_:-]+/gi,
replacement: "",
},
];
const MAX_MESSAGE_LENGTH = 2000;
@@ -81,6 +138,20 @@ export function isNoiseMessage(content: string): boolean {
return NOISE_MESSAGE_PATTERNS.some((p) => p.test(trimmed));
}
/**
* Check whether a message contains session-specific technical content
* that should not be stored (tool availability discussions, plugin
* capability statements, etc.). These are facts about the current session
* that have no value in future sessions.
*/
export function isSessionSpecificContent(content: string): boolean {
const trimmed = content.trim();
if (!trimmed) return false;
// Check if multiple session-specific patterns match (more confident filtering)
const matches = SESSION_SPECIFIC_PATTERNS.filter((p) => p.test(trimmed));
return matches.length >= 2;
}
/**
* Check whether an assistant message is a generic acknowledgment with no
* extractable facts (e.g. "I see you've shared an update. How can I help?").
@@ -120,7 +191,7 @@ function truncateMessage(content: string): string {
/**
* Full pre-extraction pipeline: drop noise messages, strip noise fragments,
* and truncate remaining messages to a reasonable length.
* filter session-specific content, and truncate remaining messages.
*/
export function filterMessagesForExtraction(
messages: Array<{ role: string; content: string }>,
@@ -131,6 +202,8 @@ export function filterMessagesForExtraction(
// Drop generic assistant acknowledgments that contain no facts
if (msg.role === "assistant" && isGenericAssistantMessage(msg.content))
continue;
// Drop session-specific technical content (tool availability, plugin capabilities)
if (isSessionSpecificContent(msg.content)) continue;
const cleaned = stripNoiseFromContent(msg.content);
if (!cleaned) continue;
filtered.push({ role: msg.role, content: truncateMessage(cleaned) });
+27 -9
View File
@@ -28,6 +28,7 @@ import type {
import { createProvider, providerToBackend } from "./providers.ts";
import { mem0ConfigSchema } from "./config.ts";
import type { FileConfig } from "./config.ts";
import { createPublicArtifactsProvider } from "./public-artifacts.ts";
import { filterMessagesForExtraction } from "./filtering.ts";
import {
effectiveUserId,
@@ -53,13 +54,14 @@ import {
import { PlatformBackend } from "./backend/platform.ts";
import type { Backend } from "./backend/base.ts";
import { registerCliCommands } from "./cli/commands.ts";
import { readPluginAuth } from "./cli/config-file.ts";
import { readPluginAuth, ensureInstallRecord } from "./cli/config-file.ts";
import { registerAllTools } from "./tools/index.ts";
import type { ToolDeps } from "./tools/index.ts";
import { captureEvent } from "./telemetry.ts";
import { bootstrapTelemetryFlag } from "./fs-safe.ts";
bootstrapTelemetryFlag();
ensureInstallRecord();
// ============================================================================
// Re-exports (for tests and external consumers)
@@ -76,6 +78,7 @@ export {
export {
isNoiseMessage,
isGenericAssistantMessage,
isSessionSpecificContent,
stripNoiseFromContent,
filterMessagesForExtraction,
} from "./filtering.ts";
@@ -191,24 +194,43 @@ const memoryPlugin = definePluginEntry({
`openclaw-mem0: registered (mode: ${cfg.mode}, user: ${cfg.userId}, autoRecall: ${cfg.autoRecall}, autoCapture: ${cfg.autoCapture}, skills: ${skillsActive})`,
);
// ========================================================================
// Public Artifacts (for memory-wiki bridge mode)
// ========================================================================
if (typeof api.registerMemoryCapability === "function") {
api.registerMemoryCapability({
publicArtifacts: createPublicArtifactsProvider({
provider,
cfg,
get stateDir() {
return pluginStateDir;
},
effectiveUserId: _effectiveUserId,
}),
});
api.logger.debug("openclaw-mem0: publicArtifacts capability registered");
}
// Helper: build add options
function buildAddOptions(
userIdOverride?: string,
runId?: string,
sessionKey?: string,
): AddOptions {
// v3.0.0: removed output_format, customPrompt renamed to customInstructions
const opts: AddOptions = {
user_id: userIdOverride || _effectiveUserId(sessionKey),
source: "OPENCLAW",
};
if (runId) opts.run_id = runId;
if (cfg.mode === "platform") {
opts.output_format = "v1.1";
}
// Pass customInstructions and customCategories to control what Mem0 extracts
if (cfg.customInstructions) opts.custom_instructions = cfg.customInstructions;
if (cfg.customCategories) opts.custom_categories = cfg.customCategories;
return opts;
}
// Helper: build search options (skills config overrides legacy defaults)
// v3.0.0: removed keyword_search, reranking, filter_memories, limit
function buildSearchOptions(
userIdOverride?: string,
limit?: number,
@@ -219,13 +241,9 @@ const memoryPlugin = definePluginEntry({
const opts: SearchOptions = {
user_id: userIdOverride || _effectiveUserId(sessionKey),
top_k: limit ?? cfg.topK,
limit: limit ?? cfg.topK,
threshold: recallCfg?.threshold ?? cfg.searchThreshold,
keyword_search: recallCfg?.keywordSearch !== false,
reranking: recallCfg?.rerank !== false,
source: "OPENCLAW",
};
if (recallCfg?.filterMemories) opts.filter_memories = true;
if (runId) opts.run_id = runId;
return opts;
}
@@ -950,7 +968,7 @@ function registerHooks(
content: `Current date: ${timestamp}. The user is identified as "${cfg.userId}". Extract durable facts from this conversation. Include this date when storing time-sensitive information.`,
});
const addOpts = buildAddOptions(undefined, sessionId, sessionId);
const addOpts = buildAddOptions(undefined, undefined, sessionId);
const captureStart = Date.now();
provider
.add(formattedMessages, addOpts)
+26
View File
@@ -1,4 +1,29 @@
declare module "openclaw/plugin-sdk" {
export interface MemoryArtifact {
id: string;
type: "memory" | "dream" | "digest" | "entity";
title: string;
content: string;
metadata?: Record<string, unknown>;
createdAt?: string;
updatedAt?: string;
}
export interface PublicArtifactsProvider {
listArtifacts(options?: {
userId?: string;
types?: string[];
limit?: number;
}): Promise<MemoryArtifact[]>;
}
export interface MemoryCapabilityConfig {
promptBuilder?: (ctx: any) => Promise<string | null>;
flushPlanResolver?: (ctx: any) => Promise<any>;
runtime?: Record<string, unknown>;
publicArtifacts?: PublicArtifactsProvider;
}
export interface OpenClawPluginApi {
pluginConfig: Record<string, unknown>;
logger: {
@@ -32,6 +57,7 @@ declare module "openclaw/plugin-sdk" {
start: (...args: any[]) => void;
stop: () => void;
}): void;
registerMemoryCapability?(config: MemoryCapabilityConfig): void;
[key: string]: unknown;
}
}
+13 -3
View File
@@ -1,10 +1,17 @@
{
"id": "openclaw-mem0",
"name": "Memory (Mem0)",
"description": "Mem0 memory backend for OpenClaw — platform or self-hosted open-source. Injects recalled memories into agent context (auto-recall) and extracts facts after each turn (auto-capture). Both configurable via autoRecall/autoCapture settings.",
"version": "1.0.5",
"description": "Mem0 memory backend for OpenClaw — platform or self-hosted open-source. PLATFORM MODE: Sends conversation data to mem0.ai cloud (requires MEM0_API_KEY). OPEN-SOURCE MODE: Stores vectors locally (~/.mem0/history.db) but uses external APIs for embeddings/LLM (default: OpenAI, requires OPENAI_API_KEY). Auto-recall injects memories before agent turns; auto-capture extracts facts after turns. Both configurable via autoRecall/autoCapture settings. Config stored in ~/.openclaw/openclaw.json.",
"version": "1.0.7",
"kind": "memory",
"skills": ["skills"],
"commandAliases": [
{
"name": "mem0",
"cliCommand": "mem0",
"description": "Mem0 memory plugin commands"
}
],
"contracts": {
"tools": [
"memory_search", "memory_add", "memory_get", "memory_list",
@@ -13,7 +20,7 @@
},
"providerAuthEnvVars": {
"mem0": ["MEM0_API_KEY"],
"openclaw-mem0-oss": ["OPENAI_API_KEY"]
"openclaw-mem0-oss": ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "AZURE_OPENAI_API_KEY", "COHERE_API_KEY"]
},
"providerAuthChoices": [
{
@@ -176,6 +183,9 @@
},
"historyDbPath": {
"type": "string"
},
"disableHistory": {
"type": "boolean"
}
}
},
+5 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@mem0/openclaw-mem0",
"version": "1.0.6",
"version": "1.0.7",
"type": "module",
"description": "Mem0 memory backend for OpenClaw — platform or self-hosted open-source",
"license": "Apache-2.0",
@@ -35,7 +35,7 @@
},
"dependencies": {
"@sinclair/typebox": "0.34.47",
"mem0ai": "2.4.5"
"mem0ai": "3.0.1"
},
"openclaw": {
"extensions": [
@@ -48,6 +48,9 @@
"build": {
"openclawVersion": "2026.4.1",
"pluginSdkVersion": "2026.4.1"
},
"install": {
"npmSpec": "@mem0/openclaw-mem0"
}
},
"devDependencies": {
+338 -118
View File
@@ -12,8 +12,8 @@ importers:
specifier: 0.34.47
version: 0.34.47
mem0ai:
specifier: 2.4.5
version: 2.4.5(@anthropic-ai/sdk@0.40.1)(@azure/identity@4.13.0)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260313.1)(@google/genai@1.45.0)(@langchain/core@0.3.80(openai@4.104.0(ws@8.19.0)(zod@3.25.76)))(@mistralai/mistralai@1.15.1)(@qdrant/js-client-rest@1.13.0(typescript@5.9.3))(@supabase/supabase-js@2.99.1)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.8.0)(cloudflare@4.5.0)(groq-sdk@0.3.0)(neo4j-driver@5.28.3)(ollama@0.5.18)(pg@8.11.3)(redis@4.7.1)(ws@8.19.0)
specifier: 3.0.1
version: 3.0.1(@anthropic-ai/sdk@0.40.1)(@azure/identity@4.13.0)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260313.1)(@google/genai@1.45.0)(@langchain/core@0.3.80(openai@4.104.0(ws@8.19.0)(zod@3.25.76)))(@mistralai/mistralai@1.15.1)(@qdrant/js-client-rest@1.13.0(typescript@5.9.3))(@supabase/supabase-js@2.99.1)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.8.0)(cloudflare@4.5.0)(compromise@14.15.0)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.20.0)(redis@5.12.1)(ws@8.19.0)
devDependencies:
'@types/node':
specifier: ^22.15.0
@@ -340,6 +340,9 @@ packages:
'@mistralai/mistralai@1.15.1':
resolution: {integrity: sha512-fb995eiz3r0KsBGtRjFV+/iLbX+UpfalxpF+YitT3R6ukrPD4PN+FGwwmYcRFhNAzVzDUtTVxQYnjQWEnwV5nw==}
'@mongodb-js/saslprep@1.4.8':
resolution: {integrity: sha512-kpjr2jy2w71w0oqAMI8oibBmiF9lXxWkEQs5gMkW4hVE48bsqINGLxnCSYW62ck/NHXJQpQEfA9WlJ1sY0eqBg==}
'@napi-rs/wasm-runtime@1.1.1':
resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==}
@@ -394,34 +397,41 @@ packages:
resolution: {integrity: sha512-oQG/FejNpItrxRHoyctYvT3rwGZOnK4jr3JdppO/c78ktDvkWiPXPHNsrDf33K9sZdRb6PR7gi4noIapu5q4HA==}
engines: {node: '>=18.0.0', pnpm: '>=8'}
'@redis/bloom@1.2.0':
resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==}
'@redis/bloom@5.12.1':
resolution: {integrity: sha512-PUUfv+ms7jgPSBVoo/DN4AkPHj4D5TZSd6SbJX7egzBplkYUcKmHRE8RKia7UtZ8bSQbLguLvxVO+asKtQfZWA==}
engines: {node: '>= 18.19.0'}
peerDependencies:
'@redis/client': ^1.0.0
'@redis/client': ^5.12.1
'@redis/client@1.6.1':
resolution: {integrity: sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==}
engines: {node: '>=14'}
'@redis/graph@1.1.1':
resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==}
'@redis/client@5.12.1':
resolution: {integrity: sha512-7aPGWeqA3uFm43o19umzdl16CEjK/JQGtSXVPevplTaOU3VJA/rseBC1QvYUz9lLDIMBimc4SW/zrW4S89BaCA==}
engines: {node: '>= 18.19.0'}
peerDependencies:
'@redis/client': ^1.0.0
'@node-rs/xxhash': ^1.1.0
'@opentelemetry/api': '>=1 <2'
peerDependenciesMeta:
'@node-rs/xxhash':
optional: true
'@opentelemetry/api':
optional: true
'@redis/json@1.0.7':
resolution: {integrity: sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==}
'@redis/json@5.12.1':
resolution: {integrity: sha512-eOze75esLve4vfqDel7aMX08CNaiLLQS2fV8mpRN9NxPe1rVR4vQyYiW/OgtGUysF6QOr9ANhfxABKNOJfXdKg==}
engines: {node: '>= 18.19.0'}
peerDependencies:
'@redis/client': ^1.0.0
'@redis/client': ^5.12.1
'@redis/search@1.2.0':
resolution: {integrity: sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==}
'@redis/search@5.12.1':
resolution: {integrity: sha512-ItlxbxC9cKI6IU1TLWoczwJCRb6TdmkEpWv05UrPawqaAnWGRu3rcIqsc5vN483T2fSociuyV1UkWIL5I4//2w==}
engines: {node: '>= 18.19.0'}
peerDependencies:
'@redis/client': ^1.0.0
'@redis/client': ^5.12.1
'@redis/time-series@1.1.0':
resolution: {integrity: sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==}
'@redis/time-series@5.12.1':
resolution: {integrity: sha512-c6JL6E3EcZJuNqKFz+KM+l9l5mpcQiKvTwgA3blt5glWJ8hjDk0yeHN3beE/MpqYIQ8UEX44ItQzgkE/gCBELQ==}
engines: {node: '>= 18.19.0'}
peerDependencies:
'@redis/client': ^1.0.0
'@redis/client': ^5.12.1
'@rolldown/binding-android-arm64@1.0.0-rc.9':
resolution: {integrity: sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==}
@@ -724,6 +734,12 @@ packages:
'@types/uuid@10.0.0':
resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
'@types/webidl-conversions@7.0.3':
resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==}
'@types/whatwg-url@13.0.0':
resolution: {integrity: sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==}
'@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
@@ -784,6 +800,12 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
afinn-165-financialmarketnews@3.0.0:
resolution: {integrity: sha512-0g9A1S3ZomFIGDTzZ0t6xmv4AuokBvBmpes8htiyHpH7N4xDmvSQL6UxL/Zcs2ypRb3VwgCscaD8Q3zEawKYhw==}
afinn-165@2.0.2:
resolution: {integrity: sha512-mJ/RLUfpXfQA6bzugv+bBsc/QYkVrKaLYeS8fWBpKbTCsonv4iuV9ET0fgReEunm9vKLkaNgnekuSNlTC3WQ1Q==}
agent-base@7.1.4:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
@@ -815,6 +837,10 @@ packages:
any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
apparatus@0.0.10:
resolution: {integrity: sha512-KLy/ugo33KZA7nugtQ7O0E1c8kQ52N3IvD/XgIh4w/Nr28ypfkwDfA67F1ev4N1m5D+BOk1+b2dEJDfpj/VvZg==}
engines: {node: '>=0.2.6'}
assertion-error@2.0.1:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
@@ -857,19 +883,16 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
bson@7.2.0:
resolution: {integrity: sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==}
engines: {node: '>=20.19.0'}
buffer-equal-constant-time@1.0.1:
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
buffer-writer@2.0.0:
resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==}
engines: {node: '>=4'}
buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
bundle-name@4.1.0:
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
engines: {node: '>=18'}
@@ -936,6 +959,10 @@ packages:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
compromise@14.15.0:
resolution: {integrity: sha512-YEMv5JGWyqRJw5hdZqDVQF3MMlHA6TRiXreR8IYffk6xB7GA5p/8DeDzvg0Jy2tHNGpD+qJGl0+oJwA+5R/sVA==}
engines: {node: '>=12.0.0'}
confbox@0.1.8:
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
@@ -1008,6 +1035,10 @@ packages:
digest-fetch@1.3.0:
resolution: {integrity: sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==}
dotenv@17.4.2:
resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==}
engines: {node: '>=12'}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
@@ -1018,6 +1049,10 @@ packages:
ecdsa-sig-formatter@1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
efrt@2.7.0:
resolution: {integrity: sha512-/RInbCy1d4P6Zdfa+TMVsf/ufZVotat5hCw3QXmWtjU+3pFEOvOQ7ibo3aIxyCJw2leIeAMjmPj+1SLJiCpdrQ==}
engines: {node: '>=12.0.0'}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -1154,10 +1189,6 @@ packages:
resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==}
engines: {node: '>=18'}
generic-pool@3.9.0:
resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==}
engines: {node: '>= 4'}
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
@@ -1189,6 +1220,10 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
grad-school@0.0.5:
resolution: {integrity: sha512-rXunEHF9M9EkMydTBux7+IryYXEZinRk6g8OBOGDBzo/qWJjhTxy86i5q7lQYpCLHN8Sqv1XX3OIOc7ka2gtvQ==}
engines: {node: '>=8.0.0'}
groq-sdk@0.3.0:
resolution: {integrity: sha512-Cdgjh4YoSBE2X4S9sxPGXaAy1dlN4bRtAaDZ3cnq+XsxhhN9WSBeHF64l7LWwuD5ntmw7YC5Vf4Ff1oHCg1LOg==}
@@ -1324,6 +1359,10 @@ packages:
jws@4.0.1:
resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==}
kareem@3.2.0:
resolution: {integrity: sha512-VS8MWZz/cT+SqBCpVfNN4zoVz5VskR3N4+sTmUXme55e9avQHntpwpNq0yjnosISXqwJ3AQVjlbI4Dyzv//JtA==}
engines: {node: '>=18.0.0'}
langsmith@0.3.87:
resolution: {integrity: sha512-XXR1+9INH8YX96FKWc5tie0QixWz6tOqAsAKfcJyPkE0xPep+NDz0IQLR32q4bn10QK3LqD2HN6T3n6z1YLW7Q==}
peerDependencies:
@@ -1466,8 +1505,8 @@ packages:
md5@2.3.0:
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
mem0ai@2.4.5:
resolution: {integrity: sha512-0XMRe5/KKZXkSJDb2YDqgWdIfJpL2cn8Lelx9pvDPJOvBfXx8uHjtSc80jyGyb9C/1wW2uPcaE+WS+Rs/2phCg==}
mem0ai@3.0.1:
resolution: {integrity: sha512-6phM544/3NRcCg7n5DBNRc9uUEMqGTe7sksULM2KM/FTihH27yTl5nPms8cnNpSKM5/ZKJ9jnkpzwGlvYYbtBQ==}
engines: {node: '>=18'}
peerDependencies:
'@anthropic-ai/sdk': ^0.40.1
@@ -1483,12 +1522,20 @@ packages:
'@types/pg': 8.11.0
better-sqlite3: ^12.6.2
cloudflare: ^4.2.0
compromise: ^14.0.0
groq-sdk: 0.3.0
neo4j-driver: ^5.28.1
natural: ^8.0.1
ollama: ^0.5.14
pg: 8.11.3
redis: ^4.6.13
memjs@1.3.2:
resolution: {integrity: sha512-qUEg2g8vxPe+zPn09KidjIStHPtoBO8Cttm8bgJFWWabbsjQ9Av9Ky+6UcvKx6ue0LLb/LEhtcyQpRyKfzeXcg==}
engines: {node: '>=0.10.0'}
memory-pager@1.5.0:
resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==}
micromatch@4.0.8:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
@@ -1522,6 +1569,49 @@ packages:
mlly@1.8.1:
resolution: {integrity: sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==}
mongodb-connection-string-url@7.0.1:
resolution: {integrity: sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==}
engines: {node: '>=20.19.0'}
mongodb@7.1.1:
resolution: {integrity: sha512-067DXiMjcpYQl6bGjWQoTUEE9UoRViTtKFcoqX7z08I+iDZv/emH1g8XEFiO3qiDfXAheT5ozl1VffDTKhIW/w==}
engines: {node: '>=20.19.0'}
peerDependencies:
'@aws-sdk/credential-providers': ^3.806.0
'@mongodb-js/zstd': ^7.0.0
gcp-metadata: ^7.0.1
kerberos: ^7.0.0
mongodb-client-encryption: '>=7.0.0 <7.1.0'
snappy: ^7.3.2
socks: ^2.8.6
peerDependenciesMeta:
'@aws-sdk/credential-providers':
optional: true
'@mongodb-js/zstd':
optional: true
gcp-metadata:
optional: true
kerberos:
optional: true
mongodb-client-encryption:
optional: true
snappy:
optional: true
socks:
optional: true
mongoose@9.4.1:
resolution: {integrity: sha512-4rFBWa+/wdBQSfvnOPJBpiSG6UCEbhSQh865dEdaH9Y8WfHBUC+I2XT28dp0IBIGrEwmh+gzrgZgea5PbmrHWA==}
engines: {node: '>=20.19.0'}
mpath@0.9.0:
resolution: {integrity: sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==}
engines: {node: '>=4.0.0'}
mquery@6.0.0:
resolution: {integrity: sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==}
engines: {node: '>=20.19.0'}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -1540,14 +1630,9 @@ packages:
napi-build-utils@2.0.0:
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
neo4j-driver-bolt-connection@5.28.3:
resolution: {integrity: sha512-wqHBYcU0FVRDmdsoZ+Fk0S/InYmu9/4BT6fPYh45Jimg/J7vQBUcdkiHGU7nop7HRb1ZgJmL305mJb6g5Bv35Q==}
neo4j-driver-core@5.28.3:
resolution: {integrity: sha512-Jk+hAmjFmO5YzVH/U7FyKXigot9zmIfLz6SZQy0xfr4zfTE/S8fOYFOGqKQTHBE86HHOWH2RbTslbxIb+XtU2g==}
neo4j-driver@5.28.3:
resolution: {integrity: sha512-k7c0wEh3HoONv1v5AyLp9/BDAbYHJhz2TZvzWstSEU3g3suQcXmKEaYBfrK2UMzxcy3bCT0DrnfRbzsOW5G/Ag==}
natural@8.1.1:
resolution: {integrity: sha512-Ucb+lsUcGxUqu3rn8cwHjT6gJQosO63nIX/aBQXB3+IDkNbFV7PuviysO+Rzz3aKn7PZhPj3bNF4PS9gDVjYCQ==}
engines: {node: '>=0.4.10'}
node-abi@3.88.0:
resolution: {integrity: sha512-At6b4UqIEVudaqPsXjmUO1r/N5BUr4yhDGs5PkBE8/oG5+TfLPhFechiskFsnT6Ql0VfUXbalUUCbfXxtj7K+w==}
@@ -1622,9 +1707,6 @@ packages:
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
packet-reader@1.0.0:
resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==}
path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
@@ -1666,9 +1748,9 @@ packages:
resolution: {integrity: sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==}
engines: {node: '>=10'}
pg@8.11.3:
resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==}
engines: {node: '>= 8.0.0'}
pg@8.20.0:
resolution: {integrity: sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==}
engines: {node: '>= 16.0.0'}
peerDependencies:
pg-native: '>=3.0.1'
peerDependenciesMeta:
@@ -1773,6 +1855,10 @@ packages:
pump@3.0.4:
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
rc@1.2.8:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true
@@ -1788,8 +1874,9 @@ packages:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
redis@4.7.1:
resolution: {integrity: sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==}
redis@5.12.1:
resolution: {integrity: sha512-LDsoVvb/CpoV9EN3FXvgvSHNJWuCIzl9MiO3ppOevuGLpSGJhwfQjpEwfFJcQvNSddHADDdZaWx0HnmMxRXG7g==}
engines: {node: '>= 18.19.0'}
resolve-from@5.0.0:
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
@@ -1817,12 +1904,13 @@ packages:
resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==}
engines: {node: '>=18'}
rxjs@7.8.2:
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
safe-stable-stringify@2.5.0:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
engines: {node: '>=10'}
semver@7.7.4:
resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
engines: {node: '>=10'}
@@ -1836,6 +1924,9 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
sift@17.1.3:
resolution: {integrity: sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==}
siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
@@ -1864,6 +1955,9 @@ packages:
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
engines: {node: '>= 12'}
sparse-bitfield@3.0.3:
resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==}
split2@4.2.0:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
@@ -1878,6 +1972,10 @@ packages:
std-env@4.0.0:
resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==}
stopwords-iso@1.1.0:
resolution: {integrity: sha512-I6GPS/E0zyieHehMRPQcqkiBMJKGgLta+1hREixhoLPqEA0AlVFiC43dl8uPpmkkeRdDMzYRWFWk5/l9x7nmNg==}
engines: {node: '>=0.10.0'}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
@@ -1906,10 +2004,17 @@ packages:
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
suffix-thumb@5.0.2:
resolution: {integrity: sha512-I5PWXAFKx3FYnI9a+dQMWNqTxoRt6vdBdb0O+BJ1sxXCWtSoQCusc13E58f+9p4MYx/qCnEMkD5jac6K2j3dgA==}
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
sylvester@0.0.21:
resolution: {integrity: sha512-yUT0ukFkFEt4nb+NY+n2ag51aS/u9UHXoZw+A4jgD77/jzZsBoSDHuqysrVCBC4CYR4TYvUJq54ONpXgDBH8tA==}
engines: {node: '>=0.2.6'}
tar-fs@2.1.4:
resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
@@ -1949,6 +2054,10 @@ packages:
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
tr46@5.1.1:
resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==}
engines: {node: '>=18'}
tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
@@ -1989,6 +2098,9 @@ packages:
ufo@1.6.3:
resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==}
underscore@1.13.8:
resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==}
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
@@ -2006,6 +2118,10 @@ packages:
resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
hasBin: true
uuid@13.0.0:
resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==}
hasBin: true
uuid@8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true
@@ -2103,9 +2219,17 @@ packages:
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
webidl-conversions@7.0.0:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
engines: {node: '>=12'}
whatwg-fetch@3.6.20:
resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==}
whatwg-url@14.2.0:
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
engines: {node: '>=18'}
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
@@ -2119,6 +2243,10 @@ packages:
engines: {node: '>=8'}
hasBin: true
wordnet-db@3.1.14:
resolution: {integrity: sha512-zVyFsvE+mq9MCmwXUWHIcpfbrHHClZWZiVOzKSxNJruIcFn2RbY55zkhiAMMxM8zCVSmtNiViq8FsAZSFpMYag==}
engines: {node: '>=0.6.0'}
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
@@ -2150,9 +2278,6 @@ packages:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
zod-to-json-schema@3.25.1:
resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==}
peerDependencies:
@@ -2487,6 +2612,10 @@ snapshots:
- bufferutil
- utf-8-validate
'@mongodb-js/saslprep@1.4.8':
dependencies:
sparse-bitfield: 3.0.3
'@napi-rs/wasm-runtime@1.1.1':
dependencies:
'@emnapi/core': 1.9.0
@@ -2533,31 +2662,25 @@ snapshots:
'@qdrant/openapi-typescript-fetch@1.2.6': {}
'@redis/bloom@1.2.0(@redis/client@1.6.1)':
'@redis/bloom@5.12.1(@redis/client@5.12.1)':
dependencies:
'@redis/client': 1.6.1
'@redis/client': 5.12.1
'@redis/client@1.6.1':
'@redis/client@5.12.1':
dependencies:
cluster-key-slot: 1.1.2
generic-pool: 3.9.0
yallist: 4.0.0
'@redis/graph@1.1.1(@redis/client@1.6.1)':
'@redis/json@5.12.1(@redis/client@5.12.1)':
dependencies:
'@redis/client': 1.6.1
'@redis/client': 5.12.1
'@redis/json@1.0.7(@redis/client@1.6.1)':
'@redis/search@5.12.1(@redis/client@5.12.1)':
dependencies:
'@redis/client': 1.6.1
'@redis/client': 5.12.1
'@redis/search@1.2.0(@redis/client@1.6.1)':
'@redis/time-series@5.12.1(@redis/client@5.12.1)':
dependencies:
'@redis/client': 1.6.1
'@redis/time-series@1.1.0(@redis/client@1.6.1)':
dependencies:
'@redis/client': 1.6.1
'@redis/client': 5.12.1
'@rolldown/binding-android-arm64@1.0.0-rc.9':
optional: true
@@ -2785,6 +2908,12 @@ snapshots:
'@types/uuid@10.0.0': {}
'@types/webidl-conversions@7.0.3': {}
'@types/whatwg-url@13.0.0':
dependencies:
'@types/webidl-conversions': 7.0.3
'@types/ws@8.18.1':
dependencies:
'@types/node': 22.19.15
@@ -2864,6 +2993,10 @@ snapshots:
acorn@8.16.0: {}
afinn-165-financialmarketnews@3.0.0: {}
afinn-165@2.0.2: {}
agent-base@7.1.4: {}
agentkeepalive@4.6.0:
@@ -2884,6 +3017,10 @@ snapshots:
any-promise@1.3.0: {}
apparatus@0.0.10:
dependencies:
sylvester: 0.0.21
assertion-error@2.0.1: {}
ast-v8-to-istanbul@1.0.0:
@@ -2933,20 +3070,15 @@ snapshots:
dependencies:
fill-range: 7.1.1
bson@7.2.0: {}
buffer-equal-constant-time@1.0.1: {}
buffer-writer@2.0.0: {}
buffer@5.7.1:
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
buffer@6.0.3:
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
bundle-name@4.1.0:
dependencies:
run-applescript: 7.1.0
@@ -3008,6 +3140,12 @@ snapshots:
commander@4.1.1: {}
compromise@14.15.0:
dependencies:
efrt: 2.7.0
grad-school: 0.0.5
suffix-thumb: 5.0.2
confbox@0.1.8: {}
consola@3.4.2: {}
@@ -3060,6 +3198,8 @@ snapshots:
base-64: 0.1.0
md5: 2.3.0
dotenv@17.4.2: {}
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -3072,6 +3212,8 @@ snapshots:
dependencies:
safe-buffer: 5.2.1
efrt@2.7.0: {}
emoji-regex@8.0.0: {}
emoji-regex@9.2.2: {}
@@ -3223,8 +3365,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
generic-pool@3.9.0: {}
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -3271,6 +3411,8 @@ snapshots:
graceful-fs@4.2.11: {}
grad-school@0.0.5: {}
groq-sdk@0.3.0:
dependencies:
'@types/node': 18.19.130
@@ -3437,6 +3579,8 @@ snapshots:
jwa: 2.0.1
safe-buffer: 5.2.1
kareem@3.2.0: {}
langsmith@0.3.87(openai@4.104.0(ws@8.19.0)(zod@3.25.76)):
dependencies:
'@types/uuid': 10.0.0
@@ -3543,7 +3687,7 @@ snapshots:
crypt: 0.0.2
is-buffer: 1.1.6
mem0ai@2.4.5(@anthropic-ai/sdk@0.40.1)(@azure/identity@4.13.0)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260313.1)(@google/genai@1.45.0)(@langchain/core@0.3.80(openai@4.104.0(ws@8.19.0)(zod@3.25.76)))(@mistralai/mistralai@1.15.1)(@qdrant/js-client-rest@1.13.0(typescript@5.9.3))(@supabase/supabase-js@2.99.1)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.8.0)(cloudflare@4.5.0)(groq-sdk@0.3.0)(neo4j-driver@5.28.3)(ollama@0.5.18)(pg@8.11.3)(redis@4.7.1)(ws@8.19.0):
mem0ai@3.0.1(@anthropic-ai/sdk@0.40.1)(@azure/identity@4.13.0)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260313.1)(@google/genai@1.45.0)(@langchain/core@0.3.80(openai@4.104.0(ws@8.19.0)(zod@3.25.76)))(@mistralai/mistralai@1.15.1)(@qdrant/js-client-rest@1.13.0(typescript@5.9.3))(@supabase/supabase-js@2.99.1)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.8.0)(cloudflare@4.5.0)(compromise@14.15.0)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.20.0)(redis@5.12.1)(ws@8.19.0):
dependencies:
'@anthropic-ai/sdk': 0.40.1
'@azure/identity': 4.13.0
@@ -3559,12 +3703,13 @@ snapshots:
axios: 1.13.6
better-sqlite3: 12.8.0
cloudflare: 4.5.0
compromise: 14.15.0
groq-sdk: 0.3.0
neo4j-driver: 5.28.3
natural: 8.1.1
ollama: 0.5.18
openai: 4.104.0(ws@8.19.0)(zod@3.25.76)
pg: 8.11.3
redis: 4.7.1
pg: 8.20.0
redis: 5.12.1
uuid: 9.0.1
zod: 3.25.76
transitivePeerDependencies:
@@ -3572,6 +3717,10 @@ snapshots:
- encoding
- ws
memjs@1.3.2: {}
memory-pager@1.5.0: {}
micromatch@4.0.8:
dependencies:
braces: 3.0.3
@@ -3602,6 +3751,38 @@ snapshots:
pkg-types: 1.3.1
ufo: 1.6.3
mongodb-connection-string-url@7.0.1:
dependencies:
'@types/whatwg-url': 13.0.0
whatwg-url: 14.2.0
mongodb@7.1.1:
dependencies:
'@mongodb-js/saslprep': 1.4.8
bson: 7.2.0
mongodb-connection-string-url: 7.0.1
mongoose@9.4.1:
dependencies:
kareem: 3.2.0
mongodb: 7.1.1
mpath: 0.9.0
mquery: 6.0.0
ms: 2.1.3
sift: 17.1.3
transitivePeerDependencies:
- '@aws-sdk/credential-providers'
- '@mongodb-js/zstd'
- gcp-metadata
- kerberos
- mongodb-client-encryption
- snappy
- socks
mpath@0.9.0: {}
mquery@6.0.0: {}
ms@2.1.3: {}
mustache@4.2.0: {}
@@ -3616,19 +3797,33 @@ snapshots:
napi-build-utils@2.0.0: {}
neo4j-driver-bolt-connection@5.28.3:
natural@8.1.1:
dependencies:
buffer: 6.0.3
neo4j-driver-core: 5.28.3
string_decoder: 1.3.0
neo4j-driver-core@5.28.3: {}
neo4j-driver@5.28.3:
dependencies:
neo4j-driver-bolt-connection: 5.28.3
neo4j-driver-core: 5.28.3
rxjs: 7.8.2
afinn-165: 2.0.2
afinn-165-financialmarketnews: 3.0.0
apparatus: 0.0.10
dotenv: 17.4.2
memjs: 1.3.2
mongoose: 9.4.1
pg: 8.20.0
redis: 5.12.1
safe-stable-stringify: 2.5.0
stopwords-iso: 1.1.0
sylvester: 0.0.21
underscore: 1.13.8
uuid: 13.0.0
wordnet-db: 3.1.14
transitivePeerDependencies:
- '@aws-sdk/credential-providers'
- '@mongodb-js/zstd'
- '@node-rs/xxhash'
- '@opentelemetry/api'
- gcp-metadata
- kerberos
- mongodb-client-encryption
- pg-native
- snappy
- socks
node-abi@3.88.0:
dependencies:
@@ -3700,8 +3895,6 @@ snapshots:
package-json-from-dist@1.0.1: {}
packet-reader@1.0.0: {}
path-key@3.1.1: {}
path-scurry@1.11.1:
@@ -3720,9 +3913,9 @@ snapshots:
pg-numeric@1.0.2: {}
pg-pool@3.13.0(pg@8.11.3):
pg-pool@3.13.0(pg@8.20.0):
dependencies:
pg: 8.11.3
pg: 8.20.0
pg-protocol@1.13.0: {}
@@ -3744,12 +3937,10 @@ snapshots:
postgres-interval: 3.0.0
postgres-range: 1.1.4
pg@8.11.3:
pg@8.20.0:
dependencies:
buffer-writer: 2.0.0
packet-reader: 1.0.0
pg-connection-string: 2.12.0
pg-pool: 3.13.0(pg@8.11.3)
pg-pool: 3.13.0(pg@8.20.0)
pg-protocol: 1.13.0
pg-types: 2.2.0
pgpass: 1.0.5
@@ -3851,6 +4042,8 @@ snapshots:
end-of-stream: 1.4.5
once: 1.4.0
punycode@2.3.1: {}
rc@1.2.8:
dependencies:
deep-extend: 0.6.0
@@ -3868,14 +4061,16 @@ snapshots:
readdirp@4.1.2: {}
redis@4.7.1:
redis@5.12.1:
dependencies:
'@redis/bloom': 1.2.0(@redis/client@1.6.1)
'@redis/client': 1.6.1
'@redis/graph': 1.1.1(@redis/client@1.6.1)
'@redis/json': 1.0.7(@redis/client@1.6.1)
'@redis/search': 1.2.0(@redis/client@1.6.1)
'@redis/time-series': 1.1.0(@redis/client@1.6.1)
'@redis/bloom': 5.12.1(@redis/client@5.12.1)
'@redis/client': 5.12.1
'@redis/json': 5.12.1(@redis/client@5.12.1)
'@redis/search': 5.12.1(@redis/client@5.12.1)
'@redis/time-series': 5.12.1(@redis/client@5.12.1)
transitivePeerDependencies:
- '@node-rs/xxhash'
- '@opentelemetry/api'
resolve-from@5.0.0: {}
@@ -3939,12 +4134,10 @@ snapshots:
run-applescript@7.1.0: {}
rxjs@7.8.2:
dependencies:
tslib: 2.8.1
safe-buffer@5.2.1: {}
safe-stable-stringify@2.5.0: {}
semver@7.7.4: {}
shebang-command@2.0.0:
@@ -3953,6 +4146,8 @@ snapshots:
shebang-regex@3.0.0: {}
sift@17.1.3: {}
siginfo@2.0.0: {}
signal-exit@4.1.0: {}
@@ -3973,6 +4168,10 @@ snapshots:
source-map@0.7.6: {}
sparse-bitfield@3.0.3:
dependencies:
memory-pager: 1.5.0
split2@4.2.0: {}
stack-utils@2.0.6:
@@ -3983,6 +4182,8 @@ snapshots:
std-env@4.0.0: {}
stopwords-iso@1.1.0: {}
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
@@ -4019,10 +4220,14 @@ snapshots:
tinyglobby: 0.2.15
ts-interface-checker: 0.1.13
suffix-thumb@5.0.2: {}
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
sylvester@0.0.21: {}
tar-fs@2.1.4:
dependencies:
chownr: 1.1.4
@@ -4065,6 +4270,10 @@ snapshots:
tr46@0.0.3: {}
tr46@5.1.1:
dependencies:
punycode: 2.3.1
tree-kill@1.2.2: {}
ts-interface-checker@0.1.13: {}
@@ -4107,6 +4316,8 @@ snapshots:
ufo@1.6.3: {}
underscore@1.13.8: {}
undici-types@5.26.5: {}
undici-types@6.21.0: {}
@@ -4119,6 +4330,8 @@ snapshots:
uuid@10.0.0: {}
uuid@13.0.0: {}
uuid@8.3.2: {}
uuid@9.0.1: {}
@@ -4169,8 +4382,15 @@ snapshots:
webidl-conversions@3.0.1: {}
webidl-conversions@7.0.0: {}
whatwg-fetch@3.6.20: {}
whatwg-url@14.2.0:
dependencies:
tr46: 5.1.1
webidl-conversions: 7.0.0
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
@@ -4185,6 +4405,8 @@ snapshots:
siginfo: 2.0.0
stackback: 0.0.2
wordnet-db@3.1.14: {}
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
@@ -4207,8 +4429,6 @@ snapshots:
xtend@4.0.2: {}
yallist@4.0.0: {}
zod-to-json-schema@3.25.1(zod@3.25.76):
dependencies:
zod: 3.25.76
+61 -55
View File
@@ -111,21 +111,19 @@ class PlatformProvider implements Mem0Provider {
options: AddOptions,
): Promise<AddResult> {
await this.ensureClient();
const opts: Record<string, unknown> = { user_id: options.user_id };
if (options.run_id) opts.run_id = options.run_id;
// v3.0.0: SDK uses camelCase (userId, runId, etc.) - it converts to snake_case internally
const opts: Record<string, unknown> = { userId: options.user_id };
if (options.run_id) opts.runId = options.run_id;
if (options.custom_instructions)
opts.custom_instructions = options.custom_instructions;
opts.customInstructions = options.custom_instructions;
if (options.custom_categories)
opts.custom_categories = options.custom_categories;
if (options.output_format) opts.output_format = options.output_format;
opts.customCategories = options.custom_categories;
if (options.source) opts.source = options.source;
// Agentic harness: direct storage bypass
if (options.infer !== undefined) opts.infer = options.infer;
if (options.deduced_memories)
opts.deduced_memories = options.deduced_memories;
opts.deducedMemories = options.deduced_memories;
if (options.metadata) opts.metadata = options.metadata;
if (options.expiration_date) opts.expiration_date = options.expiration_date;
if (options.immutable) opts.immutable = options.immutable;
const result = await this.client.add(messages, opts);
return normalizeAddResult(result);
@@ -133,19 +131,15 @@ class PlatformProvider implements Mem0Provider {
async search(query: string, options: SearchOptions): Promise<MemoryItem[]> {
await this.ensureClient();
const opts: Record<string, unknown> = {
api_version: "v2",
user_id: options.user_id,
};
if (options.run_id) opts.run_id = options.run_id;
if (options.top_k != null) opts.top_k = options.top_k;
// v3.0.0: SDK uses camelCase options, userId must be in filters
const opts: Record<string, unknown> = {};
if (options.top_k != null) opts.topK = options.top_k;
if (options.threshold != null) opts.threshold = options.threshold;
if (options.keyword_search != null)
opts.keyword_search = options.keyword_search;
if (options.reranking != null) opts.rerank = options.reranking;
if (options.filter_memories != null)
opts.filter_memories = options.filter_memories;
if (options.categories != null) opts.categories = options.categories;
// Build filters with user_id/run_id inside (v3.0.0 requirement)
// Filters use snake_case as they're passed directly to the API
// Note: source is NOT a valid filter field - only used when adding
const baseFilters: Record<string, unknown> = { user_id: options.user_id };
if (options.run_id) baseFilters.run_id = options.run_id;
@@ -167,16 +161,14 @@ class PlatformProvider implements Mem0Provider {
async getAll(options: ListOptions): Promise<MemoryItem[]> {
await this.ensureClient();
const opts: Record<string, unknown> = {
api_version: "v2",
user_id: options.user_id,
filters: { user_id: options.user_id },
};
if (options.run_id) {
opts.run_id = options.run_id;
(opts.filters as Record<string, unknown>).run_id = options.run_id;
}
if (options.page_size != null) opts.page_size = options.page_size;
// v3.0.0: SDK uses camelCase options, userId must be in filters
// Filters use snake_case as they're passed directly to the API
// Note: source is NOT a valid filter field - only used when adding
const filters: Record<string, unknown> = { user_id: options.user_id };
if (options.run_id) filters.run_id = options.run_id;
const opts: Record<string, unknown> = { filters };
if (options.page_size != null) opts.pageSize = options.page_size;
const results = await this.client.getAll(opts);
if (Array.isArray(results)) return results.map(normalizeMemoryItem);
@@ -198,7 +190,8 @@ class PlatformProvider implements Mem0Provider {
async deleteAll(userId: string): Promise<void> {
await this.ensureClient();
await this.client.deleteAll({ user_id: userId });
// v3.0.0: SDK uses camelCase
await this.client.deleteAll({ userId });
}
async history(memoryId: string): Promise<
@@ -226,7 +219,7 @@ class OSSProvider implements Mem0Provider {
constructor(
private readonly ossConfig?: Mem0Config["oss"],
private readonly customPrompt?: string,
private readonly customInstructions?: string,
private readonly resolvePath?: (p: string) => string,
) {}
@@ -241,7 +234,8 @@ class OSSProvider implements Mem0Provider {
}
private _buildConfig(disableHistory = false): Record<string, unknown> {
const config: Record<string, unknown> = { version: "v1.1" };
// v3.0.0: removed version field
const config: Record<string, unknown> = {};
const defaultEmbedder = {
provider: "openai",
@@ -299,7 +293,8 @@ class OSSProvider implements Mem0Provider {
config.disableHistory = true;
}
if (this.customPrompt) config.customPrompt = this.customPrompt;
// v3.0.0: customPrompt renamed to customInstructions
if (this.customInstructions) config.customInstructions = this.customInstructions;
return config;
}
@@ -347,7 +342,8 @@ class OSSProvider implements Mem0Provider {
}
}
await mem.getAll({ userId: "__mem0_warmup__" });
// v3.0.0: entity IDs must be in filters, not top-level
await mem.getAll({ filters: { user_id: "__mem0_warmup__" } });
this.memory = mem;
}
@@ -364,9 +360,7 @@ class OSSProvider implements Mem0Provider {
// Agentic harness: direct storage bypass
if (options.infer !== undefined) addOpts.infer = options.infer;
if (options.metadata) addOpts.metadata = options.metadata;
if (options.expiration_date)
addOpts.expirationDate = options.expiration_date;
if (options.immutable) addOpts.immutable = options.immutable;
// v3.0.0: removed expiration_date, immutable
// OSS SDK doesn't support deduced_memories — when infer=false, it stores
// raw message content directly. Rewrite messages to contain the facts so
@@ -385,17 +379,24 @@ class OSSProvider implements Mem0Provider {
async search(query: string, options: SearchOptions): Promise<MemoryItem[]> {
await this.ensureMemory();
// OSS SDK uses camelCase: userId/runId, not user_id/run_id
const opts: Record<string, unknown> = { userId: options.user_id };
if (options.run_id) opts.runId = options.run_id;
if (options.limit != null) opts.limit = options.limit;
else if (options.top_k != null) opts.limit = options.top_k;
if (options.keyword_search != null)
opts.keyword_search = options.keyword_search;
if (options.reranking != null) opts.reranking = options.reranking;
if (options.source) opts.source = options.source;
// v3.0.0: entity IDs must be in filters, not top-level; limit renamed to topK
const opts: Record<string, unknown> = {};
if (options.top_k != null) opts.topK = options.top_k;
if (options.threshold != null) opts.threshold = options.threshold;
// Build filters with user_id/run_id inside (v3.0.0 requirement)
// Filters use snake_case as they're passed directly to the vector store
// Note: source is NOT a valid filter field - only used when adding
const baseFilters: Record<string, unknown> = { user_id: options.user_id };
if (options.run_id) baseFilters.run_id = options.run_id;
// Merge with any additional user-provided filters
if (options.filters) {
opts.filters = { AND: [baseFilters, options.filters] };
} else {
opts.filters = baseFilters;
}
const results = await this.memory.search(query, opts);
const normalized = normalizeSearchResults(results);
@@ -417,10 +418,16 @@ class OSSProvider implements Mem0Provider {
async getAll(options: ListOptions): Promise<MemoryItem[]> {
await this.ensureMemory();
// OSS SDK uses camelCase: userId/runId, not user_id/run_id
const getAllOpts: Record<string, unknown> = { userId: options.user_id };
if (options.run_id) getAllOpts.runId = options.run_id;
if (options.source) getAllOpts.source = options.source;
// v3.0.0: entity IDs must be in filters, not top-level
// Filters use snake_case as they're passed directly to the vector store
// Note: source is NOT a valid filter field - only used when adding
const filters: Record<string, unknown> = { user_id: options.user_id };
if (options.run_id) filters.run_id = options.run_id;
// OSS SDK uses topK for limiting results (not pageSize like Platform)
const getAllOpts: Record<string, unknown> = { filters };
if (options.page_size != null) getAllOpts.topK = options.page_size;
const results = await this.memory.getAll(getAllOpts);
if (Array.isArray(results)) return results.map(normalizeMemoryItem);
if (results?.results && Array.isArray(results.results))
@@ -476,7 +483,8 @@ export function createProvider(
api: OpenClawPluginApi,
): Mem0Provider {
if (cfg.mode === "open-source") {
return new OSSProvider(cfg.oss, cfg.customPrompt, (p) =>
// v3.0.0: use customInstructions (was customPrompt)
return new OSSProvider(cfg.oss, cfg.customInstructions, (p) =>
api.resolvePath(p),
);
}
@@ -502,6 +510,7 @@ export function providerToBackend(
return {
async add(content, messages, opts = {}) {
const msgs = messages ?? (content ? [{ role: "user", content }] : []);
// v3.0.0: removed immutable, expiration_date
const result = await provider.add(
msgs as Array<{ role: string; content: string }>,
{
@@ -509,21 +518,18 @@ export function providerToBackend(
source: "OPENCLAW",
...(opts.runId && { run_id: opts.runId }),
...(opts.metadata && { metadata: opts.metadata }),
...(opts.immutable && { immutable: true }),
...(opts.infer === false && { infer: false }),
...(opts.expires && { expiration_date: opts.expires }),
},
);
return result as unknown as Record<string, unknown>;
},
async search(query, opts = {}) {
// v3.0.0: removed keyword_search, reranking
const results = await provider.search(query, {
user_id: opts.userId ?? userId,
top_k: opts.topK,
threshold: opts.threshold,
keyword_search: opts.keyword,
reranking: opts.rerank,
filters: opts.filters,
source: "OPENCLAW",
});
+159
View File
@@ -0,0 +1,159 @@
/**
* Public Artifacts Provider for OpenClaw memory-wiki bridge mode.
*
* Exposes Mem0 memories and dream state as artifacts that can be
* consumed by other plugins (e.g., memory-wiki in bridge mode).
*/
import type { Mem0Provider, MemoryItem, Mem0Config } from "./types.ts";
import type { MemoryArtifact } from "openclaw/plugin-sdk";
import { getDreamState } from "./dream-gate.ts";
export interface PublicArtifactsContext {
provider: Mem0Provider;
cfg: Mem0Config;
stateDir?: string;
effectiveUserId: (sessionKey?: string) => string;
}
/**
* Create a publicArtifacts provider that exposes Mem0 data to other plugins.
*/
export function createPublicArtifactsProvider(ctx: PublicArtifactsContext) {
return {
async listArtifacts(options?: {
userId?: string;
types?: string[];
limit?: number;
}): Promise<MemoryArtifact[]> {
const artifacts: MemoryArtifact[] = [];
const userId = options?.userId ?? ctx.effectiveUserId();
const types = options?.types ?? ["memory", "dream", "entity"];
const limit = options?.limit ?? 100;
try {
// Memory artifacts
if (types.includes("memory")) {
const memories = await ctx.provider.getAll({
user_id: userId,
page_size: limit,
});
for (const mem of memories) {
artifacts.push(memoryToArtifact(mem));
}
}
// Dream state artifact (if dream enabled and stateDir available)
if (types.includes("dream") && ctx.stateDir && ctx.cfg.skills?.dream?.enabled) {
const dreamArtifact = getDreamArtifact(ctx.stateDir, userId);
if (dreamArtifact) {
artifacts.push(dreamArtifact);
}
}
// Entity artifacts (grouped memories by category)
if (types.includes("entity")) {
const entityArtifacts = extractEntityArtifacts(artifacts.filter(a => a.type === "memory"));
artifacts.push(...entityArtifacts);
}
} catch (err) {
console.warn(
"[mem0] publicArtifacts.listArtifacts failed:",
err instanceof Error ? err.message : err,
);
}
return artifacts.slice(0, limit);
},
};
}
/**
* Convert a MemoryItem to a MemoryArtifact.
*/
function memoryToArtifact(mem: MemoryItem): MemoryArtifact {
return {
id: `mem0:memory:${mem.id}`,
type: "memory",
title: mem.memory.slice(0, 80) + (mem.memory.length > 80 ? "..." : ""),
content: mem.memory,
metadata: {
score: mem.score,
categories: mem.categories,
user_id: mem.user_id,
...mem.metadata,
},
createdAt: mem.created_at,
updatedAt: mem.updated_at,
};
}
/**
* Get dream consolidation state as an artifact.
*/
function getDreamArtifact(stateDir: string, userId: string): MemoryArtifact | null {
try {
const state = getDreamState(stateDir);
if (state.lastConsolidatedAt === 0) {
return null; // No consolidation has occurred yet
}
const lastDate = new Date(state.lastConsolidatedAt).toISOString();
return {
id: `mem0:dream:${userId}:state`,
type: "dream",
title: `Dream State (last: ${lastDate.split("T")[0]})`,
content: [
`Last consolidation: ${lastDate}`,
`Sessions since: ${state.sessionsSince}`,
`Last session: ${state.lastSessionId ?? "none"}`,
].join("\n"),
metadata: {
lastConsolidatedAt: state.lastConsolidatedAt,
sessionsSince: state.sessionsSince,
lastSessionId: state.lastSessionId,
user_id: userId,
},
updatedAt: lastDate,
};
} catch {
return null;
}
}
/**
* Extract entity artifacts from memories (grouped by category).
*/
function extractEntityArtifacts(memoryArtifacts: MemoryArtifact[]): MemoryArtifact[] {
const byCategory = new Map<string, MemoryArtifact[]>();
for (const artifact of memoryArtifacts) {
const categories = (artifact.metadata?.categories as string[]) ?? ["uncategorized"];
for (const cat of categories) {
const existing = byCategory.get(cat) ?? [];
existing.push(artifact);
byCategory.set(cat, existing);
}
}
const entities: MemoryArtifact[] = [];
for (const [category, mems] of byCategory) {
if (mems.length >= 2) {
entities.push({
id: `mem0:entity:${category}`,
type: "entity",
title: `${category.charAt(0).toUpperCase() + category.slice(1)} (${mems.length} memories)`,
content: mems.map(m => `- ${m.content}`).join("\n"),
metadata: {
category,
memoryCount: mems.length,
memoryIds: mems.map(m => m.id),
},
});
}
}
return entities;
}
+2 -7
View File
@@ -2,7 +2,7 @@
* Token-budgeted, category-ranked recall engine.
*
* Replaces naive "dump all search results" with:
* 1. Search with rerank + keyword_search
* 1. Search memories with threshold filtering
* 2. Rank by category priority (identity first)
* 3. Token-budget the results
* 4. Format by category with importance scores
@@ -253,18 +253,13 @@ export async function recall(
const categoryOrder = recallConfig.categoryOrder ?? DEFAULT_CATEGORY_ORDER;
const identityAlwaysInclude = recallConfig.identityAlwaysInclude !== false;
// Build search options with enhanced features
// Build search options (v3.0.0: keyword_search, reranking, filter_memories removed)
const searchOpts: SearchOptions = {
user_id: userId,
top_k: maxMemories * 2, // Over-fetch for ranking
threshold,
keyword_search: recallConfig.keywordSearch !== false, // Default on
reranking: recallConfig.rerank !== false, // Default on
source: "OPENCLAW",
};
if (recallConfig.filterMemories) {
searchOpts.filter_memories = true;
}
// Sanitize query: strip OpenClaw metadata prefix before searching
const cleanQuery = sanitizeQuery(query);
+6 -5
View File
@@ -654,9 +654,9 @@ describe("OSSProvider — history error handling", () => {
});
// ---------------------------------------------------------------------------
// 9. OSSProvider: customPrompt passthrough
// 9. OSSProvider: customInstructions passthrough (v3.0.0: renamed from customPrompt)
// ---------------------------------------------------------------------------
describe("OSSProvider — customPrompt passthrough", () => {
describe("OSSProvider — customInstructions passthrough", () => {
let capturedConfig: Record<string, unknown> | undefined;
beforeEach(() => {
@@ -682,12 +682,13 @@ describe("OSSProvider — customPrompt passthrough", () => {
vi.restoreAllMocks();
});
it("passes customPrompt to Memory config when provided", async () => {
// v3.0.0: customPrompt renamed to customInstructions
it("passes customInstructions to Memory config when provided", async () => {
const { createProvider } = await import("./index.ts");
const cfg = mem0ConfigSchema.parse({
mode: "open-source",
oss: { disableHistory: true },
customPrompt: "Extract only user preferences.",
customInstructions: "Extract only user preferences.",
});
const api = { resolvePath: (p: string) => p } as any;
const provider = createProvider(cfg, api);
@@ -695,6 +696,6 @@ describe("OSSProvider — customPrompt passthrough", () => {
await provider.search("test", { user_id: "u1" });
expect(capturedConfig).toBeDefined();
expect(capturedConfig!.customPrompt).toBe("Extract only user preferences.");
expect(capturedConfig!.customInstructions).toBe("Extract only user preferences.");
});
});
+4 -3
View File
@@ -9,9 +9,9 @@
*/
import { createHash, randomUUID } from "node:crypto";
import { readPluginAuth, writePluginAuth, getBaseUrl } from "./cli/config-file.ts";
import { readPluginAuth, writePluginAuth, getBaseUrl, clearAnonymousTelemetryId } from "./cli/config-file.ts";
export const PLUGIN_VERSION = "1.0.6";
export const PLUGIN_VERSION = "1.0.7";
const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX";
const POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/";
@@ -84,8 +84,9 @@ function maybeBuildIdentifyEvent(
$lib: "posthog-node",
},
};
// Clear the anonymous ID from config after aliasing (don't write empty string)
try {
writePluginAuth({ anonymousTelemetryId: "" });
clearAnonymousTelemetryId();
} catch {
/* ignore — alias may double-fire next session, harmless */
}
+7 -1
View File
@@ -113,9 +113,15 @@ describe("readPluginAuth", () => {
expect(auth.userId).toBe("user-snake");
});
it("returns empty object when JSON is invalid", () => {
it("throws error when JSON is invalid (prevents config destruction)", () => {
mockExists.mockReturnValue(true);
mockReadText.mockReturnValue("not valid json {{{");
expect(() => readPluginAuth()).toThrow(/Failed to parse[\s\S]*Fix the JSON syntax error/);
});
it("returns empty object when config file is empty", () => {
mockExists.mockReturnValue(true);
mockReadText.mockReturnValue(" ");
expect(readPluginAuth()).toEqual({});
});
});
+7 -5
View File
@@ -74,9 +74,10 @@ describe("mem0ConfigSchema.parse() — defaults", () => {
expect(cfg.customCategories).toBe(DEFAULT_CUSTOM_CATEGORIES);
});
it("customPrompt defaults to DEFAULT_CUSTOM_INSTRUCTIONS", () => {
const cfg = mem0ConfigSchema.parse({ apiKey: "test-key" });
expect(cfg.customPrompt).toBe(DEFAULT_CUSTOM_INSTRUCTIONS);
// v3.0.0: customPrompt removed, use customInstructions instead
it("customPrompt input falls back to customInstructions", () => {
const cfg = mem0ConfigSchema.parse({ apiKey: "test-key", customPrompt: "My prompt" });
expect(cfg.customInstructions).toBe("My prompt");
});
it("oss defaults to undefined", () => {
@@ -296,13 +297,14 @@ describe("mem0ConfigSchema.parse() — explicit overrides", () => {
expect(cfg.customInstructions).toBe(custom);
});
it("custom customPrompt overrides defaults", () => {
// v3.0.0: customPrompt renamed to customInstructions (backwards compat: customPrompt maps to customInstructions)
it("customPrompt input maps to customInstructions output", () => {
const custom = "My custom prompt";
const cfg = mem0ConfigSchema.parse({
apiKey: "k",
customPrompt: custom,
});
expect(cfg.customPrompt).toBe(custom);
expect(cfg.customInstructions).toBe(custom);
});
it("custom customCategories override defaults", () => {
+96
View File
@@ -0,0 +1,96 @@
import { describe, it, expect } from "vitest";
import {
isNoiseMessage,
isGenericAssistantMessage,
isSessionSpecificContent,
stripNoiseFromContent,
filterMessagesForExtraction,
} from "../filtering.ts";
describe("isNoiseMessage", () => {
it("returns true for heartbeat messages", () => {
expect(isNoiseMessage("HEARTBEAT_OK")).toBe(true);
expect(isNoiseMessage("NO_REPLY")).toBe(true);
});
it("returns true for single-word acknowledgments", () => {
expect(isNoiseMessage("ok")).toBe(true);
expect(isNoiseMessage("done")).toBe(true);
expect(isNoiseMessage("thanks")).toBe(true);
});
it("returns false for substantive content", () => {
expect(isNoiseMessage("My name is John and I live in Tokyo")).toBe(false);
});
});
describe("isGenericAssistantMessage", () => {
it("returns true for generic acknowledgments", () => {
expect(isGenericAssistantMessage("I see you've shared this. How can I help?")).toBe(true);
expect(isGenericAssistantMessage("Got it! How can I assist you?")).toBe(true);
});
it("returns false for substantive responses", () => {
expect(isGenericAssistantMessage("Based on the code, the bug is in line 42 where the null check is missing.")).toBe(false);
});
});
describe("isSessionSpecificContent", () => {
it("returns true for tool availability discussions", () => {
expect(isSessionSpecificContent(
"I do not currently see Mem0 write/update/delete tools exposed in this session."
)).toBe(true);
});
it("returns true for plugin capability statements", () => {
expect(isSessionSpecificContent(
"As of 2026-04-18, the openclaw-mem0 plugin does not expose a memory wiki capability."
)).toBe(true);
});
it("returns true for session-specific tool lists", () => {
expect(isSessionSpecificContent(
"The tools I have access to in this session are memory_search and memory_get."
)).toBe(true);
});
it("returns false for user preferences", () => {
expect(isSessionSpecificContent(
"My name is Kartik and I like to watch Pokemon."
)).toBe(false);
});
it("returns false for technical facts that span sessions", () => {
expect(isSessionSpecificContent(
"The project uses TypeScript 5.0 and Node.js 20."
)).toBe(false);
});
});
describe("filterMessagesForExtraction", () => {
it("filters out session-specific tool discussions", () => {
const messages = [
{ role: "user", content: "What tools do you have access to?" },
{ role: "assistant", content: "I have memory_search and memory_get tools. The other tools are not exposed in this session." },
{ role: "user", content: "My name is Kartik" },
{ role: "assistant", content: "Got it, I'll remember that!" },
];
const filtered = filterMessagesForExtraction(messages);
// Should keep the name statement, filter the tool discussion
expect(filtered.some(m => m.content.includes("Kartik"))).toBe(true);
expect(filtered.some(m => m.content.includes("not exposed in this session"))).toBe(false);
});
it("keeps valuable user facts", () => {
const messages = [
{ role: "user", content: "I prefer dark mode and use VS Code for development." },
{ role: "assistant", content: "Noted! Dark mode in VS Code is great for reducing eye strain." },
];
const filtered = filterMessagesForExtraction(messages);
expect(filtered.length).toBe(2);
expect(filtered[0].content).toContain("dark mode");
});
});
+3 -10
View File
@@ -42,6 +42,7 @@ beforeEach(() => {
// ---------------------------------------------------------------------------
describe("providerToBackend — search", () => {
// v3.0.0: keyword_search, reranking removed from SDK
it("delegates to provider.search with correct options", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
@@ -49,8 +50,6 @@ describe("providerToBackend — search", () => {
const results = await backend.search("hello world", {
topK: 10,
threshold: 0.5,
keyword: true,
rerank: true,
filters: { category: "preference" },
});
@@ -58,8 +57,6 @@ describe("providerToBackend — search", () => {
user_id: DEFAULT_USER,
top_k: 10,
threshold: 0.5,
keyword_search: true,
reranking: true,
filters: { category: "preference" },
source: "OPENCLAW",
});
@@ -73,12 +70,11 @@ describe("providerToBackend — search", () => {
await backend.search("query");
// v3.0.0: keyword_search, reranking removed
expect(provider.search).toHaveBeenCalledWith("query", {
user_id: DEFAULT_USER,
top_k: undefined,
threshold: undefined,
keyword_search: undefined,
reranking: undefined,
filters: undefined,
source: "OPENCLAW",
});
@@ -119,6 +115,7 @@ describe("providerToBackend — add", () => {
);
});
// v3.0.0: immutable, expiration_date removed from SDK
it("forwards optional add options", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
@@ -126,9 +123,7 @@ describe("providerToBackend — add", () => {
await backend.add("fact", undefined, {
runId: "run-1",
metadata: { source: "test" },
immutable: true,
infer: false,
expires: "2027-01-01",
});
expect(provider.add).toHaveBeenCalledWith(
@@ -137,9 +132,7 @@ describe("providerToBackend — add", () => {
user_id: DEFAULT_USER,
run_id: "run-1",
metadata: { source: "test" },
immutable: true,
infer: false,
expiration_date: "2027-01-01",
}),
);
});
+1 -1
View File
@@ -24,7 +24,7 @@ describe("telemetry", () => {
});
it("exports PLUGIN_VERSION", () => {
expect(PLUGIN_VERSION).toBe("1.0.6");
expect(PLUGIN_VERSION).toBe("1.0.7");
});
it("captureEvent does not throw", () => {
+3 -3
View File
@@ -37,7 +37,6 @@ function createMockToolDeps(overrides = {}): ToolDeps {
searchThreshold: 0.5,
customInstructions: "test",
customCategories: {},
customPrompt: "test",
} as any,
provider: {
search: vi
@@ -108,13 +107,14 @@ describe("registerAllTools", () => {
]);
});
it("registers tools without a second argument (required, not optional)", () => {
it("registers tools with optional: false metadata", () => {
const ctx = createMockToolDeps();
registerAllTools(ctx);
const calls = (ctx.api.registerTool as ReturnType<typeof vi.fn>).mock.calls;
for (const call of calls) {
expect(call).toHaveLength(1);
expect(call).toHaveLength(2);
expect(call[1]).toEqual({ optional: false });
}
});
});
+9 -8
View File
@@ -28,13 +28,14 @@ export interface ToolDeps {
export function registerAllTools(deps: ToolDeps): void {
const { api } = deps;
const nonOptional = { optional: false };
api.registerTool(createMemorySearchTool(deps));
api.registerTool(createMemoryAddTool(deps));
api.registerTool(createMemoryGetTool(deps));
api.registerTool(createMemoryListTool(deps));
api.registerTool(createMemoryUpdateTool(deps));
api.registerTool(createMemoryDeleteTool(deps));
api.registerTool(createMemoryEventListTool(deps));
api.registerTool(createMemoryEventStatusTool(deps));
api.registerTool(createMemorySearchTool(deps), nonOptional);
api.registerTool(createMemoryAddTool(deps), nonOptional);
api.registerTool(createMemoryGetTool(deps), nonOptional);
api.registerTool(createMemoryListTool(deps), nonOptional);
api.registerTool(createMemoryUpdateTool(deps), nonOptional);
api.registerTool(createMemoryDeleteTool(deps), nonOptional);
api.registerTool(createMemoryEventListTool(deps), nonOptional);
api.registerTool(createMemoryEventStatusTool(deps), nonOptional);
}
+14 -13
View File
@@ -1,11 +1,12 @@
import { Type } from "@sinclair/typebox";
import type { AddOptions } from "../types.ts";
import { isSubagentSession } from "../isolation.ts";
import { resolveCategories, ttlToExpirationDate } from "../skill-loader.ts";
import { isNoiseMessage, stripNoiseFromContent } from "../filtering.ts";
// v3.0.0: resolveCategories/ttlToExpirationDate removed - expiration_date/immutable no longer supported
import type { ToolDeps } from "./index.ts";
export function createMemoryAddTool(deps: ToolDeps) {
const { api, cfg, provider, resolveUserId, getCurrentSessionId, buildAddOptions, buildSearchOptions, skillsActive } = deps;
const { api, provider, resolveUserId, getCurrentSessionId, buildAddOptions, buildSearchOptions, skillsActive } = deps;
return {
name: "memory_add",
@@ -28,11 +29,20 @@ export function createMemoryAddTool(deps: ToolDeps) {
userId?: string; agentId?: string; metadata?: Record<string, unknown>; longTerm?: boolean;
};
const allFacts: string[] = p.facts?.length ? p.facts : (p.text ? [p.text] : []);
if (allFacts.length === 0) {
const rawFacts: string[] = p.facts?.length ? p.facts : (p.text ? [p.text] : []);
if (rawFacts.length === 0) {
return { content: [{ type: "text", text: "No facts provided. Pass 'text' or 'facts' array." }], details: { error: "missing_facts" } };
}
// Filter out noise and clean the facts before storing
const allFacts = rawFacts
.map((f) => stripNoiseFromContent(f))
.filter((f) => f.length > 0 && !isNoiseMessage(f));
if (allFacts.length === 0) {
return { content: [{ type: "text", text: "All provided facts were filtered as noise. Nothing stored." }], details: { error: "all_noise" } };
}
const start = Date.now();
try {
const currentSessionId = getCurrentSessionId();
@@ -53,21 +63,12 @@ export function createMemoryAddTool(deps: ToolDeps) {
...(category && { category }),
...(importance !== undefined && { importance }),
};
const categories = resolveCategories(cfg.skills);
const catConfig = category ? categories[category] : undefined;
const expirationDate = catConfig ? ttlToExpirationDate(catConfig.ttl) : undefined;
const isImmutable = catConfig?.immutable ?? false;
const addOpts: AddOptions = {
user_id: uid, source: "OPENCLAW", infer: false,
deduced_memories: allFacts, metadata: parsedMetadata ?? {},
...(expirationDate && { expiration_date: expirationDate }),
...(isImmutable && { immutable: true }),
};
if (runId) addOpts.run_id = runId;
if (cfg.mode === "platform") {
addOpts.output_format = "v1.1";
}
const result = await provider.add([{ role: "user", content: allFacts.join("\n") }], addOpts);
const count = result.results?.length ?? 0;
+2 -2
View File
@@ -16,7 +16,7 @@ export function createMemorySearchTool(deps: ToolDeps) {
agentId: Type.Optional(Type.String({ description: "Agent ID to search a specific agent's memories" })),
scope: Type.Optional(
Type.Union([Type.Literal("session"), Type.Literal("long-term"), Type.Literal("all")], {
description: 'Scope: "long-term" (default), "session", or "all"',
description: 'Scope: "all" (default), "session", or "long-term"',
}),
),
categories: Type.Optional(Type.Array(Type.String(), { description: "Filter by category" })),
@@ -25,7 +25,7 @@ export function createMemorySearchTool(deps: ToolDeps) {
async execute(_toolCallId: string, params: Record<string, unknown>) {
const {
query, limit, userId, agentId, scope = "long-term",
query, limit, userId, agentId, scope = "all",
categories: filterCategories, filters: agentFilters,
} = params as {
query: string; limit?: number; userId?: string; agentId?: string;
+2 -10
View File
@@ -12,8 +12,7 @@ export type Mem0Config = {
baseUrl?: string;
customInstructions: string;
customCategories: Record<string, string>;
// OSS-specific
customPrompt?: string;
// OSS-specific (customPrompt renamed to customInstructions in v3.0.0)
oss?: {
embedder?: { provider: string; config: Record<string, unknown> };
vectorStore?: { provider: string; config: Record<string, unknown> };
@@ -37,15 +36,12 @@ export interface AddOptions {
user_id: string;
run_id?: string;
custom_instructions?: string;
custom_categories?: Array<Record<string, string>>;
output_format?: string;
custom_categories?: Record<string, string>;
source?: string;
// Agentic harness additions
infer?: boolean;
deduced_memories?: string[];
metadata?: Record<string, unknown>;
expiration_date?: string;
immutable?: boolean;
}
export interface SearchOptions {
@@ -53,10 +49,6 @@ export interface SearchOptions {
run_id?: string;
top_k?: number;
threshold?: number;
limit?: number;
keyword_search?: boolean;
reranking?: boolean;
filter_memories?: boolean;
categories?: string[];
filters?: Record<string, unknown>;
source?: string;