Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c49e1aa0b | |||
| 4e611e8dba | |||
| 5520226b5b | |||
| 00695e3113 |
@@ -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:
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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:**
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
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>
|
||||
@@ -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**.
|
||||
|
||||

|
||||
|
||||
### 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.
|
||||
|
||||

|
||||
|
||||
### 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>
|
||||
|
||||

|
||||
|
||||
### 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
|
||||
|
||||

|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Memory Settings
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
## 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -92,7 +92,6 @@ config = {
|
||||
"provider": "openai",
|
||||
"config": {"model": "text-embedding-3-small"},
|
||||
},
|
||||
"version": "v1.1",
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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"}
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -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"}}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Vendored
+4
-1
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
node_modules/
|
||||
package-lock.json
|
||||
*.db
|
||||
.claude/
|
||||
.claude-*/
|
||||
+97
-21
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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[];
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
Vendored
+26
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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": {
|
||||
|
||||
Generated
+338
-118
@@ -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
@@ -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",
|
||||
});
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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.");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
@@ -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({});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
@@ -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",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user