Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a280b4a54 | |||
| 1ba9c71f54 | |||
| 5c6fbcaab0 | |||
| b339cab3c1 | |||
| a952df0953 | |||
| 6cebddebbe | |||
| b3d340f59c | |||
| 78e2efc0f2 | |||
| d21970efcc | |||
| 816039036d | |||
| faf1a34f70 | |||
| 6986153c90 | |||
| 8048e0b32f | |||
| af1cfd8139 | |||
| f5c3804f79 | |||
| 443816365a | |||
| 097959d5cc | |||
| bad6e12972 | |||
| d85fcda037 | |||
| dff91154a7 | |||
| c3f3f82a3e | |||
| 70af43c08c | |||
| 1786d907f7 | |||
| 12a268da30 | |||
| 0aefdf5251 | |||
| df72245b6b | |||
| fe872d0776 | |||
| 052d31939d | |||
| 1c44b675d9 | |||
| a1c9a63074 | |||
| 931df14e25 | |||
| 1b0d8bdd2e | |||
| 5c67a5e6bc | |||
| 267e5b13ea | |||
| a22287a3ba | |||
| da59412150 | |||
| c41719ff9a | |||
| f51b39db91 | |||
| 8d61d73d2f | |||
| 10acf78618 | |||
| caeae60dda | |||
| d7b8497b24 | |||
| a96e1d58f7 | |||
| 0d895b28ae | |||
| 84910b40da | |||
| 0e7c34f541 | |||
| 2b58775c17 | |||
| 326f33757b | |||
| c01221d4aa | |||
| 73d9ccac69 | |||
| 5bbd0d9ca9 | |||
| 641be2878d | |||
| eb7f5a774c | |||
| 6e9f8cf218 | |||
| 02a2b59555 | |||
| ec1d7a45d3 | |||
| 725a1aa114 | |||
| d41f19b9ce | |||
| a0fe9ca5b2 |
@@ -18,20 +18,17 @@ jobs:
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install Poetry
|
||||
- name: Install Hatch
|
||||
run: |
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
pip install hatch
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd mem0
|
||||
poetry install
|
||||
hatch env create
|
||||
|
||||
- name: Build a binary wheel and a source tarball
|
||||
run: |
|
||||
cd mem0
|
||||
poetry build
|
||||
hatch build --clean
|
||||
|
||||
# TODO: Needs to setup mem0 repo on Test PyPI
|
||||
# - name: Publish distribution 📦 to Test PyPI
|
||||
|
||||
+22
-19
@@ -44,21 +44,24 @@ jobs:
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install poetry
|
||||
uses: snok/install-poetry@v1
|
||||
with:
|
||||
version: 1.4.2
|
||||
virtualenvs-create: true
|
||||
virtualenvs-in-project: true
|
||||
- name: Install Hatch
|
||||
run: pip install hatch
|
||||
- name: Load cached venv
|
||||
id: cached-poetry-dependencies
|
||||
id: cached-hatch-dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: .venv
|
||||
key: venv-mem0-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
|
||||
key: venv-mem0-${{ runner.os }}-${{ hashFiles('**/pyproject.toml') }}
|
||||
- name: Install dependencies
|
||||
run: make install_all
|
||||
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
make install_all
|
||||
pip install -e ".[test]"
|
||||
pip install pinecone pinecone-text
|
||||
if: steps.cached-hatch-dependencies.outputs.cache-hit != 'true'
|
||||
- name: Run Formatting
|
||||
run: |
|
||||
mkdir -p .ruff_cache && chmod -R 777 .ruff_cache
|
||||
hatch run format
|
||||
- name: Run tests and generate coverage report
|
||||
run: make test
|
||||
|
||||
@@ -75,21 +78,21 @@ jobs:
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install poetry
|
||||
uses: snok/install-poetry@v1
|
||||
with:
|
||||
version: 1.4.2
|
||||
virtualenvs-create: true
|
||||
virtualenvs-in-project: true
|
||||
- name: Install Hatch
|
||||
run: pip install hatch
|
||||
- name: Load cached venv
|
||||
id: cached-poetry-dependencies
|
||||
id: cached-hatch-dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: .venv
|
||||
key: venv-embedchain-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
|
||||
key: venv-embedchain-${{ runner.os }}-${{ hashFiles('**/pyproject.toml') }}
|
||||
- name: Install dependencies
|
||||
run: cd embedchain && make install_all
|
||||
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
|
||||
if: steps.cached-hatch-dependencies.outputs.cache-hit != 'true'
|
||||
- name: Run Formatting
|
||||
run: |
|
||||
mkdir -p embedchain/.ruff_cache && chmod -R 777 embedchain/.ruff_cache
|
||||
cd embedchain && hatch run format
|
||||
- name: Lint with ruff
|
||||
run: cd embedchain && make lint
|
||||
- name: Run tests and generate coverage report
|
||||
|
||||
@@ -8,37 +8,36 @@ PROJECT_NAME := mem0ai
|
||||
all: format sort lint
|
||||
|
||||
install:
|
||||
poetry install
|
||||
hatch env create
|
||||
|
||||
install_all:
|
||||
poetry install
|
||||
poetry run pip install groq together boto3 litellm ollama chromadb weaviate weaviate-client sentence_transformers vertexai \
|
||||
pip install ruff==0.6.9 groq together boto3 litellm ollama chromadb weaviate weaviate-client sentence_transformers vertexai \
|
||||
google-generativeai elasticsearch opensearch-py vecs pinecone pinecone-text faiss-cpu langchain-community \
|
||||
upstash-vector azure-search-documents langchain-memgraph
|
||||
upstash-vector azure-search-documents langchain-memgraph langchain-neo4j rank-bm25
|
||||
|
||||
# Format code with ruff
|
||||
format:
|
||||
poetry run ruff format mem0/
|
||||
hatch run format
|
||||
|
||||
# Sort imports with isort
|
||||
sort:
|
||||
poetry run isort mem0/
|
||||
hatch run isort mem0/
|
||||
|
||||
# Lint code with ruff
|
||||
lint:
|
||||
poetry run ruff check mem0/
|
||||
hatch run lint
|
||||
|
||||
docs:
|
||||
cd docs && mintlify dev
|
||||
|
||||
build:
|
||||
poetry build
|
||||
hatch build
|
||||
|
||||
publish:
|
||||
poetry publish
|
||||
hatch publish
|
||||
|
||||
clean:
|
||||
poetry run rm -rf dist
|
||||
rm -rf dist
|
||||
|
||||
test:
|
||||
poetry run pytest tests
|
||||
hatch run test
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
<a href="https://mem0.dev/DiG">Join Discord</a>
|
||||
·
|
||||
<a href="https://mem0.dev/demo">Demo</a>
|
||||
·
|
||||
<a href="https://mem0.dev/openmemory">OpenMemory</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"import anthropic\n",
|
||||
"\n",
|
||||
"# Set up environment variables\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = \"your_openai_api_key\" # needed for embedding model\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = \"your_openai_api_key\" # needed for embedding model\n",
|
||||
"os.environ[\"ANTHROPIC_API_KEY\"] = \"your_anthropic_api_key\""
|
||||
]
|
||||
},
|
||||
@@ -33,7 +33,7 @@
|
||||
" \"model\": \"claude-3-5-sonnet-latest\",\n",
|
||||
" \"temperature\": 0.1,\n",
|
||||
" \"max_tokens\": 2000,\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" self.client = anthropic.Client(api_key=os.environ[\"ANTHROPIC_API_KEY\"])\n",
|
||||
@@ -50,11 +50,7 @@
|
||||
" - Keep track of open issues and follow-ups\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" def store_customer_interaction(self,\n",
|
||||
" user_id: str,\n",
|
||||
" message: str,\n",
|
||||
" response: str,\n",
|
||||
" metadata: Dict = None):\n",
|
||||
" def store_customer_interaction(self, user_id: str, message: str, response: str, metadata: Dict = None):\n",
|
||||
" \"\"\"Store customer interaction in memory.\"\"\"\n",
|
||||
" if metadata is None:\n",
|
||||
" metadata = {}\n",
|
||||
@@ -63,24 +59,17 @@
|
||||
" metadata[\"timestamp\"] = datetime.now().isoformat()\n",
|
||||
"\n",
|
||||
" # Format conversation for storage\n",
|
||||
" conversation = [\n",
|
||||
" {\"role\": \"user\", \"content\": message},\n",
|
||||
" {\"role\": \"assistant\", \"content\": response}\n",
|
||||
" ]\n",
|
||||
" conversation = [{\"role\": \"user\", \"content\": message}, {\"role\": \"assistant\", \"content\": response}]\n",
|
||||
"\n",
|
||||
" # Store in Mem0\n",
|
||||
" self.memory.add(\n",
|
||||
" conversation,\n",
|
||||
" user_id=user_id,\n",
|
||||
" metadata=metadata\n",
|
||||
" )\n",
|
||||
" self.memory.add(conversation, user_id=user_id, metadata=metadata)\n",
|
||||
"\n",
|
||||
" def get_relevant_history(self, user_id: str, query: str) -> List[Dict]:\n",
|
||||
" \"\"\"Retrieve relevant past interactions.\"\"\"\n",
|
||||
" return self.memory.search(\n",
|
||||
" query=query,\n",
|
||||
" user_id=user_id,\n",
|
||||
" limit=5 # Adjust based on needs\n",
|
||||
" limit=5, # Adjust based on needs\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" def handle_customer_query(self, user_id: str, query: str) -> str:\n",
|
||||
@@ -112,15 +101,12 @@
|
||||
" model=\"claude-3-5-sonnet-latest\",\n",
|
||||
" messages=[{\"role\": \"user\", \"content\": prompt}],\n",
|
||||
" max_tokens=2000,\n",
|
||||
" temperature=0.1\n",
|
||||
" temperature=0.1,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Store interaction\n",
|
||||
" self.store_customer_interaction(\n",
|
||||
" user_id=user_id,\n",
|
||||
" message=query,\n",
|
||||
" response=response,\n",
|
||||
" metadata={\"type\": \"support_query\"}\n",
|
||||
" user_id=user_id, message=query, response=response, metadata={\"type\": \"support_query\"}\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" return response.content[0].text"
|
||||
@@ -203,12 +189,12 @@
|
||||
" # Get user input\n",
|
||||
" query = input()\n",
|
||||
" print(\"Customer:\", query)\n",
|
||||
" \n",
|
||||
"\n",
|
||||
" # Check if user wants to exit\n",
|
||||
" if query.lower() == 'exit':\n",
|
||||
" if query.lower() == \"exit\":\n",
|
||||
" print(\"Thank you for using our support service. Goodbye!\")\n",
|
||||
" break\n",
|
||||
" \n",
|
||||
"\n",
|
||||
" # Handle the query and print the response\n",
|
||||
" response = chatbot.handle_customer_query(user_id, query)\n",
|
||||
" print(\"Support:\", response, \"\\n\\n\")"
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
# forked from autogen.agentchat.contrib.capabilities.teachability.Teachability
|
||||
|
||||
from typing import Dict, Optional, Union
|
||||
|
||||
from autogen.agentchat.assistant_agent import ConversableAgent
|
||||
from autogen.agentchat.contrib.capabilities.agent_capability import AgentCapability
|
||||
from autogen.agentchat.contrib.text_analyzer_agent import TextAnalyzerAgent
|
||||
from termcolor import colored
|
||||
|
||||
from mem0 import Memory
|
||||
|
||||
|
||||
|
||||
+272
-274
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ title: 'Get Memories (v2)'
|
||||
openapi: post /v2/memories/
|
||||
---
|
||||
|
||||
The v2 get memories API is powerful and flexible, allowing for more precise memory listing without the need for a search query. It supports complex logical operations (AND, OR) and comparison operators for advanced filtering capabilities. The comparison operators include:
|
||||
The v2 get memories API is powerful and flexible, allowing for more precise memory listing without the need for a search query. It supports complex logical operations (AND, OR, NOT) and comparison operators for advanced filtering capabilities. The comparison operators include:
|
||||
- `in`: Matches any of the values specified
|
||||
- `gte`: Greater than or equal to
|
||||
- `lte`: Less than or equal to
|
||||
|
||||
@@ -3,7 +3,7 @@ title: 'Search Memories (v2)'
|
||||
openapi: post /v2/memories/search/
|
||||
---
|
||||
|
||||
The v2 search API is powerful and flexible, allowing for more precise memory retrieval. It supports complex logical operations (AND, OR) and comparison operators for advanced filtering capabilities. The comparison operators include:
|
||||
The v2 search API is powerful and flexible, allowing for more precise memory retrieval. It supports complex logical operations (AND, OR, NOT) and comparison operators for advanced filtering capabilities. The comparison operators include:
|
||||
- `in`: Matches any of the values specified
|
||||
- `gte`: Greater than or equal to
|
||||
- `lte`: Less than or equal to
|
||||
@@ -18,7 +18,7 @@ The v2 search API is powerful and flexible, allowing for more precise memory ret
|
||||
query="What are Alice's hobbies?",
|
||||
version="v2",
|
||||
filters={
|
||||
"AND": [
|
||||
"OR": [
|
||||
{
|
||||
"user_id": "alice"
|
||||
},
|
||||
|
||||
@@ -8,6 +8,85 @@ mode: "wide"
|
||||
<Tabs>
|
||||
<Tab title="Python">
|
||||
|
||||
<Update label="2025-05-10" description="v0.1.100">
|
||||
|
||||
**New Features:**
|
||||
- **Memory:** Added Group Chat Memory Feature support
|
||||
- **Examples:** Added Healthcare assistant using Mem0 and Google ADK
|
||||
|
||||
**Bug Fixes:**
|
||||
- **SSE:** Fixed SSE connection issues
|
||||
- **MCP:** Fixed memories not appearing in MCP clients added from Dashboard
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-07" description="v0.1.99">
|
||||
|
||||
**New Features:**
|
||||
- **OpenMemory:** Added OpenMemory support
|
||||
- **Neo4j:** Added weights to Neo4j model
|
||||
- **AWS:** Added support for Opsearch Serverless
|
||||
- **Examples:** Added ElizaOS Example
|
||||
|
||||
**Improvements:**
|
||||
- **Documentation:** Updated Azure AI documentation
|
||||
- **AI SDK:** Added missing parameters and updated demo application
|
||||
- **OSS:** Fixed AOSS and AWS BedRock LLM
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-30" description="v0.1.98">
|
||||
|
||||
**New Features:**
|
||||
- **Neo4j:** Added support for Neo4j database
|
||||
- **AWS:** Added support for AWS Bedrock Embeddings
|
||||
|
||||
**Improvements:**
|
||||
- **Client:** Updated delete_users() to use V2 API endpoints
|
||||
- **Documentation:** Updated timestamp and dual-identity memory management docs
|
||||
- **Neo4j:** Improved Neo4j queries and removed warnings
|
||||
- **AI SDK:** Added support for graceful failure when services are down
|
||||
|
||||
**Bug Fixes:**
|
||||
- Fixed AI SDK filters
|
||||
- Fixed new memories wrong type
|
||||
- Fixed duplicated metadata issue while adding/updating memories
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-23" description="v0.1.97">
|
||||
|
||||
**New Features:**
|
||||
- **HuggingFace:** Added support for HF Inference
|
||||
|
||||
**Bug Fixes:**
|
||||
- Fixed proxy for Mem0
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-16" description="v0.1.96">
|
||||
|
||||
**New Features:**
|
||||
- **Vercel AI SDK:** Added Graph Memory support
|
||||
|
||||
**Improvements:**
|
||||
- **Documentation:** Fixed timestamp and README links
|
||||
- **Client:** Updated TS client to use proper types for deleteUsers
|
||||
- **Dependencies:** Removed unnecessary dependencies from base package
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-09" description="v0.1.95">
|
||||
|
||||
**Improvements:**
|
||||
- **Client:** Fixed Ping Method for using default org_id and project_id
|
||||
- **Documentation:** Updated documentation
|
||||
|
||||
**Bug Fixes:**
|
||||
- Fixed mem0-migrations issue
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-26" description="v0.1.94">
|
||||
|
||||
**New Features:**
|
||||
@@ -209,6 +288,28 @@ mode: "wide"
|
||||
|
||||
<Tab title="TypeScript">
|
||||
|
||||
<Update label="2025-05-23" description="v2.1.26">
|
||||
**Improvements:**
|
||||
- **Client:** Removed type `string` from `messages` interface
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-08" description="v2.1.25">
|
||||
**Improvements:**
|
||||
- **Client:** Improved error handling in client.
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-06" description="v2.1.24">
|
||||
**New Features:**
|
||||
- **Client:** Added new param `output_format` to match Python SDK.
|
||||
- **Client:** Added new enum `OutputFormat` for `v1.0` and `v1.1`
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-05" description="v2.1.23">
|
||||
**New Features:**
|
||||
- **Client:** Updated `deleteUsers` to use `v2` API.
|
||||
- **Client:** Deprecated `deleteUser` and added deprecation warning.
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-02" description="v2.1.22">
|
||||
**New Features:**
|
||||
- **Client:** Updated `deleteUser` to use `entity_id` and `entity_type`
|
||||
@@ -313,6 +414,104 @@ mode: "wide"
|
||||
|
||||
<Tab title="Platform">
|
||||
|
||||
<Update label="2025-05-19" description="">
|
||||
|
||||
**Bug Fixes:**
|
||||
- **Core:** Fixed unicode error in user_id, agent_id, run_id and app_id
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-17" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Graph:** Added Neo4J Graph Migration
|
||||
- **API:** Added API to set custom instructions
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-16" description="">
|
||||
|
||||
**New Features:**
|
||||
- **API:** Added Org-wide API Limit and Usage
|
||||
|
||||
**Improvements:**
|
||||
- **Database:** Added migration for "is_deleted" column
|
||||
- **Graph:** Improved graph queries
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-15" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Lambda:** Added actions to lambda
|
||||
- **Core:** Added background runs support
|
||||
- **Models:** Added o4-mini for pro users
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-10" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Integrations:** Added Intercom Events integration
|
||||
- **Billing:** Added prefilled email for payments
|
||||
- **Organizations:** Added Pro organization marking
|
||||
|
||||
**Improvements:**
|
||||
- **UI:** Fixed loading jitter for organization selection
|
||||
- **Infrastructure:** Improved production scaling
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-09" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Memory:** Fixed filters in Memory Page
|
||||
- **Deployment:** Added custom categories for on-premise
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-08" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Backend:** Updated Django settings for metrics
|
||||
- **Memory:** Added retries to memory filtering
|
||||
- **Search:** Added scoring mechanism
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-07" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Deployment:** Updated deployment scripts
|
||||
- **Testing:** Added code coverage tracking
|
||||
- **Memory:** Added background cron job for memory quality
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-06" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Models:** Added support for 4.1-mini model
|
||||
|
||||
**Improvements:**
|
||||
- **Infrastructure:** Increased instance count
|
||||
- **API:** Added V2 for Manage Entities
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-04" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Testing:** Added code coverage tracking
|
||||
- **AI:** Added Keywords AI integration
|
||||
|
||||
**Improvements:**
|
||||
- **UI:** Updated UI with tabs
|
||||
- **Database:** Added migrations for custom instructions
|
||||
- **Search:** Added criteria filtering
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-26" description="">
|
||||
|
||||
**Improvements:**
|
||||
@@ -470,6 +669,21 @@ mode: "wide"
|
||||
|
||||
<Tab title="Vercel AI SDK">
|
||||
|
||||
<Update label="2025-05-23" description="v1.0.5">
|
||||
**New Features:**
|
||||
- **Vercel AI SDK:** Added support for Google provider.
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-10" description="v1.0.4">
|
||||
**New Features:**
|
||||
- **Vercel AI SDK:** Added support for new param `output_format`.
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-08" description="v1.0.3">
|
||||
**Improvements:**
|
||||
- **Vercel AI SDK:** Added support for graceful failure in cases services are down.
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-01" description="v1.0.1">
|
||||
**New Features:**
|
||||
- **Vercel AI SDK:** Added support for graph memories
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: AWS Bedrock
|
||||
---
|
||||
|
||||
To use AWS Bedrock embedding models, you need to have the appropriate AWS credentials and permissions. The embeddings implementation relies on the `boto3` library.
|
||||
|
||||
### Setup
|
||||
- Ensure you have model access from the [AWS Bedrock Console](https://us-east-1.console.aws.amazon.com/bedrock/home?region=us-east-1#/modelaccess)
|
||||
- Authenticate the boto3 client using a method described in the [AWS documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html)
|
||||
- Set up environment variables for authentication:
|
||||
```bash
|
||||
export AWS_REGION=us-east-1
|
||||
export AWS_ACCESS_KEY_ID=your-access-key
|
||||
export AWS_SECRET_ACCESS_KEY=your-secret-key
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
# For LLM if needed
|
||||
os.environ["OPENAI_API_KEY"] = "your-openai-api-key"
|
||||
|
||||
# AWS credentials
|
||||
os.environ["AWS_REGION"] = "us-west-2"
|
||||
os.environ["AWS_ACCESS_KEY_ID"] = "your-access-key"
|
||||
os.environ["AWS_SECRET_ACCESS_KEY"] = "your-secret-key"
|
||||
|
||||
config = {
|
||||
"embedder": {
|
||||
"provider": "aws_bedrock",
|
||||
"config": {
|
||||
"model": "amazon.titan-embed-text-v2:0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m = Memory.from_config(config)
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
|
||||
{"role": "assistant", "content": "How about a thriller movies? They can be quite engaging."},
|
||||
{"role": "user", "content": "I'm not a big fan of thriller movies but I love sci-fi movies."},
|
||||
{"role": "assistant", "content": "Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future."}
|
||||
]
|
||||
m.add(messages, user_id="alice")
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Config
|
||||
|
||||
Here are the parameters available for configuring AWS Bedrock embedder:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Python">
|
||||
| Parameter | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `model` | The name of the embedding model to use | `amazon.titan-embed-text-v1` |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -26,6 +26,7 @@ See the list of supported embedders below.
|
||||
<Card title="Together" href="/components/embedders/models/together"></Card>
|
||||
<Card title="LM Studio" href="/components/embedders/models/lmstudio"></Card>
|
||||
<Card title="Langchain" href="/components/embedders/models/langchain"></Card>
|
||||
<Card title="AWS Bedrock" href="/components/embedders/models/aws_bedrock"></Card>
|
||||
</CardGroup>
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -110,6 +110,12 @@ Here's a comprehensive list of all parameters that can be used across different
|
||||
| `azure_kwargs` | Azure LLM args for initialization | AzureOpenAI |
|
||||
| `deepseek_base_url` | Base URL for DeepSeek API | DeepSeek |
|
||||
| `xai_base_url` | Base URL for XAI API | XAI |
|
||||
| `sarvam_base_url` | Base URL for Sarvam API | Sarvam |
|
||||
| `reasoning_effort` | Reasoning level (low, medium, high) | Sarvam |
|
||||
| `frequency_penalty` | Penalize frequent tokens (-2.0 to 2.0) | Sarvam |
|
||||
| `presence_penalty` | Penalize existing tokens (-2.0 to 2.0) | Sarvam |
|
||||
| `seed` | Seed for deterministic sampling | Sarvam |
|
||||
| `stop` | Stop sequences (max 4) | Sarvam |
|
||||
| `lmstudio_base_url` | Base URL for LM Studio API | LM Studio |
|
||||
</Tab>
|
||||
<Tab title="TypeScript">
|
||||
|
||||
@@ -20,7 +20,7 @@ config = {
|
||||
"llm": {
|
||||
"provider": "anthropic",
|
||||
"config": {
|
||||
"model": "claude-3-7-sonnet-latest",
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"temperature": 0.1,
|
||||
"max_tokens": 2000,
|
||||
}
|
||||
@@ -45,7 +45,7 @@ const config = {
|
||||
provider: 'anthropic',
|
||||
config: {
|
||||
apiKey: process.env.ANTHROPIC_API_KEY || '',
|
||||
model: 'claude-3-7-sonnet-latest',
|
||||
model: 'claude-sonnet-4-20250514',
|
||||
temperature: 0.1,
|
||||
maxTokens: 2000,
|
||||
},
|
||||
|
||||
@@ -15,16 +15,15 @@ title: AWS Bedrock
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "your-api-key" # used for embedding model
|
||||
os.environ['AWS_REGION'] = 'us-east-1'
|
||||
os.environ["AWS_ACCESS_KEY"] = "xx"
|
||||
os.environ['AWS_REGION'] = 'us-west-2'
|
||||
os.environ["AWS_ACCESS_KEY_ID"] = "xx"
|
||||
os.environ["AWS_SECRET_ACCESS_KEY"] = "xx"
|
||||
|
||||
config = {
|
||||
"llm": {
|
||||
"provider": "aws_bedrock",
|
||||
"config": {
|
||||
"model": "arn:aws:bedrock:us-east-1:123456789012:model/your-model-name",
|
||||
"model": "anthropic.claude-3-5-haiku-20241022-v1:0",
|
||||
"temperature": 0.2,
|
||||
"max_tokens": 2000,
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ To use Azure OpenAI models, you have to set the `LLM_AZURE_OPENAI_API_KEY`, `LLM
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "your-api-key" # used for embedding model
|
||||
|
||||
os.environ["LLM_AZURE_OPENAI_API_KEY"] = "your-api-key"
|
||||
os.environ["LLM_AZURE_DEPLOYMENT"] = "your-deployment-name"
|
||||
os.environ["LLM_AZURE_ENDPOINT"] = "your-api-base-url"
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: Sarvam AI
|
||||
---
|
||||
|
||||
<Snippet file="paper-release.mdx" />
|
||||
|
||||
**Sarvam AI** is an Indian AI company developing language models with a focus on Indian languages and cultural context. Their latest model **Sarvam-M** is designed to understand and generate content in multiple Indian languages while maintaining high performance in English.
|
||||
|
||||
To use Sarvam AI's models, please set the `SARVAM_API_KEY` which you can get from their [platform](https://dashboard.sarvam.ai/).
|
||||
|
||||
## Usage
|
||||
|
||||
```python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "your-api-key" # used for embedding model
|
||||
os.environ["SARVAM_API_KEY"] = "your-api-key"
|
||||
|
||||
config = {
|
||||
"llm": {
|
||||
"provider": "sarvam",
|
||||
"config": {
|
||||
"model": "sarvam-m",
|
||||
"temperature": 0.7,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m = Memory.from_config(config)
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
|
||||
{"role": "assistant", "content": "How about a thriller movies? They can be quite engaging."},
|
||||
{"role": "user", "content": "I'm not a big fan of thriller movies but I love sci-fi movies."},
|
||||
{"role": "assistant", "content": "Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future."}
|
||||
]
|
||||
m.add(messages, user_id="alex")
|
||||
```
|
||||
|
||||
## Advanced Usage with Sarvam-Specific Features
|
||||
|
||||
```python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
config = {
|
||||
"llm": {
|
||||
"provider": "sarvam",
|
||||
"config": {
|
||||
"model": {
|
||||
"name": "sarvam-m",
|
||||
"reasoning_effort": "high", # Enable advanced reasoning
|
||||
"frequency_penalty": 0.1, # Reduce repetition
|
||||
"seed": 42 # For deterministic outputs
|
||||
},
|
||||
"temperature": 0.3,
|
||||
"max_tokens": 2000,
|
||||
"api_key": "your-sarvam-api-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m = Memory.from_config(config)
|
||||
|
||||
# Example with Hindi conversation
|
||||
messages = [
|
||||
{"role": "user", "content": "मैं SBI में joint account खोलना चाहता हूँ।"},
|
||||
{"role": "assistant", "content": "SBI में joint account खोलने के लिए आपको कुछ documents की जरूरत होगी। क्या आप जानना चाहते हैं कि कौन से documents चाहिए?"}
|
||||
]
|
||||
m.add(messages, user_id="rajesh", metadata={"language": "hindi", "topic": "banking"})
|
||||
```
|
||||
|
||||
## Config
|
||||
|
||||
All available parameters for the `sarvam` config are present in [Master List of All Params in Config](../config).
|
||||
@@ -34,6 +34,7 @@ To view all supported llms, visit the [Supported LLMs](./models).
|
||||
<Card title="Gemini" href="/components/llms/models/gemini" />
|
||||
<Card title="DeepSeek" href="/components/llms/models/deepseek" />
|
||||
<Card title="xAI" href="/components/llms/models/xAI" />
|
||||
<Card title="XAI" href="/components/llms/models/sarvam" />
|
||||
<Card title="LM Studio" href="/components/llms/models/lmstudio" />
|
||||
<Card title="Langchain" href="/components/llms/models/langchain" />
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,59 +1,75 @@
|
||||
[OpenSearch](https://opensearch.org/) is an open-source, enterprise-grade search and observability suite that brings order to unstructured data at scale. OpenSearch supports k-NN (k-Nearest Neighbors) and allows you to store and retrieve high-dimensional vector embeddings efficiently.
|
||||
[OpenSearch](https://opensearch.org/) is an enterprise-grade search and observability suite that brings order to unstructured data at scale. OpenSearch supports k-NN (k-Nearest Neighbors) and allows you to store and retrieve high-dimensional vector embeddings efficiently.
|
||||
|
||||
### Installation
|
||||
|
||||
OpenSearch support requires additional dependencies. Install them with:
|
||||
|
||||
```bash
|
||||
pip install opensearch>=2.8.0
|
||||
pip install opensearch-py
|
||||
```
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before using OpenSearch with Mem0, you need to set up a collection in AWS OpenSearch Service.
|
||||
|
||||
#### AWS OpenSearch Service
|
||||
You can create a collection through the AWS Console:
|
||||
- Navigate to [OpenSearch Service Console](https://console.aws.amazon.com/aos/home)
|
||||
- Click "Create collection"
|
||||
- Select "Serverless collection" and then enable "Vector search" capabilities
|
||||
- Once created, note the endpoint URL (host) for your configuration
|
||||
|
||||
|
||||
### Usage
|
||||
|
||||
```python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
import boto3
|
||||
from opensearchpy import OpenSearch, RequestsHttpConnection, AWSV4SignerAuth
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "sk-xx"
|
||||
# For AWS OpenSearch Service with IAM authentication
|
||||
region = 'us-west-2'
|
||||
service = 'aoss'
|
||||
credentials = boto3.Session().get_credentials()
|
||||
auth = AWSV4SignerAuth(credentials, region, service)
|
||||
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "opensearch",
|
||||
"config": {
|
||||
"collection_name": "mem0",
|
||||
"host": "localhost",
|
||||
"port": 9200,
|
||||
"embedding_model_dims": 1536
|
||||
"host": "your-domain.us-west-2.aoss.amazonaws.com",
|
||||
"port": 443,
|
||||
"http_auth": auth,
|
||||
"embedding_model_dims": 1024,
|
||||
"connection_class": RequestsHttpConnection,
|
||||
"pool_maxsize": 20,
|
||||
"use_ssl": True,
|
||||
"verify_certs": True
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Add Memories
|
||||
|
||||
```python
|
||||
m = Memory.from_config(config)
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
|
||||
{"role": "assistant", "content": "How about a thriller movies? They can be quite engaging."},
|
||||
{"role": "user", "content": "I’m not a big fan of thriller movies but I love sci-fi movies."},
|
||||
{"role": "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."}
|
||||
]
|
||||
m.add(messages, user_id="alice", metadata={"category": "movies"})
|
||||
```
|
||||
|
||||
### Config
|
||||
### Search Memories
|
||||
|
||||
Let's see the available parameters for the `opensearch` config:
|
||||
|
||||
| Parameter | Description | Default Value |
|
||||
| ---------------------- | -------------------------------------------------- | ------------- |
|
||||
| `collection_name` | The name of the index to store the vectors | `mem0` |
|
||||
| `embedding_model_dims` | Dimensions of the embedding model | `1536` |
|
||||
| `host` | The host where the OpenSearch server is running | `localhost` |
|
||||
| `port` | The port where the OpenSearch server is running | `9200` |
|
||||
| `api_key` | API key for authentication | `None` |
|
||||
| `user` | Username for basic authentication | `None` |
|
||||
| `password` | Password for basic authentication | `None` |
|
||||
| `verify_certs` | Whether to verify SSL certificates | `False` |
|
||||
| `auto_create_index` | Whether to automatically create the index | `True` |
|
||||
| `use_ssl` | Whether to use SSL for connection | `False` |
|
||||
```python
|
||||
results = m.search("What kind of movies does Alice like?", user_id="alice")
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ For detailed guidance on pull requests, refer to [GitHub's documentation](https:
|
||||
|
||||
## 📦 Dependency Management
|
||||
|
||||
We use `poetry` as our package manager. Install it by following the [official instructions](https://python-poetry.org/docs/#installation).
|
||||
We use `hatch` as our package manager. Install it by following the [official instructions](https://hatch.pypa.io/latest/install/).
|
||||
|
||||
⚠️ **Do NOT use `pip` or `conda` for dependency management.** Instead, run:
|
||||
|
||||
@@ -37,7 +37,7 @@ We use `poetry` as our package manager. Install it by following the [official in
|
||||
make install_all
|
||||
|
||||
# Activate virtual environment
|
||||
poetry shell
|
||||
hatch shell
|
||||
```
|
||||
|
||||
---
|
||||
@@ -60,9 +60,9 @@ Run the linter and fix any reported issues before submitting your PR:
|
||||
make lint
|
||||
```
|
||||
|
||||
### 🎨 Code Formatting with `black`
|
||||
### 🎨 Code Formatting
|
||||
|
||||
To maintain a consistent code style, format your code using `black`:
|
||||
To maintain a consistent code style, format your code:
|
||||
|
||||
```bash
|
||||
make format
|
||||
@@ -76,7 +76,7 @@ Run tests to verify functionality before submitting your PR:
|
||||
make test
|
||||
```
|
||||
|
||||
💡 **Note:** Some dependencies have been removed from Poetry to reduce package size. Run `make install_all` to install necessary dependencies before running tests.
|
||||
💡 **Note:** Some dependencies have been removed from the main dependencies to reduce package size. Run `make install_all` to install necessary dependencies before running tests.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+38
-21
@@ -45,21 +45,22 @@
|
||||
"group": "Features",
|
||||
"icon": "star",
|
||||
"pages": [
|
||||
"features/platform-overview",
|
||||
"features/advanced-retrieval",
|
||||
"features/contextual-add",
|
||||
"features/multimodal-support",
|
||||
"features/timestamp",
|
||||
"features/selective-memory",
|
||||
"features/custom-categories",
|
||||
"features/custom-instructions",
|
||||
"features/direct-import",
|
||||
"features/async-client",
|
||||
"features/memory-export",
|
||||
"features/webhooks",
|
||||
"features/graph-memory",
|
||||
"features/feedback-mechanism",
|
||||
"features/expiration-date"
|
||||
"platform/features/platform-overview",
|
||||
"platform/features/advanced-retrieval",
|
||||
"platform/features/criteria-retrieval",
|
||||
"platform/features/contextual-add",
|
||||
"platform/features/multimodal-support",
|
||||
"platform/features/timestamp",
|
||||
"platform/features/selective-memory",
|
||||
"platform/features/custom-categories",
|
||||
"platform/features/custom-instructions",
|
||||
"platform/features/direct-import",
|
||||
"platform/features/async-client",
|
||||
"platform/features/memory-export",
|
||||
"platform/features/webhooks",
|
||||
"platform/features/graph-memory",
|
||||
"platform/features/feedback-mechanism",
|
||||
"platform/features/expiration-date"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -76,10 +77,10 @@
|
||||
"icon": "wrench",
|
||||
"pages": [
|
||||
"open-source/features/async-memory",
|
||||
"features/openai_compatibility",
|
||||
"features/custom-fact-extraction-prompt",
|
||||
"features/custom-update-memory-prompt",
|
||||
"open-source/multimodal-support",
|
||||
"open-source/features/openai_compatibility",
|
||||
"open-source/features/custom-fact-extraction-prompt",
|
||||
"open-source/features/custom-update-memory-prompt",
|
||||
"open-source/features/multimodal-support",
|
||||
"open-source/features/rest-api"
|
||||
]
|
||||
},
|
||||
@@ -114,6 +115,7 @@
|
||||
"components/llms/models/gemini",
|
||||
"components/llms/models/deepseek",
|
||||
"components/llms/models/xAI",
|
||||
"components/llms/models/sarvam",
|
||||
"components/llms/models/lmstudio",
|
||||
"components/llms/models/langchain"
|
||||
]
|
||||
@@ -166,7 +168,8 @@
|
||||
"components/embedders/models/gemini",
|
||||
"components/embedders/models/lmstudio",
|
||||
"components/embedders/models/together",
|
||||
"components/embedders/models/langchain"
|
||||
"components/embedders/models/langchain",
|
||||
"components/embedders/models/aws_bedrock"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -183,6 +186,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "OpenMemory",
|
||||
"icon": "square-terminal",
|
||||
"pages": [
|
||||
"openmemory/overview",
|
||||
"openmemory/quickstart"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Examples",
|
||||
"groups": [
|
||||
@@ -191,8 +202,11 @@
|
||||
"icon": "lightbulb",
|
||||
"pages": [
|
||||
"examples",
|
||||
"examples/aws_example",
|
||||
"examples/mem0-demo",
|
||||
"examples/ai_companion_js",
|
||||
"examples/collaborative-task-agent",
|
||||
"examples/eliza_os",
|
||||
"examples/mem0-mastra",
|
||||
"examples/mem0-with-ollama",
|
||||
"examples/personal-ai-tutor",
|
||||
@@ -206,6 +220,7 @@
|
||||
"examples/mem0-agentic-tool",
|
||||
"examples/openai-inbuilt-tools",
|
||||
"examples/mem0-openai-voice-demo",
|
||||
"examples/mem0-google-adk-healthcare-assistant",
|
||||
"examples/email_processing",
|
||||
"examples/youtube-assistant"
|
||||
]
|
||||
@@ -234,7 +249,9 @@
|
||||
"integrations/elevenlabs",
|
||||
"integrations/pipecat",
|
||||
"integrations/agno",
|
||||
"integrations/keywords"
|
||||
"integrations/keywords",
|
||||
"integrations/raycast",
|
||||
"integrations/mastra"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
+5
-1
@@ -75,7 +75,11 @@ Explore how **Mem0** can power real-world applications and bring personalized, i
|
||||
|
||||
<Card title="Mem0 OpenAI Voice Demo" icon="microphone" href="/examples/mem0-openai-voice-demo">
|
||||
Use Mem0's memory capabilities with OpenAI's Inbuilt Tools to create AI agents with persistent memory.
|
||||
</Card>
|
||||
</Card>
|
||||
|
||||
<Card title="Healthcare Assistant Google ADK" icon="microphone" href="/examples/mem0-google-adk-healthcare-assistant">
|
||||
Build a personalized healthcare assistant with persistent memory using Google's ADK and Mem0.
|
||||
</Card>
|
||||
|
||||
<Card title="Email Processing" icon="envelope" href="/examples/email_processing">
|
||||
Use Mem0's memory capabilities to process emails and create AI agents with persistent memory.
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
---
|
||||
title: AWS Bedrock and AOSS
|
||||
---
|
||||
|
||||
<Snippet file="paper-release.mdx" />
|
||||
|
||||
This example demonstrates how to configure and use the `mem0ai` SDK with **AWS Bedrock** and **OpenSearch Service (AOSS)** for persistent memory capabilities in Python.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the required dependencies:
|
||||
|
||||
```bash
|
||||
pip install mem0ai boto3 opensearch-py
|
||||
```
|
||||
|
||||
## Environment Setup
|
||||
|
||||
Set your AWS environment variables:
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
# Set these in your environment or notebook
|
||||
os.environ['AWS_REGION'] = 'us-west-2'
|
||||
os.environ['AWS_ACCESS_KEY_ID'] = 'AK00000000000000000'
|
||||
os.environ['AWS_SECRET_ACCESS_KEY'] = 'AS00000000000000000'
|
||||
|
||||
# Confirm they are set
|
||||
print(os.environ['AWS_REGION'])
|
||||
print(os.environ['AWS_ACCESS_KEY_ID'])
|
||||
print(os.environ['AWS_SECRET_ACCESS_KEY'])
|
||||
```
|
||||
|
||||
## Configuration and Usage
|
||||
|
||||
This sets up Mem0 with AWS Bedrock for embeddings and LLM, and OpenSearch as the vector store.
|
||||
|
||||
```python
|
||||
import boto3
|
||||
from opensearchpy import OpenSearch, RequestsHttpConnection, AWSV4SignerAuth
|
||||
from mem0.memory.main import Memory
|
||||
|
||||
region = 'us-west-2'
|
||||
service = 'aoss'
|
||||
credentials = boto3.Session().get_credentials()
|
||||
auth = AWSV4SignerAuth(credentials, region, service)
|
||||
|
||||
config = {
|
||||
"embedder": {
|
||||
"provider": "aws_bedrock",
|
||||
"config": {
|
||||
"model": "amazon.titan-embed-text-v2:0"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
"provider": "aws_bedrock",
|
||||
"config": {
|
||||
"model": "anthropic.claude-3-5-haiku-20241022-v1:0",
|
||||
"temperature": 0.1,
|
||||
"max_tokens": 2000
|
||||
}
|
||||
},
|
||||
"vector_store": {
|
||||
"provider": "opensearch",
|
||||
"config": {
|
||||
"collection_name": "mem0",
|
||||
"host": "your-opensearch-domain.us-west-2.es.amazonaws.com",
|
||||
"port": 443,
|
||||
"http_auth": auth,
|
||||
"embedding_model_dims": 1024,
|
||||
"connection_class": RequestsHttpConnection,
|
||||
"pool_maxsize": 20,
|
||||
"use_ssl": True,
|
||||
"verify_certs": True
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Initialize memory system
|
||||
m = Memory.from_config(config)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
#### Add a memory:
|
||||
|
||||
```python
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
|
||||
{"role": "assistant", "content": "How about a thriller movies? They can be quite engaging."},
|
||||
{"role": "user", "content": "I'm not a big fan of thriller movies but I love sci-fi movies."},
|
||||
{"role": "assistant", "content": "Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future."}
|
||||
]
|
||||
|
||||
# Store inferred memories (default behavior)
|
||||
result = m.add(messages, user_id="alice", metadata={"category": "movie_recommendations"})
|
||||
```
|
||||
|
||||
#### Search a memory:
|
||||
```python
|
||||
relevant_memories = m.search(query, user_id="alice")
|
||||
```
|
||||
|
||||
#### Get all memories:
|
||||
```python
|
||||
all_memories = m.get_all(user_id="alice")
|
||||
```
|
||||
|
||||
#### Get a specific memory:
|
||||
```python
|
||||
memory = m.get(memory_id)
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
With Mem0 and AWS services like Bedrock and OpenSearch, you can build intelligent AI companions that remember, adapt, and personalize their responses over time. This makes them ideal for long-term assistants, tutors, or support bots with persistent memory and natural conversation abilities.
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
title: Multi-User Collaboration with Mem0
|
||||
---
|
||||
|
||||
<Snippet file="paper-release.mdx" />
|
||||
|
||||
## Overview
|
||||
|
||||
Build a multi-user collaborative chat or task management system with Mem0. Each message is attributed to its author, and all messages are stored in a shared project space. Mem0 makes it easy to track contributions, sort and group messages, and collaborate in real time.
|
||||
|
||||
## Setup
|
||||
|
||||
Install the required packages:
|
||||
|
||||
```bash
|
||||
pip install openai mem0ai
|
||||
```
|
||||
|
||||
## Full Code Example
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
from mem0 import Memory
|
||||
import os
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
|
||||
# Set your OpenAI API key
|
||||
os.environ["OPENAI_API_KEY"] = "sk-your-key"
|
||||
|
||||
# Shared project context
|
||||
RUN_ID = "project-demo"
|
||||
|
||||
# Initialize Mem0
|
||||
mem = Memory()
|
||||
|
||||
class CollaborativeAgent:
|
||||
def __init__(self, run_id):
|
||||
self.run_id = run_id
|
||||
self.mem = mem
|
||||
|
||||
def add_message(self, role, name, content):
|
||||
msg = {"role": role, "name": name, "content": content}
|
||||
self.mem.add([msg], run_id=self.run_id, infer=False)
|
||||
|
||||
def brainstorm(self, prompt):
|
||||
# Get recent messages for context
|
||||
memories = self.mem.search(prompt, run_id=self.run_id, limit=5)["results"]
|
||||
context = "\n".join(f"- {m['memory']} (by {m.get('actor_id', 'Unknown')})" for m in memories)
|
||||
client = OpenAI()
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful project assistant."},
|
||||
{"role": "user", "content": f"Prompt: {prompt}\nContext:\n{context}"}
|
||||
]
|
||||
reply = client.chat.completions.create(
|
||||
model="gpt-4o-mini",
|
||||
messages=messages
|
||||
).choices[0].message.content.strip()
|
||||
self.add_message("assistant", "assistant", reply)
|
||||
return reply
|
||||
|
||||
def get_all_messages(self):
|
||||
return self.mem.get_all(run_id=self.run_id)["results"]
|
||||
|
||||
def print_sorted_by_time(self):
|
||||
messages = self.get_all_messages()
|
||||
messages.sort(key=lambda m: m.get('created_at', ''))
|
||||
print("\n--- Messages (sorted by time) ---")
|
||||
for m in messages:
|
||||
who = m.get("actor_id") or "Unknown"
|
||||
ts = m.get('created_at', 'Timestamp N/A')
|
||||
try:
|
||||
dt = datetime.fromisoformat(ts.replace('Z', '+00:00'))
|
||||
ts_fmt = dt.strftime('%Y-%m-%d %H:%M:%S')
|
||||
except Exception:
|
||||
ts_fmt = ts
|
||||
print(f"[{ts_fmt}] [{who}] {m['memory']}")
|
||||
|
||||
def print_grouped_by_actor(self):
|
||||
messages = self.get_all_messages()
|
||||
grouped = defaultdict(list)
|
||||
for m in messages:
|
||||
grouped[m.get("actor_id") or "Unknown"].append(m)
|
||||
print("\n--- Messages (grouped by actor) ---")
|
||||
for actor, mems in grouped.items():
|
||||
print(f"\n=== {actor} ===")
|
||||
for m in mems:
|
||||
ts = m.get('created_at', 'Timestamp N/A')
|
||||
try:
|
||||
dt = datetime.fromisoformat(ts.replace('Z', '+00:00'))
|
||||
ts_fmt = dt.strftime('%Y-%m-%d %H:%M:%S')
|
||||
except Exception:
|
||||
ts_fmt = ts
|
||||
print(f"[{ts_fmt}] {m['memory']}")
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```python
|
||||
# Example usage
|
||||
agent = CollaborativeAgent(RUN_ID)
|
||||
agent.add_message("user", "alice", "Let's list tasks for the new landing page.")
|
||||
agent.add_message("user", "bob", "I'll own the hero section copy.")
|
||||
agent.add_message("user", "carol", "I'll choose product screenshots.")
|
||||
|
||||
# Brainstorm with context
|
||||
print("\nAssistant reply:\n", agent.brainstorm("What are the current open tasks?"))
|
||||
|
||||
# Print all messages sorted by time
|
||||
agent.print_sorted_by_time()
|
||||
|
||||
# Print all messages grouped by actor
|
||||
agent.print_grouped_by_actor()
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- Each message is attributed to a user or agent (actor)
|
||||
- All messages are stored in a shared project space (`run_id`)
|
||||
- You can sort messages by time, group by actor, and format timestamps for clarity
|
||||
- Mem0 makes it easy to build collaborative, attributed chat/task systems
|
||||
|
||||
## Conclusion
|
||||
|
||||
Mem0 enables fast, transparent collaboration for teams and agents, with full attribution, flexible memory search, and easy message organization.
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: Eliza OS Character
|
||||
---
|
||||
|
||||
<Snippet file="paper-release.mdx" />
|
||||
|
||||
You can create a personalised Eliza OS Character using Mem0. This guide will walk you through the necessary steps and provide the complete code to get you started.
|
||||
|
||||
## Overview
|
||||
|
||||
ElizaOS is a powerful AI agent framework for autonomy & personality. It is a collection of tools that help you create a personalised AI agent.
|
||||
|
||||
## Setup
|
||||
You can start by cloning the eliza-os repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/elizaOS/eliza.git
|
||||
```
|
||||
|
||||
Change the directory to the eliza-os repository:
|
||||
|
||||
```bash
|
||||
cd eliza
|
||||
```
|
||||
|
||||
Install the dependencies:
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
Build the project:
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## Setup ENVs
|
||||
|
||||
Create a `.env` file in the root of the project and add the following ( You can use the `.env.example` file as a reference):
|
||||
|
||||
```bash
|
||||
# Mem0 Configuration
|
||||
MEM0_API_KEY= # Mem0 API Key ( Get from https://app.mem0.ai/dashboard/api-keys )
|
||||
MEM0_USER_ID= # Default: eliza-os-user
|
||||
MEM0_PROVIDER= # Default: openai
|
||||
MEM0_PROVIDER_API_KEY= # API Key for the provider (openai, anthropic, etc.)
|
||||
SMALL_MEM0_MODEL= # Default: gpt-4o-mini
|
||||
MEDIUM_MEM0_MODEL= # Default: gpt-4o
|
||||
LARGE_MEM0_MODEL= # Default: gpt-4o
|
||||
```
|
||||
|
||||
## Make the default character use Mem0
|
||||
|
||||
By default, there is a character called `eliza` that uses the `ollama` model. You can make this character use Mem0 by changing the config in the `agent/src/defaultCharacter.ts` file.
|
||||
|
||||
```ts
|
||||
modelProvider: ModelProviderName.MEM0,
|
||||
```
|
||||
|
||||
This will make the character use Mem0 to generate responses.
|
||||
|
||||
## Run the project
|
||||
|
||||
```bash
|
||||
pnpm start
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
You have now created a personalised Eliza OS Character using Mem0. You can now start interacting with the character by running the project and talking to the character.
|
||||
|
||||
This is a simple example of how to use Mem0 to create a personalised AI agent. You can use this as a starting point to create your own AI agent.
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ You can create a personalized AI Companion using Mem0. This guide will walk you
|
||||
src="https://github.com/user-attachments/assets/cebc4f8e-bdb9-4837-868d-13c5ab7bb433"
|
||||
></video>
|
||||
|
||||
You can try the [Mem0 Demo](https://mem0.dev/demo) live here.
|
||||
You can try the [Mem0 Demo](https://mem0-4vmi.vercel.app) live here.
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
---
|
||||
title: 'Healthcare Assistant with Mem0 and Google ADK'
|
||||
description: 'Build a personalized healthcare agent that remembers patient information across conversations using Mem0 and Google ADK'
|
||||
---
|
||||
|
||||
<Snippet file="paper-release.mdx" />
|
||||
|
||||
# Healthcare Assistant with Memory
|
||||
|
||||
This example demonstrates how to build a healthcare assistant that remembers patient information across conversations using Google ADK and Mem0.
|
||||
|
||||
## Overview
|
||||
|
||||
The Healthcare Assistant helps patients by:
|
||||
- Remembering their medical history and symptoms
|
||||
- Providing general health information
|
||||
- Scheduling appointment reminders
|
||||
- Maintaining a personalized experience across conversations
|
||||
|
||||
By integrating Mem0's memory layer with Google ADK, the assistant maintains context about the patient without requiring them to repeat information.
|
||||
|
||||
## Setup
|
||||
|
||||
Before you begin, make sure you have:
|
||||
|
||||
Installed Google ADK and Mem0 SDK:
|
||||
```bash
|
||||
pip install google-adk
|
||||
pip install mem0ai
|
||||
```
|
||||
|
||||
## Code Breakdown
|
||||
|
||||
Let's get started and understand the different components required in building a healthcare assistant powered by memory
|
||||
|
||||
```python
|
||||
# Import dependencies
|
||||
import os
|
||||
from google.adk.agents import Agent
|
||||
from google.adk.sessions import InMemorySessionService
|
||||
from google.adk.runners import Runner
|
||||
from google.genai import types
|
||||
from mem0 import MemoryClient
|
||||
|
||||
# Set up API keys (replace with your actual keys)
|
||||
os.environ["GOOGLE_API_KEY"] = "your-google-api-key"
|
||||
os.environ["MEM0_API_KEY"] = "your-mem0-api-key"
|
||||
|
||||
# Define a global user ID for simplicity
|
||||
USER_ID = "Alex"
|
||||
|
||||
# Initialize Mem0 client
|
||||
mem0_client = MemoryClient()
|
||||
```
|
||||
|
||||
## Define Memory Tools
|
||||
|
||||
First, we'll create tools that allow our agent to store and retrieve information using Mem0:
|
||||
|
||||
```python
|
||||
def save_patient_info(information: str) -> dict:
|
||||
"""Saves important patient information to memory."""
|
||||
|
||||
# Store in Mem0
|
||||
response = mem0_client.add(
|
||||
[{"role": "user", "content": information}],
|
||||
user_id=USER_ID,
|
||||
run_id="healthcare_session",
|
||||
metadata={"type": "patient_information"}
|
||||
)
|
||||
|
||||
|
||||
def retrieve_patient_info(query: str) -> dict:
|
||||
"""Retrieves relevant patient information from memory."""
|
||||
|
||||
# Search Mem0
|
||||
results = mem0_client.search(
|
||||
query,
|
||||
user_id=USER_ID,
|
||||
limit=5,
|
||||
threshold=0.7, # Higher threshold for more relevant results
|
||||
output_format="v1.1"
|
||||
)
|
||||
|
||||
# Format and return the results
|
||||
if results and len(results) > 0:
|
||||
memories = [memory["memory"] for memory in results.get('results', [])]
|
||||
return {
|
||||
"status": "success",
|
||||
"memories": memories,
|
||||
"count": len(memories)
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"status": "no_results",
|
||||
"memories": [],
|
||||
"count": 0
|
||||
}
|
||||
```
|
||||
|
||||
## Define Healthcare Tools
|
||||
|
||||
Next, we'll add tools specific to healthcare assistance:
|
||||
|
||||
```python
|
||||
def schedule_appointment(date: str, time: str, reason: str) -> dict:
|
||||
"""Schedules a doctor's appointment."""
|
||||
# In a real app, this would connect to a scheduling system
|
||||
appointment_id = f"APT-{hash(date + time) % 10000}"
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"appointment_id": appointment_id,
|
||||
"confirmation": f"Appointment scheduled for {date} at {time} for {reason}",
|
||||
"message": "Please arrive 15 minutes early to complete paperwork."
|
||||
}
|
||||
```
|
||||
|
||||
## Create the Healthcare Assistant Agent
|
||||
|
||||
Now we'll create our main agent with all the tools:
|
||||
|
||||
```python
|
||||
# Create the agent
|
||||
healthcare_agent = Agent(
|
||||
name="healthcare_assistant",
|
||||
model="gemini-1.5-flash", # Using Gemini for healthcare assistant
|
||||
description="Healthcare assistant that helps patients with health information and appointment scheduling.",
|
||||
instruction="""You are a helpful Healthcare Assistant with memory capabilities.
|
||||
|
||||
Your primary responsibilities are to:
|
||||
1. Remember patient information using the 'save_patient_info' tool when they share symptoms, conditions, or preferences.
|
||||
2. Retrieve past patient information using the 'retrieve_patient_info' tool when relevant to the current conversation.
|
||||
3. Help schedule appointments using the 'schedule_appointment' tool.
|
||||
|
||||
IMPORTANT GUIDELINES:
|
||||
- Always be empathetic, professional, and helpful.
|
||||
- Save important patient information like symptoms, conditions, allergies, and preferences.
|
||||
- Check if you have relevant patient information before asking for details they may have shared previously.
|
||||
- Make it clear you are not a doctor and cannot provide medical diagnosis or treatment.
|
||||
- For serious symptoms, always recommend consulting a healthcare professional.
|
||||
- Keep all patient information confidential.
|
||||
""",
|
||||
tools=[save_patient_info, retrieve_patient_info, schedule_appointment]
|
||||
)
|
||||
```
|
||||
|
||||
## Set Up Session and Runner
|
||||
|
||||
```python
|
||||
# Set up Session Service and Runner
|
||||
session_service = InMemorySessionService()
|
||||
|
||||
# Define constants for the conversation
|
||||
APP_NAME = "healthcare_assistant_app"
|
||||
USER_ID = "Alex"
|
||||
SESSION_ID = "session_001"
|
||||
|
||||
# Create a session
|
||||
session = session_service.create_session(
|
||||
app_name=APP_NAME,
|
||||
user_id=USER_ID,
|
||||
session_id=SESSION_ID
|
||||
)
|
||||
|
||||
# Create the runner
|
||||
runner = Runner(
|
||||
agent=healthcare_agent,
|
||||
app_name=APP_NAME,
|
||||
session_service=session_service
|
||||
)
|
||||
```
|
||||
|
||||
## Interact with the Healthcare Assistant
|
||||
|
||||
```python
|
||||
# Function to interact with the agent
|
||||
async def call_agent_async(query, runner, user_id, session_id):
|
||||
"""Sends a query to the agent and returns the final response."""
|
||||
print(f"\n>>> Patient: {query}")
|
||||
|
||||
# Format the user's message
|
||||
content = types.Content(
|
||||
role='user',
|
||||
parts=[types.Part(text=query)]
|
||||
)
|
||||
|
||||
# Set user_id for tools to access
|
||||
save_patient_info.user_id = user_id
|
||||
retrieve_patient_info.user_id = user_id
|
||||
|
||||
# Run the agent
|
||||
async for event in runner.run_async(
|
||||
user_id=user_id,
|
||||
session_id=session_id,
|
||||
new_message=content
|
||||
):
|
||||
if event.is_final_response():
|
||||
if event.content and event.content.parts:
|
||||
response = event.content.parts[0].text
|
||||
print(f"<<< Assistant: {response}")
|
||||
return response
|
||||
|
||||
return "No response received."
|
||||
|
||||
# Example conversation flow
|
||||
async def run_conversation():
|
||||
# First interaction - patient introduces themselves with key information
|
||||
await call_agent_async(
|
||||
"Hi, I'm Alex. I've been having headaches for the past week, and I have a penicillin allergy.",
|
||||
runner=runner,
|
||||
user_id=USER_ID,
|
||||
session_id=SESSION_ID
|
||||
)
|
||||
|
||||
# Request for health information
|
||||
await call_agent_async(
|
||||
"Can you tell me more about what might be causing my headaches?",
|
||||
runner=runner,
|
||||
user_id=USER_ID,
|
||||
session_id=SESSION_ID
|
||||
)
|
||||
|
||||
# Schedule an appointment
|
||||
await call_agent_async(
|
||||
"I think I should see a doctor. Can you help me schedule an appointment for next Monday at 2pm?",
|
||||
runner=runner,
|
||||
user_id=USER_ID,
|
||||
session_id=SESSION_ID
|
||||
)
|
||||
|
||||
# Test memory - should remember patient name, symptoms, and allergy
|
||||
await call_agent_async(
|
||||
"What medications should I avoid for my headaches?",
|
||||
runner=runner,
|
||||
user_id=USER_ID,
|
||||
session_id=SESSION_ID
|
||||
)
|
||||
|
||||
# Run the conversation example
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_conversation())
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
This healthcare assistant demonstrates several key capabilities:
|
||||
|
||||
1. **Memory Storage**: When Alex mentions her headaches and penicillin allergy, the agent stores this information in Mem0 using the `save_patient_info` tool.
|
||||
|
||||
2. **Contextual Retrieval**: When Alex asks about headache causes, the agent uses the `retrieve_patient_info` tool to recall her specific situation.
|
||||
|
||||
3. **Memory Application**: When discussing medications, the agent remembers Alex's penicillin allergy without her needing to repeat it, providing safer and more personalized advice.
|
||||
|
||||
4. **Conversation Continuity**: The agent maintains context across the entire conversation session, creating a more natural and efficient interaction.
|
||||
|
||||
## Key Implementation Details
|
||||
|
||||
### User ID Management
|
||||
|
||||
Instead of passing the user ID as a parameter to the memory tools (which would require modifying the ADK's tool calling system), we attach it directly to the function object:
|
||||
|
||||
```python
|
||||
# Set user_id for tools to access
|
||||
save_patient_info.user_id = user_id
|
||||
retrieve_patient_info.user_id = user_id
|
||||
```
|
||||
|
||||
Inside the tool functions, we retrieve this attribute:
|
||||
|
||||
```python
|
||||
# Get user_id from session state or use default
|
||||
user_id = getattr(save_patient_info, 'user_id', 'default_user')
|
||||
```
|
||||
|
||||
This approach allows our tools to maintain user context without complicating their parameter signatures.
|
||||
|
||||
### Mem0 Integration
|
||||
|
||||
The integration with Mem0 happens through two primary functions:
|
||||
|
||||
1. `mem0_client.add()` - Stores new information with appropriate metadata
|
||||
2. `mem0_client.search()` - Retrieves relevant memories using semantic search
|
||||
|
||||
The `threshold` parameter in the search function ensures that only highly relevant memories are returned.
|
||||
|
||||
## Conclusion
|
||||
|
||||
This example demonstrates how to build a healthcare assistant with persistent memory using Google ADK and Mem0. The integration allows for a more personalized patient experience by maintaining context across conversation turns, which is particularly valuable in healthcare scenarios where continuity of information is crucial.
|
||||
|
||||
By storing and retrieving patient information intelligently, the assistant provides more relevant responses without requiring the patient to repeat their medical history, symptoms, or preferences.
|
||||
@@ -322,4 +322,60 @@ Here are the available integrations for Mem0:
|
||||
>
|
||||
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={
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M12 2L22 7L12 12L2 7L12 2Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M2 17L12 22L22 17"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M2 12L12 17L22 12"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
href="/integrations/mastra"
|
||||
>
|
||||
Build AI agents with persistent memory using Mastra's framework and tools.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
title: Mastra
|
||||
---
|
||||
|
||||
<Snippet file="paper-release.mdx" />
|
||||
|
||||
The [**Mastra**](https://mastra.ai/) integration demonstrates how to use Mastra's agent system with Mem0 as the memory backend through custom tools. This enables agents to remember and recall information across conversations.
|
||||
|
||||
## Overview
|
||||
|
||||
In this guide, we'll create a Mastra agent that:
|
||||
1. Uses Mem0 to store information using a memory tool
|
||||
2. Retrieves relevant memories using a search tool
|
||||
3. Provides personalized responses based on past interactions
|
||||
4. Maintains context across conversations and sessions
|
||||
|
||||
## Setup and Configuration
|
||||
|
||||
Install the required libraries:
|
||||
|
||||
```bash
|
||||
npm install @mastra/core @mastra/mem0 @ai-sdk/openai zod
|
||||
```
|
||||
|
||||
Set up your environment variables:
|
||||
|
||||
<Note>Remember to get the Mem0 API key from [Mem0 Platform](https://app.mem0.ai).</Note>
|
||||
|
||||
```bash
|
||||
MEM0_API_KEY=your-mem0-api-key
|
||||
OPENAI_API_KEY=your-openai-api-key
|
||||
```
|
||||
|
||||
## Initialize Mem0 Integration
|
||||
|
||||
Import required modules and set up the Mem0 integration:
|
||||
|
||||
```typescript
|
||||
import { Mem0Integration } from '@mastra/mem0';
|
||||
import { createTool } from '@mastra/core/tools';
|
||||
import { Agent } from '@mastra/core/agent';
|
||||
import { openai } from '@ai-sdk/openai';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Initialize Mem0 integration
|
||||
const mem0 = new Mem0Integration({
|
||||
config: {
|
||||
apiKey: process.env.MEM0_API_KEY || '',
|
||||
user_id: 'alice', // Unique user identifier
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Create Memory Tools
|
||||
|
||||
Set up tools for memorizing and remembering information:
|
||||
|
||||
```typescript
|
||||
// Tool for remembering saved memories
|
||||
const mem0RememberTool = createTool({
|
||||
id: 'Mem0-remember',
|
||||
description: "Remember your agent memories that you've previously saved using the Mem0-memorize tool.",
|
||||
inputSchema: z.object({
|
||||
question: z.string().describe('Question used to look up the answer in saved memories.'),
|
||||
}),
|
||||
outputSchema: z.object({
|
||||
answer: z.string().describe('Remembered answer'),
|
||||
}),
|
||||
execute: async ({ context }) => {
|
||||
console.log(`Searching memory "${context.question}"`);
|
||||
const memory = await mem0.searchMemory(context.question);
|
||||
console.log(`\nFound memory "${memory}"\n`);
|
||||
|
||||
return {
|
||||
answer: memory,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
// Tool for saving new memories
|
||||
const mem0MemorizeTool = createTool({
|
||||
id: 'Mem0-memorize',
|
||||
description: 'Save information to mem0 so you can remember it later using the Mem0-remember tool.',
|
||||
inputSchema: z.object({
|
||||
statement: z.string().describe('A statement to save into memory'),
|
||||
}),
|
||||
execute: async ({ context }) => {
|
||||
console.log(`\nCreating memory "${context.statement}"\n`);
|
||||
// To reduce latency, memories can be saved async without blocking tool execution
|
||||
void mem0.createMemory(context.statement).then(() => {
|
||||
console.log(`\nMemory "${context.statement}" saved.\n`);
|
||||
});
|
||||
return { success: true };
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Create Mastra Agent
|
||||
|
||||
Initialize an agent with memory tools and clear instructions:
|
||||
|
||||
```typescript
|
||||
// Create an agent with memory tools
|
||||
const mem0Agent = new Agent({
|
||||
name: 'Mem0 Agent',
|
||||
instructions: `
|
||||
You are a helpful assistant that has the ability to memorize and remember facts using Mem0.
|
||||
Use the Mem0-memorize tool to save important information that might be useful later.
|
||||
Use the Mem0-remember tool to recall previously saved information when answering questions.
|
||||
`,
|
||||
model: openai('gpt-4o'),
|
||||
tools: { mem0RememberTool, mem0MemorizeTool },
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Key Features
|
||||
|
||||
1. **Tool-based Memory Control**: The agent decides when to save and retrieve information using specific tools
|
||||
2. **Semantic Search**: Mem0 finds relevant memories based on semantic similarity, not just exact matches
|
||||
3. **User-specific Memory Spaces**: Each user_id maintains separate memory contexts
|
||||
4. **Asynchronous Saving**: Memories are saved in the background to reduce response latency
|
||||
5. **Cross-conversation Persistence**: Memories persist across different conversation threads
|
||||
6. **Transparent Operations**: Memory operations are visible through tool usage
|
||||
|
||||
## Conclusion
|
||||
|
||||
By integrating Mastra with Mem0, you can build intelligent agents that learn and remember information across conversations. The tool-based approach provides transparency and control over memory operations, making it easy to create personalized and context-aware AI experiences.
|
||||
|
||||
## Help
|
||||
|
||||
- For more details on Mastra, visit the [Mastra documentation](https://docs.mastra.ai/).
|
||||
- For Mem0 documentation, refer to the [Mem0 Platform](https://app.mem0.ai/).
|
||||
- If you need further assistance, please feel free to reach out to us through the following methods:
|
||||
|
||||
<Snippet file="get-help.mdx" />
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: "Raycast Extension"
|
||||
description: "Mem0 Raycast extension for intelligent memory management"
|
||||
---
|
||||
|
||||
# Mem0
|
||||
|
||||
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 [app.mem0.ai](https://app.mem0.ai)
|
||||
|
||||
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, just like your brain does - 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 over and over - your AI remembers your context and preferences
|
||||
|
||||
---
|
||||
|
||||
<Snippet file="get-help.mdx" />
|
||||
+10
-10
@@ -37,13 +37,13 @@
|
||||
|
||||
## Features
|
||||
|
||||
[Graph Memory](https://docs.mem0.ai/features/graph-memory): Mem0's graph memory system builds relationships between entities in your data, enabling contextually relevant retrieval by analyzing connections between information points - activate it with `enable_graph=True` to enhance search results beyond direct semantic matches, ideal for applications tracking evolving relationships.
|
||||
[Advanced Retrieval](https://docs.mem0.ai/features/advanced-retrieval): Mem0 offers enhanced search capabilities through three advanced retrieval modes: keyword search (improves recall by matching specific terms), reranking (ensures most relevant results appear first using neural networks), and filtering (narrows results by specific criteria) - each can be enabled independently or in combination to optimize search precision and relevance.
|
||||
[Multimodal Support](https://docs.mem0.ai/features/multimodal-support): Mem0 extends beyond text by supporting images and documents (JPG, PNG, MDX, TXT, PDF), allowing users to integrate visual and document content through direct URLs or Base64 encoding, enhancing the memory system's ability to understand and recall information from various media types.
|
||||
[Memory Customization](https://docs.mem0.ai/features/selective-memory): Mem0 enables selective memory storage through inclusion and exclusion rules, allowing users to focus on relevant information (like specific topics) while omitting irrelevant data (such as food preferences), resulting in more efficient, accurate, and privacy-conscious AI interactions.
|
||||
[Custom Categories](https://docs.mem0.ai/features/custom-categories): Mem0 allows setting custom categories at both project level and during individual API calls, overriding default categories (like personal_details, family, sports) with more specific ones to improve memory categorization accuracy - simply provide a list of category dictionaries with descriptive definitions when adding memories.
|
||||
[Async Client](https://docs.mem0.ai/features/async-client): Mem0 provides an AsyncMemoryClient for non-blocking operations, offering the same functionality as the synchronous client (add, search, get_all, delete, etc.) but with async/await support, making it ideal for high-concurrency applications that need to perform memory operations without blocking execution.
|
||||
[Memory Export](https://docs.mem0.ai/features/memory-export): Mem0 enables exporting memories in structured formats using customizable Pydantic schemas, allowing you to transform stored memories into specific data structures by defining schemas, submitting export jobs with optional processing instructions, and retrieving the formatted data with various filtering options.
|
||||
[Graph Memory](https://docs.mem0.ai/platform/features/graph-memory): Mem0's graph memory system builds relationships between entities in your data, enabling contextually relevant retrieval by analyzing connections between information points - activate it with `enable_graph=True` to enhance search results beyond direct semantic matches, ideal for applications tracking evolving relationships.
|
||||
[Advanced Retrieval](https://docs.mem0.ai/platform/features/advanced-retrieval): Mem0 offers enhanced search capabilities through three advanced retrieval modes: keyword search (improves recall by matching specific terms), reranking (ensures most relevant results appear first using neural networks), and filtering (narrows results by specific criteria) - each can be enabled independently or in combination to optimize search precision and relevance.
|
||||
[Multimodal Support](https://docs.mem0.ai/platform/features/multimodal-support): Mem0 extends beyond text by supporting images and documents (JPG, PNG, MDX, TXT, PDF), allowing users to integrate visual and document content through direct URLs or Base64 encoding, enhancing the memory system's ability to understand and recall information from various media types.
|
||||
[Memory Customization](https://docs.mem0.ai/platform/features/selective-memory): Mem0 enables selective memory storage through inclusion and exclusion rules, allowing users to focus on relevant information (like specific topics) while omitting irrelevant data (such as food preferences), resulting in more efficient, accurate, and privacy-conscious AI interactions.
|
||||
[Custom Categories](https://docs.mem0.ai/platform/features/custom-categories): Mem0 allows setting custom categories at both project level and during individual API calls, overriding default categories (like personal_details, family, sports) with more specific ones to improve memory categorization accuracy - simply provide a list of category dictionaries with descriptive definitions when adding memories.
|
||||
[Async Client](https://docs.mem0.ai/platform/features/async-client): Mem0 provides an AsyncMemoryClient for non-blocking operations, offering the same functionality as the synchronous client (add, search, get_all, delete, etc.) but with async/await support, making it ideal for high-concurrency applications that need to perform memory operations without blocking execution.
|
||||
[Memory Export](https://docs.mem0.ai/platform/features/memory-export): Mem0 enables exporting memories in structured formats using customizable Pydantic schemas, allowing you to transform stored memories into specific data structures by defining schemas, submitting export jobs with optional processing instructions, and retrieving the formatted data with various filtering options.
|
||||
|
||||
## OSS
|
||||
|
||||
@@ -72,9 +72,9 @@
|
||||
|
||||
### Features
|
||||
|
||||
[OpenAI Compatibility](https://docs.mem0.ai/features/openai_compatibility): Mem0 offers seamless integration with OpenAI-compatible APIs, allowing developers to enhance conversational agents with structured memory by initializing with a Mem0 API key (or locally without one), supporting various LLM providers, and enabling personalized responses through user context persistence across interactions with parameters like user_id, agent_id, and custom filters.
|
||||
[Custom Fact Extraction Prompt](https://docs.mem0.ai/features/custom-fact-extraction-prompt): Mem0 enables custom fact extraction prompts to tailor information extraction for specific use cases by defining domain-specific examples and formats, allowing precise control over what information is extracted from messages - simply provide a custom prompt with few-shot examples in the config when initializing the Memory client.
|
||||
[Custom Update Memory Prompt](https://docs.mem0.ai/features/custom-update-memory-prompt): Mem0 enables customizing the update memory prompt to control how memories are modified by comparing newly retrieved facts with existing memories and determining appropriate actions (add, update, delete, or no change) based on custom logic and examples provided in the prompt configuration.
|
||||
[OpenAI Compatibility](https://docs.mem0.ai/open-source/features/openai_compatibility): Mem0 offers seamless integration with OpenAI-compatible APIs, allowing developers to enhance conversational agents with structured memory by initializing with a Mem0 API key (or locally without one), supporting various LLM providers, and enabling personalized responses through user context persistence across interactions with parameters like user_id, agent_id, and custom filters.
|
||||
[Custom Fact Extraction Prompt](https://docs.mem0.ai/open-source/features/custom-fact-extraction-prompt): Mem0 enables custom fact extraction prompts to tailor information extraction for specific use cases by defining domain-specific examples and formats, allowing precise control over what information is extracted from messages - simply provide a custom prompt with few-shot examples in the config when initializing the Memory client.
|
||||
[Custom Update Memory Prompt](https://docs.mem0.ai/open-source/features/custom-update-memory-prompt): Mem0 enables customizing the update memory prompt to control how memories are modified by comparing newly retrieved facts with existing memories and determining appropriate actions (add, update, delete, or no change) based on custom logic and examples provided in the prompt configuration.
|
||||
[REST API Server](https://docs.mem0.ai/open-source/features/rest-api): Mem0 provides a FastAPI-based REST API server that supports core operations (create/retrieve/search/update/delete memories) with OpenAPI documentation at /docs, easily deployable via Docker Compose with pre-configured databases (postgres pgvector, neo4j) - just set OPENAI_API_KEY to get started.
|
||||
[Graph Memory](https://docs.mem0.ai/open-source/graph_memory/overview): Mem0's open-source graph memory system enables building and querying relationships between entities by installing with `pip install "mem0ai[graph]"` and configuring a graph store provider (like Neo4j) - this allows for more contextual memory retrieval by combining vector and graph-based approaches to track evolving relationships between information points.
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: Multimodal Support
|
||||
description: Integrate images into your interactions with Mem0
|
||||
icon: "image"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
<Snippet file="paper-release.mdx" />
|
||||
|
||||
Mem0 extends its capabilities beyond text by supporting multimodal data. With this feature, users can seamlessly integrate images into their interactions—allowing Mem0 to extract relevant information.
|
||||
|
||||
## How It Works
|
||||
|
||||
When a user submits an image, Mem0 processes it to extract textual information and other pertinent details. These details are then added to the user's memory, enhancing the system's ability to understand and recall multimodal inputs.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
client = Memory()
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hi, my name is Alice."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Nice to meet you, Alice! What do you like to eat?"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": {
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://www.superhealthykids.com/wp-content/uploads/2021/10/best-veggie-pizza-featured-image-square-2.jpg"
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
# Calling the add method to ingest messages into the memory system
|
||||
client.add(messages, user_id="alice")
|
||||
```
|
||||
|
||||
```json Output
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"memory": "Name is Alice",
|
||||
"event": "ADD",
|
||||
"id": "7ae113a3-3cb5-46e9-b6f7-486c36391847"
|
||||
},
|
||||
{
|
||||
"memory": "Likes large pizza with toppings including cherry tomatoes, black olives, green spinach, yellow bell peppers, diced ham, and sliced mushrooms",
|
||||
"event": "ADD",
|
||||
"id": "56545065-7dee-4acf-8bf2-a5b2535aabb3"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Using these methods, you can seamlessly incorporate various media types into your interactions, further enhancing Mem0's multimodal capabilities.
|
||||
|
||||
If you have any questions, please feel free to reach out to us using one of the following methods:
|
||||
|
||||
<Snippet file="get-help.mdx" />
|
||||
+59
-49
@@ -1490,11 +1490,11 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\nquery = \"What do you know about me?\"\nfilters = {\n \"AND\":[\n {\n \"user_id\":\"alex\"\n },\n {\n \"agent_id\":{\n \"in\":[\n \"travel-assistant\",\n \"customer-support\"\n ]\n }\n }\n ]\n}\nclient.search(query, version=\"v2\", filters=filters)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\nquery = \"What do you know about me?\"\nfilters = {\n \"OR\":[\n {\n \"user_id\":\"alex\"\n },\n {\n \"agent_id\":{\n \"in\":[\n \"travel-assistant\",\n \"customer-support\"\n ]\n }\n }\n ]\n}\nclient.search(query, version=\"v2\", filters=filters)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst query = \"What do you know about me?\";\nconst filters = {\n AND: [\n { user_id: \"alex\" },\n { agent_id: { in: [\"travel-assistant\", \"customer-support\"] } }\n ]\n};\n\nclient.search(query, { api_version: \"v2\", filters })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst query = \"What do you know about me?\";\nconst filters = {\n OR: [\n { user_id: \"alex\" },\n { agent_id: { in: [\"travel-assistant\", \"customer-support\"] } }\n ]\n};\n\nclient.search(query, { api_version: \"v2\", filters })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
@@ -2316,6 +2316,11 @@
|
||||
"message": {
|
||||
"type": "string",
|
||||
"example": "Organization created successfully."
|
||||
},
|
||||
"org_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Unique identifier for the organization"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2722,13 +2727,13 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"username",
|
||||
"email",
|
||||
"role"
|
||||
],
|
||||
"properties": {
|
||||
"username": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"description": "Username of the member whose role is to be updated"
|
||||
"description": "Email of the member whose role is to be updated"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
@@ -2798,27 +2803,27 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "import requests\n\nurl = \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\"\n\npayload = {\n \"username\": \"<string>\",\n \"role\": \"<string>\"\n}\nheaders = {\n \"Authorization\": \"<api-key>\",\n \"Content-Type\": \"application/json\"\n}\n\nresponse = requests.request(\"PUT\", url, json=payload, headers=headers)\n\nprint(response.text)"
|
||||
"source": "import requests\n\nurl = \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\"\n\npayload = {\n \"email\": \"<string>\",\n \"role\": \"<string>\"\n}\nheaders = {\n \"Authorization\": \"<api-key>\",\n \"Content-Type\": \"application/json\"\n}\n\nresponse = requests.request(\"PUT\", url, json=payload, headers=headers)\n\nprint(response.text)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "const options = {\n method: 'PUT',\n headers: {Authorization: 'Token <api-key>', 'Content-Type': 'application/json'},\n body: '{\"username\":\"<string>\",\"role\":\"<string>\"}'\n};\n\nfetch('https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/', options)\n .then(response => response.json())\n .then(response => console.log(response))\n .catch(err => console.error(err));"
|
||||
"source": "const options = {\n method: 'PUT',\n headers: {Authorization: 'Token <api-key>', 'Content-Type': 'application/json'},\n body: '{\"email\":\"<string>\",\"role\":\"<string>\"}'\n};\n\nfetch('https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/', options)\n .then(response => response.json())\n .then(response => console.log(response))\n .catch(err => console.error(err));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl --request PUT \\\n --url https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"username\": \"<string>\",\n \"role\": \"<string>\"\n}'"
|
||||
"source": "curl --request PUT \\\n --url https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"email\": \"<string>\",\n \"role\": \"<string>\"\n}'"
|
||||
},
|
||||
{
|
||||
"lang": "Go",
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\n\turl := \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\"\n\n\tpayload := strings.NewReader(\"{\n \\\"username\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n\n\treq, _ := http.NewRequest(\"PUT\", url, payload)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(res)\n\tfmt.Println(string(body))\n\n}"
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\n\turl := \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\"\n\n\tpayload := strings.NewReader(\"{\n \\\"email\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n\n\treq, _ := http.NewRequest(\"PUT\", url, payload)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(res)\n\tfmt.Println(string(body))\n\n}"
|
||||
},
|
||||
{
|
||||
"lang": "PHP",
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"PUT\",\n CURLOPT_POSTFIELDS => \"{\n \\\"username\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"PUT\",\n CURLOPT_POSTFIELDS => \"{\n \\\"email\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
},
|
||||
{
|
||||
"lang": "Java",
|
||||
"source": "HttpResponse<String> response = Unirest.put(\"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\")\n .header(\"Authorization\", \"Token <api-key>\")\n .header(\"Content-Type\", \"application/json\")\n .body(\"{\n \\\"username\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n .asString();"
|
||||
"source": "HttpResponse<String> response = Unirest.put(\"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\")\n .header(\"Authorization\", \"Token <api-key>\")\n .header(\"Content-Type\", \"application/json\")\n .body(\"{\n \\\"email\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n .asString();"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2847,13 +2852,13 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"username",
|
||||
"email",
|
||||
"role"
|
||||
],
|
||||
"properties": {
|
||||
"username": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"description": "Username of the member to be added"
|
||||
"description": "Email of the member to be added"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
@@ -2923,23 +2928,23 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "import requests\n\nurl = \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\"\n\npayload = {\n \"username\": \"<string>\",\n \"role\": \"<string>\"\n}\nheaders = {\n \"Authorization\": \"<api-key>\",\n \"Content-Type\": \"application/json\"\n}\n\nresponse = requests.request(\"POST\", url, json=payload, headers=headers)\n\nprint(response.text)"
|
||||
"source": "import requests\n\nurl = \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\"\n\npayload = {\n \"email\": \"<string>\",\n \"role\": \"<string>\"\n}\nheaders = {\n \"Authorization\": \"<api-key>\",\n \"Content-Type\": \"application/json\"\n}\n\nresponse = requests.request(\"POST\", url, json=payload, headers=headers)\n\nprint(response.text)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "const options = {\n method: 'POST',\n headers: {Authorization: 'Token <api-key>', 'Content-Type': 'application/json'},\n body: '{\"username\":\"<string>\",\"role\":\"<string>\"}'\n};\n\nfetch('https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/', options)\n .then(response => response.json())\n .then(response => console.log(response))\n .catch(err => console.error(err));"
|
||||
"source": "const options = {\n method: 'POST',\n headers: {Authorization: 'Token <api-key>', 'Content-Type': 'application/json'},\n body: '{\"email\":\"<string>\",\"role\":\"<string>\"}'\n};\n\nfetch('https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/', options)\n .then(response => response.json())\n .then(response => console.log(response))\n .catch(err => console.error(err));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl --request POST \\\n --url https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"username\": \"<string>\",\n \"role\": \"<string>\"\n}'"
|
||||
"source": "curl --request POST \\\n --url https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"email\": \"<string>\",\n \"role\": \"<string>\"\n}'"
|
||||
},
|
||||
{
|
||||
"lang": "Go",
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\n\turl := \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\"\n\n\tpayload := strings.NewReader(\"{\n \\\"username\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n\n\treq, _ := http.NewRequest(\"POST\", url, payload)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(res)\n\tfmt.Println(string(body))\n\n}"
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\n\turl := \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\"\n\n\tpayload := strings.NewReader(\"{\n \\\"email\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n\n\treq, _ := http.NewRequest(\"POST\", url, payload)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(res)\n\tfmt.Println(string(body))\n\n}"
|
||||
},
|
||||
{
|
||||
"lang": "PHP",
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"POST\",\n CURLOPT_POSTFIELDS => \"{\n \\\"username\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"POST\",\n CURLOPT_POSTFIELDS => \"{\n \\\"email\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
},
|
||||
{
|
||||
"lang": "Java",
|
||||
@@ -2971,12 +2976,12 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"username"
|
||||
"email"
|
||||
],
|
||||
"properties": {
|
||||
"username": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"description": "Username of the member to be removed"
|
||||
"description": "Email of the member to be removed"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3020,27 +3025,27 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "import requests\n\nurl = \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\"\n\npayload = {\"username\": \"<string>\"}\nheaders = {\n \"Authorization\": \"<api-key>\",\n \"Content-Type\": \"application/json\"\n}\n\nresponse = requests.request(\"DELETE\", url, json=payload, headers=headers)\n\nprint(response.text)"
|
||||
"source": "import requests\n\nurl = \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\"\n\npayload = {\"email\": \"<string>\"}\nheaders = {\n \"Authorization\": \"<api-key>\",\n \"Content-Type\": \"application/json\"\n}\n\nresponse = requests.request(\"DELETE\", url, json=payload, headers=headers)\n\nprint(response.text)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "const options = {\n method: 'DELETE',\n headers: {Authorization: 'Token <api-key>', 'Content-Type': 'application/json'},\n body: '{\"username\":\"<string>\"}'\n};\n\nfetch('https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/', options)\n .then(response => response.json())\n .then(response => console.log(response))\n .catch(err => console.error(err));"
|
||||
"source": "const options = {\n method: 'DELETE',\n headers: {Authorization: 'Token <api-key>', 'Content-Type': 'application/json'},\n body: '{\"email\":\"<string>\"}'\n};\n\nfetch('https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/', options)\n .then(response => response.json())\n .then(response => console.log(response))\n .catch(err => console.error(err));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl --request DELETE \\\n --url https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"username\": \"<string>\"\n}'"
|
||||
"source": "curl --request DELETE \\\n --url https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"email\": \"<string>\"\n}'"
|
||||
},
|
||||
{
|
||||
"lang": "Go",
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\n\turl := \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\"\n\n\tpayload := strings.NewReader(\"{\n \\\"username\\\": \\\"<string>\\\"\n}\")\n\n\treq, _ := http.NewRequest(\"DELETE\", url, payload)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(res)\n\tfmt.Println(string(body))\n\n}"
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\n\turl := \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\"\n\n\tpayload := strings.NewReader(\"{\n \\\"email\\\": \\\"<string>\\\"\n}\")\n\n\treq, _ := http.NewRequest(\"DELETE\", url, payload)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(res)\n\tfmt.Println(string(body))\n\n}"
|
||||
},
|
||||
{
|
||||
"lang": "PHP",
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"DELETE\",\n CURLOPT_POSTFIELDS => \"{\n \\\"username\\\": \\\"<string>\\\"\n}\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"DELETE\",\n CURLOPT_POSTFIELDS => \"{\n \\\"email\\\": \\\"<string>\\\"\n}\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
},
|
||||
{
|
||||
"lang": "Java",
|
||||
"source": "HttpResponse<String> response = Unirest.delete(\"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\")\n .header(\"Authorization\", \"Token <api-key>\")\n .header(\"Content-Type\", \"application/json\")\n .body(\"{\n \\\"username\\\": \\\"<string>\\\"\n}\")\n .asString();"
|
||||
"source": "HttpResponse<String> response = Unirest.delete(\"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/members/\")\n .header(\"Authorization\", \"Token <api-key>\")\n .header(\"Content-Type\", \"application/json\")\n .body(\"{\n \\\"email\\\": \\\"<string>\\\"\n}\")\n .asString();"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3199,6 +3204,11 @@
|
||||
"message": {
|
||||
"type": "string",
|
||||
"example": "Project created successfully."
|
||||
},
|
||||
"project_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Unique identifier for the project"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3753,13 +3763,13 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"username",
|
||||
"email",
|
||||
"role"
|
||||
],
|
||||
"properties": {
|
||||
"username": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"description": "Username of the member to be added"
|
||||
"description": "Email of the member to be added"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
@@ -3823,27 +3833,27 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "import requests\n\nurl = \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\"\n\npayload = {\n \"username\": \"<string>\",\n \"role\": \"<string>\"\n}\nheaders = {\n \"Authorization\": \"<api-key>\",\n \"Content-Type\": \"application/json\"\n}\n\nresponse = requests.request(\"POST\", url, json=payload, headers=headers)\n\nprint(response.text)"
|
||||
"source": "import requests\n\nurl = \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\"\n\npayload = {\n \"email\": \"<string>\",\n \"role\": \"<string>\"\n}\nheaders = {\n \"Authorization\": \"<api-key>\",\n \"Content-Type\": \"application/json\"\n}\n\nresponse = requests.request(\"POST\", url, json=payload, headers=headers)\n\nprint(response.text)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "const options = {\n method: 'POST',\n headers: {Authorization: 'Token <api-key>', 'Content-Type': 'application/json'},\n body: '{\"username\":\"<string>\",\"role\":\"<string>\"}'\n};\n\nfetch('https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/', options)\n .then(response => response.json())\n .then(response => console.log(response))\n .catch(err => console.error(err));"
|
||||
"source": "const options = {\n method: 'POST',\n headers: {Authorization: 'Token <api-key>', 'Content-Type': 'application/json'},\n body: '{\"email\":\"<string>\",\"role\":\"<string>\"}'\n};\n\nfetch('https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/', options)\n .then(response => response.json())\n .then(response => console.log(response))\n .catch(err => console.error(err));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl --request POST \\\n --url https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"username\": \"<string>\",\n \"role\": \"<string>\"\n}'"
|
||||
"source": "curl --request POST \\\n --url https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"email\": \"<string>\",\n \"role\": \"<string>\"\n}'"
|
||||
},
|
||||
{
|
||||
"lang": "Go",
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\n\turl := \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\"\n\n\tpayload := strings.NewReader(\"{\n \\\"username\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n\n\treq, _ := http.NewRequest(\"POST\", url, payload)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(res)\n\tfmt.Println(string(body))\n\n}"
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\n\turl := \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\"\n\n\tpayload := strings.NewReader(\"{\n \\\"email\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n\n\treq, _ := http.NewRequest(\"POST\", url, payload)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(res)\n\tfmt.Println(string(body))\n\n}"
|
||||
},
|
||||
{
|
||||
"lang": "PHP",
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"POST\",\n CURLOPT_POSTFIELDS => \"{\n \\\"username\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"POST\",\n CURLOPT_POSTFIELDS => \"{\n \\\"email\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
},
|
||||
{
|
||||
"lang": "Java",
|
||||
"source": "HttpResponse<String> response = Unirest.post(\"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\")\n .header(\"Authorization\", \"Token <api-key>\")\n .header(\"Content-Type\", \"application/json\")\n .body(\"{\n \\\"username\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n .asString();"
|
||||
"source": "HttpResponse<String> response = Unirest.post(\"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\")\n .header(\"Authorization\", \"Token <api-key>\")\n .header(\"Content-Type\", \"application/json\")\n .body(\"{\n \\\"email\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n .asString();"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -3881,13 +3891,13 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"username",
|
||||
"email",
|
||||
"role"
|
||||
],
|
||||
"properties": {
|
||||
"username": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"description": "Username of the member to be updated"
|
||||
"description": "Email of the member to be updated"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
@@ -3951,27 +3961,27 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "import requests\n\nurl = \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\"\n\npayload = {\n \"username\": \"<string>\",\n \"role\": \"<string>\"\n}\nheaders = {\n \"Authorization\": \"<api-key>\",\n \"Content-Type\": \"application/json\"\n}\n\nresponse = requests.request(\"PUT\", url, json=payload, headers=headers)\n\nprint(response.text)"
|
||||
"source": "import requests\n\nurl = \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\"\n\npayload = {\n \"email\": \"<string>\",\n \"role\": \"<string>\"\n}\nheaders = {\n \"Authorization\": \"<api-key>\",\n \"Content-Type\": \"application/json\"\n}\n\nresponse = requests.request(\"PUT\", url, json=payload, headers=headers)\n\nprint(response.text)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "const options = {\n method: 'PUT',\n headers: {Authorization: 'Token <api-key>', 'Content-Type': 'application/json'},\n body: '{\"username\":\"<string>\",\"role\":\"<string>\"}'\n};\n\nfetch('https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/', options)\n .then(response => response.json())\n .then(response => console.log(response))\n .catch(err => console.error(err));"
|
||||
"source": "const options = {\n method: 'PUT',\n headers: {Authorization: 'Token <api-key>', 'Content-Type': 'application/json'},\n body: '{\"email\":\"<string>\",\"role\":\"<string>\"}'\n};\n\nfetch('https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/', options)\n .then(response => response.json())\n .then(response => console.log(response))\n .catch(err => console.error(err));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl --request PUT \\\n --url https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"username\": \"<string>\",\n \"role\": \"<string>\"\n}'"
|
||||
"source": "curl --request PUT \\\n --url https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"email\": \"<string>\",\n \"role\": \"<string>\"\n}'"
|
||||
},
|
||||
{
|
||||
"lang": "Go",
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\n\turl := \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\"\n\n\tpayload := strings.NewReader(\"{\n \\\"username\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n\n\treq, _ := http.NewRequest(\"PUT\", url, payload)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(res)\n\tfmt.Println(string(body))\n\n}"
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\n\turl := \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\"\n\n\tpayload := strings.NewReader(\"{\n \\\"email\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n\n\treq, _ := http.NewRequest(\"PUT\", url, payload)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(res)\n\tfmt.Println(string(body))\n\n}"
|
||||
},
|
||||
{
|
||||
"lang": "PHP",
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"PUT\",\n CURLOPT_POSTFIELDS => \"{\n \\\"username\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"PUT\",\n CURLOPT_POSTFIELDS => \"{\n \\\"email\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
},
|
||||
{
|
||||
"lang": "Java",
|
||||
"source": "HttpResponse<String> response = Unirest.put(\"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\")\n .header(\"Authorization\", \"Token <api-key>\")\n .header(\"Content-Type\", \"application/json\")\n .body(\"{\n \\\"username\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n .asString();"
|
||||
"source": "HttpResponse<String> response = Unirest.put(\"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/members/\")\n .header(\"Authorization\", \"Token <api-key>\")\n .header(\"Content-Type\", \"application/json\")\n .body(\"{\n \\\"email\\\": \\\"<string>\\\",\n \\\"role\\\": \\\"<string>\\\"\n}\")\n .asString();"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -3999,10 +4009,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "username",
|
||||
"name": "email",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"description": "Username of the member to be removed",
|
||||
"description": "Email of the member to be removed",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -4809,7 +4819,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"messages": {
|
||||
"description": "An array of message objects representing the content of the memory. Each message object typically contains 'role' and 'content' fields, where 'role' indicates the sender (e.g., 'user', 'assistant', 'system') and 'content' contains the actual message text. This structure allows for the representation of conversations or multi-part memories.",
|
||||
"description": "An array of message objects representing the content of the memory. Each message object typically contains 'role' and 'content' fields, where 'role' indicates the sender either 'user' or 'assistant' and 'content' contains the actual message text. This structure allows for the representation of conversations or multi-part memories.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: Overview
|
||||
icon: "info"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
<Snippet file="paper-release.mdx" />
|
||||
|
||||
OpenMemory is a local memory infrastructure powered by Mem0 that lets you carry your memory accross any AI app. It provides a unified memory layer that stays with you, enabling agents and assistants to remember what matters across applications.
|
||||
|
||||
<img src="https://github.com/user-attachments/assets/3c701757-ad82-4afa-bfbe-e049c2b4320b" alt="OpenMemory UI" />
|
||||
|
||||
## What is the OpenMemory MCP Server
|
||||
|
||||
The OpenMemory MCP Server is a private, local-first memory server that creates a shared, persistent memory layer for your MCP-compatible tools. This runs entirely on your machine, enabling seamless context handoff across tools. Whether you're switching between development, planning, or debugging environments, your AI assistants can access relevant memory without needing repeated instructions.
|
||||
|
||||
The OpenMemory MCP Server ensures all memory stays local, structured, and under your control with no cloud sync or external storage.
|
||||
|
||||
## OpenMemory Easy Setup
|
||||
|
||||
### Prerequisites
|
||||
- Docker
|
||||
- OpenAI API Key
|
||||
|
||||
You can quickly run OpenMemory by running the following command:
|
||||
|
||||
```bash
|
||||
curl -sL https://raw.githubusercontent.com/mem0ai/mem0/main/openmemory/run.sh | bash
|
||||
```
|
||||
|
||||
You should set the `OPENAI_API_KEY` as a global environment variable:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key
|
||||
```
|
||||
|
||||
You can also set the `OPENAI_API_KEY` as a parameter to the script:
|
||||
|
||||
```bash
|
||||
curl -sL https://raw.githubusercontent.com/mem0ai/mem0/main/openmemory/run.sh | OPENAI_API_KEY=your_api_key bash
|
||||
```
|
||||
|
||||
This will start the OpenMemory server and the OpenMemory UI. Deleting the container will lead to the deletion of the memory store.
|
||||
We suggest you follow the instructions [here](/openmemory/quickstart#setting-up-openmemory) to set up OpenMemory on your local machine, with more persistant memory store.
|
||||
|
||||
## How the OpenMemory MCP Server Works
|
||||
|
||||
Built around the Model Context Protocol (MCP), the OpenMemory MCP Server exposes a standardized set of memory tools:
|
||||
- `add_memories`: Store new memory objects
|
||||
- `search_memory`: Retrieve relevant memories
|
||||
- `list_memories`: View all stored memory
|
||||
- `delete_all_memories`: Clear memory entirely
|
||||
|
||||
Any MCP-compatible tool can connect to the server and use these APIs to persist and access memory.
|
||||
|
||||
## What It Enables
|
||||
|
||||
### Cross-Client Memory Access
|
||||
Store context in Cursor and retrieve it later in Claude or Windsurf without repeating yourself.
|
||||
|
||||
### Fully Local Memory Store
|
||||
All memory is stored on your machine. Nothing goes to the cloud. You maintain full ownership and control.
|
||||
|
||||
### Unified Memory UI
|
||||
The built-in OpenMemory dashboard provides a central view of everything stored. Add, browse, delete and control memory access to clients directly from the dashboard.
|
||||
|
||||
## Supported Clients
|
||||
|
||||
The OpenMemory MCP Server is compatible with any client that supports the Model Context Protocol. This includes:
|
||||
- Cursor
|
||||
- Claude Desktop
|
||||
- Windsurf
|
||||
- Cline, and more.
|
||||
|
||||
As more AI systems adopt MCP, your private memory becomes more valuable.
|
||||
|
||||
## Real-World Examples
|
||||
|
||||
### Scenario 1: Cross-Tool Project Flow
|
||||
Define technical requirements of a project in Claude Desktop. Build in Cursor. Debug issues in Windsurf - all with shared context passed through OpenMemory.
|
||||
|
||||
### Scenario 2: Preferences That Persist
|
||||
Set your preferred code style or tone in one tool. When you switch to another MCP client, it can access those same preferences without redefining them.
|
||||
|
||||
### Scenario 3: Project Knowledge
|
||||
Save important project details once, then access them from any compatible AI tool, no more repetitive explanations.
|
||||
|
||||
## Conclusion
|
||||
|
||||
The OpenMemory MCP Server brings memory to MCP-compatible tools without giving up control or privacy. It solves a foundational limitation in modern LLM workflows: the loss of context across tools, sessions, and environments.
|
||||
|
||||
By standardizing memory operations and keeping all data local, it reduces token overhead, improves performance, and unlocks more intelligent interactions across the growing ecosystem of AI assistants.
|
||||
|
||||
This is just the beginning. The MCP server is the first core layer in the OpenMemory platform - a broader effort to make memory portable, private, and interoperable across AI systems.
|
||||
|
||||
## Getting Started Today
|
||||
|
||||
- Github Repository: https://github.com/mem0ai/mem0
|
||||
- Read the documentation: [Docs Link]
|
||||
- Join our community: [Discord link]
|
||||
|
||||
With OpenMemory, your AI memories stay private, portable, and under your control, exactly where they belong.
|
||||
|
||||
OpenMemory: Your memories, your control.
|
||||
|
||||
## Contributing
|
||||
|
||||
OpenMemory is open source and we welcome contributions. Please see the [CONTRIBUTING.md](https://github.com/mem0ai/mem0/blob/main/openmemory/CONTRIBUTING.md) file for more information.
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: Quickstart
|
||||
icon: "terminal"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
<Snippet file="paper-release.mdx" />
|
||||
|
||||
## OpenMemory Easy Setup
|
||||
|
||||
### Prerequisites
|
||||
- Docker
|
||||
- OpenAI API Key
|
||||
|
||||
You can quickly run OpenMemory by running the following command:
|
||||
|
||||
```bash
|
||||
curl -sL https://raw.githubusercontent.com/mem0ai/mem0/main/openmemory/run.sh | bash
|
||||
```
|
||||
|
||||
You should set the `OPENAI_API_KEY` as a global environment variable:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key
|
||||
```
|
||||
|
||||
You can also set the `OPENAI_API_KEY` as a parameter to the script:
|
||||
|
||||
```bash
|
||||
curl -sL https://raw.githubusercontent.com/mem0ai/mem0/main/openmemory/run.sh | OPENAI_API_KEY=your_api_key bash
|
||||
```
|
||||
|
||||
This will start the OpenMemory server and the OpenMemory UI. Deleting the container will lead to the deletion of the memory store.
|
||||
We suggest you follow the instructions below to set up OpenMemory on your local machine, with more persistant memory store.
|
||||
|
||||
## Setting Up OpenMemory
|
||||
|
||||
Getting started with OpenMemory is straight forward and takes just a few minutes to set up on your local machine. Follow these steps:
|
||||
|
||||
### Getting started
|
||||
First clone the repository and then follow the instructions:
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/mem0ai/mem0.git
|
||||
cd mem0/openmemory
|
||||
|
||||
# Create the backend .env file with your OpenAI key
|
||||
make env
|
||||
|
||||
# Build the Docker images
|
||||
make build
|
||||
|
||||
# Start all services (API server, vector database, and MCP server components)
|
||||
make up
|
||||
```
|
||||
|
||||
You can configure the MCP client using the following command (replace username with your username):
|
||||
|
||||
```bash
|
||||
npx install-mcp i "http://localhost:8765/mcp/cursor/sse/username" --client cursor
|
||||
```
|
||||
|
||||
The OpenMemory dashboard will be available at http://localhost:3000. From here, you can view and manage your memories, as well as check connection status with your MCP clients.
|
||||
|
||||
Once set up, OpenMemory runs locally on your machine, ensuring all your AI memories remain private and secure while being accessible across any compatible MCP client.
|
||||
|
||||
### Getting Started Today
|
||||
|
||||
- Github Repository: https://github.com/mem0ai/mem0/openmemory
|
||||
@@ -5,6 +5,10 @@ iconType: "solid"
|
||||
---
|
||||
|
||||
<Snippet file="paper-release.mdx" />
|
||||
<Note type="info">
|
||||
🎉 We're excited to announce that Claude 4 is now available with Mem0! Check it out [here](components/llms/models/anthropic).
|
||||
</Note>
|
||||
|
||||
|
||||
# Introduction
|
||||
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
---
|
||||
title: Criteria Retrieval
|
||||
icon: "magnifying-glass-plus"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
<Snippet file="paper-release.mdx" />
|
||||
|
||||
Mem0's **Criteria Retrieval** feature allows you to retrieve memories based on specific criteria. This is useful when you need to find memories that match certain conditions or criteria, such as emotional content, sentiment, or other custom attributes.
|
||||
|
||||
## Setting Up Custom Criteria
|
||||
|
||||
You can define custom criteria at the project level, assigning weights to each criterion. These weights will be normalized during memory retrieval.
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(
|
||||
api_key="mem0_api_key",
|
||||
org_id="mem0_organization_id",
|
||||
project_id="mem0_project_id"
|
||||
)
|
||||
|
||||
# Define custom criteria with weights
|
||||
retrieval_criteria = [
|
||||
{
|
||||
"name": "joy",
|
||||
"description": "Measure the intensity of positive emotions such as happiness, excitement, or amusement expressed in the sentence. A higher score reflects greater joy.",
|
||||
"weight": 3
|
||||
},
|
||||
{
|
||||
"name": "curiosity",
|
||||
"description": "Assess the extent to which the sentence reflects inquisitiveness, interest in exploring new information, or asking questions. A higher score reflects stronger curiosity.",
|
||||
"weight": 2
|
||||
},
|
||||
{
|
||||
"name": "emotion",
|
||||
"description": "Evaluate the presence and depth of sadness or negative emotional tone, including expressions of disappointment, frustration, or sorrow. A higher score reflects greater sadness.",
|
||||
"weight": 1
|
||||
}
|
||||
]
|
||||
|
||||
# Update project with custom criteria
|
||||
client.update_project(
|
||||
retrieval_criteria=retrieval_criteria
|
||||
)
|
||||
```
|
||||
|
||||
## Using Criteria Retrieval
|
||||
|
||||
After setting up your criteria, you can use them to filter and retrieve memories. Here's an example:
|
||||
|
||||
```python
|
||||
# Add some example memories
|
||||
messages = [
|
||||
{"role": "user", "content": "What a beautiful sunny day! I feel so refreshed and ready to take on anything!"},
|
||||
{"role": "user", "content": "I've always wondered how storms form—what triggers them in the atmosphere?"},
|
||||
{"role": "user", "content": "It's been raining for days, and it just makes everything feel heavier."},
|
||||
{"role": "user", "content": "Finally I get time to draw something today, after a long time!! I am super happy today."}
|
||||
]
|
||||
|
||||
client.add(messages, user_id="alice")
|
||||
|
||||
# Search with criteria-based filtering
|
||||
filters = {
|
||||
"AND": [
|
||||
{"user_id": "alice"}
|
||||
]
|
||||
}
|
||||
results_with_criteria = client.search(
|
||||
query="Why I am feeling happy today?",
|
||||
filters=filters,
|
||||
version="v2"
|
||||
)
|
||||
|
||||
# Standard search without criteria filtering
|
||||
results_without_criteria = client.search(
|
||||
query="Why I am feeling happy today?",
|
||||
user_id="alice"
|
||||
)
|
||||
```
|
||||
|
||||
## Search Results Comparison
|
||||
|
||||
Let's compare the results from criteria-based retrieval versus standard retrieval to see how the emotional criteria affects ranking:
|
||||
|
||||
### Search Results (with Criteria)
|
||||
```python
|
||||
[
|
||||
{
|
||||
"memory": "User feels refreshed and ready to take on anything on a beautiful sunny day",
|
||||
"score": 0.666,
|
||||
...
|
||||
},
|
||||
{
|
||||
"memory": "User finally has time to draw something after a long time",
|
||||
"score": 0.616,
|
||||
...
|
||||
},
|
||||
{
|
||||
"memory": "User is happy today",
|
||||
"score": 0.500,
|
||||
...
|
||||
},
|
||||
{
|
||||
"memory": "User is curious about how storms form and what triggers them in the atmosphere.",
|
||||
"score": 0.400,
|
||||
...
|
||||
},
|
||||
{
|
||||
"memory": "It has been raining for days, making everything feel heavier.",
|
||||
"score": 0.116,
|
||||
...
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Search Results (without Criteria)
|
||||
```python
|
||||
[
|
||||
{
|
||||
"memory": "User is happy today",
|
||||
"score": 0.607,
|
||||
...
|
||||
},
|
||||
{
|
||||
"memory": "User feels refreshed and ready to take on anything on a beautiful sunny day",
|
||||
"score": 0.512,
|
||||
...
|
||||
},
|
||||
{
|
||||
"memory": "It has been raining for days, making everything feel heavier.",
|
||||
"score": 0.4617,
|
||||
...
|
||||
},
|
||||
{
|
||||
"memory": "User is curious about how storms form and what triggers them in the atmosphere.",
|
||||
"score": 0.340,
|
||||
...
|
||||
},
|
||||
{
|
||||
"memory": "User finally has time to draw something after a long time",
|
||||
"score": 0.336,
|
||||
...
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Looking at the example results above, we can see how criteria-based filtering affects the output:
|
||||
|
||||
1. **Memory Ordering**: With criteria, memories with high joy scores (like feeling refreshed and drawing) are ranked higher, while without criteria, the most relevant memory ("User is happy today") comes first.
|
||||
|
||||
2. **Score Distribution**: With criteria, scores are more spread out (0.116 to 0.666) and reflect the criteria weights, while without criteria, scores are more clustered (0.336 to 0.607) and based purely on relevance.
|
||||
|
||||
3. **Negative Content**: With criteria, the negative memory about rain has a much lower score (0.116) due to the emotion criteria, while without criteria it maintains a relatively high score (0.4617) due to its relevance.
|
||||
|
||||
4. **Curiosity Content**: The storm-related memory gets a moderate score (0.400) with criteria due to the curiosity weighting, while without criteria it's ranked lower (0.340) as it's less relevant to the happiness query.
|
||||
|
||||
## Key Differences
|
||||
|
||||
1. **Scoring**: With criteria, normalized scores (0-1) are used based on custom criteria weights, while without criteria, standard relevance scoring is used
|
||||
|
||||
2. **Ordering**: With criteria, memories are first retrieved by relevance, then criteria-based filtering and prioritization is applied, while without criteria, ordering is solely by relevance
|
||||
|
||||
3. **Filtering**: With criteria, post-retrieval filtering based on custom criteria (joy, curiosity, etc.) is available, which isn't available without criteria
|
||||
|
||||
<Note>
|
||||
When no custom criteria are specified, the search will default to standard relevance-based retrieval. In this case, results are returned based solely on their relevance to the query, without any additional filtering or prioritization that would normally be applied through criteria.
|
||||
</Note>
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Criteria Definition**: Define custom criteria with names, descriptions, and weights
|
||||
2. **Project Configuration**: Apply these criteria at the project level
|
||||
3. **Memory Retrieval**: Use v2 search with filters to retrieve memories based on your criteria
|
||||
4. **Weighted Scoring**: Memories are scored based on the defined criteria weights
|
||||
|
||||
<Note>
|
||||
Criteria retrieval is currently supported only in search v2. Make sure to use `version="v2"` when performing searches with custom criteria.
|
||||
</Note>
|
||||
|
||||
If you have any questions, please feel free to reach out to us using one of the following methods:
|
||||
|
||||
<Snippet file="get-help.mdx" />
|
||||
@@ -61,8 +61,8 @@ import { MemoryClient } from "mem0";
|
||||
|
||||
const client = new MemoryClient({
|
||||
apiKey: "your-api-key",
|
||||
orgId: "your-org-id",
|
||||
projectId: "your-project-id"
|
||||
org_id: "your-org-id",
|
||||
project_id: "your-project-id"
|
||||
});
|
||||
|
||||
const messages = [
|
||||
@@ -74,10 +74,10 @@ const messages = [
|
||||
// Enable graph memory when adding
|
||||
await client.add({
|
||||
messages,
|
||||
userId: "joseph",
|
||||
user_id: "joseph",
|
||||
version: "v1",
|
||||
enableGraph: true,
|
||||
outputFormat: "v1.1"
|
||||
enable_graph: true,
|
||||
output_format: "v1.1"
|
||||
});
|
||||
```
|
||||
|
||||
@@ -142,9 +142,9 @@ print(results)
|
||||
// Search with graph memory enabled
|
||||
const results = await client.search({
|
||||
query: "what is my name?",
|
||||
userId: "joseph",
|
||||
enableGraph: true,
|
||||
outputFormat: "v1.1"
|
||||
user_id: "joseph",
|
||||
enable_graph: true,
|
||||
output_format: "v1.1"
|
||||
});
|
||||
|
||||
console.log(results);
|
||||
@@ -211,9 +211,9 @@ print(memories)
|
||||
```javascript JavaScript
|
||||
// Get all memories with graph context
|
||||
const memories = await client.getAll({
|
||||
userId: "joseph",
|
||||
enableGraph: true,
|
||||
outputFormat: "v1.1"
|
||||
user_id: "joseph",
|
||||
enable_graph: true,
|
||||
output_format: "v1.1"
|
||||
});
|
||||
|
||||
console.log(memories);
|
||||
@@ -131,12 +131,14 @@ curl -X POST "https://api.mem0.ai/v1/memories/export/" \
|
||||
|
||||
### Retrieve Export
|
||||
|
||||
Once the export job is complete, you can retrieve the structured data:
|
||||
Once the export job is complete, you can retrieve the structured data in two ways:
|
||||
|
||||
#### Using Filters
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Python
|
||||
# Corrected date range (assuming you meant July 10 to July 20)
|
||||
# Retrieve using filters
|
||||
filters = {
|
||||
"AND": [
|
||||
{"created_at": {"gte": "2024-07-10", "lte": "2024-07-20"}},
|
||||
@@ -148,9 +150,27 @@ response = client.get_memory_export(filters=filters)
|
||||
print(response)
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X GET "https://api.mem0.ai/v1/memories/export/?user_id=alice" \
|
||||
-H "Authorization: Token your-api-key"
|
||||
```json Output
|
||||
{
|
||||
"full_name": "John Doe",
|
||||
"current_role": "Senior Software Engineer",
|
||||
"years_experience": 8,
|
||||
"employment_status": "full_time",
|
||||
"education_level": "masters",
|
||||
"skills": ["Python", "AWS", "Machine Learning"]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
#### Using Export ID
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Python
|
||||
# Retrieve using export ID
|
||||
response = client.get_memory_export(memory_export_id="550e8400-e29b-41d4-a716-446655440000")
|
||||
print(response)
|
||||
```
|
||||
|
||||
```json Output
|
||||
@@ -39,9 +39,16 @@ When adding new memories, you can specify a custom timestamp to indicate when th
|
||||
<CodeGroup>
|
||||
|
||||
```python Python
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from mem0 import MemoryClient
|
||||
|
||||
os.environ["MEM0_API_KEY"] = "your-api-key"
|
||||
|
||||
client = MemoryClient()
|
||||
|
||||
# Get the current time
|
||||
current_time = datetime.now()
|
||||
|
||||
@@ -52,10 +59,16 @@ five_days_ago = current_time - timedelta(days=5)
|
||||
unix_timestamp = int(five_days_ago.timestamp())
|
||||
|
||||
# Add memory with custom timestamp
|
||||
client.add("I'm travelling to SF", user_id="user1", timestamp=unix_timestamp)
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm travelling to SF"}
|
||||
]
|
||||
client.add(messages, user_id="user1", timestamp=unix_timestamp)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
import MemoryClient from 'mem0ai';
|
||||
const client = new MemoryClient({ apiKey: 'your-api-key' });
|
||||
|
||||
// Get the current time
|
||||
const currentTime = new Date();
|
||||
|
||||
@@ -67,7 +80,10 @@ fiveDaysAgo.setDate(currentTime.getDate() - 5);
|
||||
const unixTimestamp = Math.floor(fiveDaysAgo.getTime() / 1000);
|
||||
|
||||
// Add memory with custom timestamp
|
||||
client.add("I'm travelling to SF", { user_id: "user1", timestamp: unixTimestamp })
|
||||
const messages = [
|
||||
{"role": "user", "content": "I'm travelling to SF"}
|
||||
]
|
||||
client.add(messages, { user_id: "user1", timestamp: unixTimestamp })
|
||||
.then(response => console.log(response))
|
||||
.catch(error => console.error(error));
|
||||
```
|
||||
@@ -109,14 +125,20 @@ For example, to create a memory with a timestamp of January 1, 2023:
|
||||
# January 1, 2023 timestamp
|
||||
january_2023_timestamp = 1672531200 # Unix timestamp for 2023-01-01 00:00:00 UTC
|
||||
|
||||
client.add("Important historical information", user_id="user1", timestamp=january_2023_timestamp)
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm travelling to SF"}
|
||||
]
|
||||
client.add(messages, user_id="user1", timestamp=january_2023_timestamp)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
// January 1, 2023 timestamp
|
||||
const january2023Timestamp = 1672531200; // Unix timestamp for 2023-01-01 00:00:00 UTC
|
||||
|
||||
client.add("Important historical information", { user_id: "user1", timestamp: january2023Timestamp })
|
||||
const messages = [
|
||||
{"role": "user", "content": "I'm travelling to SF"}
|
||||
]
|
||||
client.add(messages, { user_id: "user1", timestamp: january2023Timestamp })
|
||||
.then(response => console.log(response))
|
||||
.catch(error => console.error(error));
|
||||
```
|
||||
+224
-18
@@ -64,7 +64,10 @@ client = AsyncMemoryClient()
|
||||
|
||||
|
||||
async def main():
|
||||
response = await client.add("I'm travelling to SF", user_id="john")
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm travelling to SF"}
|
||||
]
|
||||
response = await client.add(messages, user_id="john")
|
||||
print(response)
|
||||
|
||||
await main()
|
||||
@@ -139,7 +142,20 @@ curl -X POST "https://api.mem0.ai/v1/memories/" \
|
||||
</CodeGroup>
|
||||
|
||||
<Note>
|
||||
Messages passed along with `user_id`, `run_id`, or `app_id` are stored as user memories, while messages from the assistant are excluded from memory. To store messages for the assistant, use `agent_id` exclusively and avoid including other IDs, such as user_id, alongside it. This ensures the memory is properly attributed to the assistant.
|
||||
When passing `user_id`, memories are primarily created based on user messages, but may be influenced by assistant messages for contextual understanding. For example, in a conversation about food preferences, both the user's stated preferences and their responses to the assistant's questions would form user memories. Similarly, when using `agent_id`, assistant messages are prioritized, but user messages might influence the agent's memories based on context. This approach ensures comprehensive memory creation while maintaining appropriate attribution to either users or agents.
|
||||
|
||||
**Example:**
|
||||
```
|
||||
User: My favorite cuisine is Italian
|
||||
Assistant: Nice! What about Indian cuisine?
|
||||
User: Don't like it much since I cannot eat spicy food
|
||||
|
||||
Resulting user memories:
|
||||
memory1 - Likes Italian food
|
||||
memory2 - Doesn't like Indian food since cannot eat spicy
|
||||
|
||||
(memory2 comes from user's response about Indian cuisine)
|
||||
```
|
||||
</Note>
|
||||
|
||||
<Note>Metadata allows you to store structured information (location, timestamp, user state) with memories. Add it during creation to enable precise filtering and retrieval during searches.</Note>
|
||||
@@ -269,10 +285,12 @@ curl -X POST "https://api.mem0.ai/v1/memories/" \
|
||||
</Note>
|
||||
|
||||
#### Long-term memory for both users and agents
|
||||
When you provide both `user_id` and `agent_id`, Mem0 will store memories with both identifiers attached:
|
||||
- Each memory will be tagged with both the specified `user_id` and `agent_id`
|
||||
- During retrieval, you'll need to provide both IDs to access the memories
|
||||
- This enables tracking the full context of conversations between specific users and agents
|
||||
When you provide both `user_id` and `agent_id`, Mem0 will store memories for both identifiers separately:
|
||||
- Memories from messages with `"role": "user"` are automatically tagged with the provided `user_id`
|
||||
- Memories from messages with `"role": "assistant"` are automatically tagged with the provided `agent_id`
|
||||
- During retrieval, you can provide either `user_id` or `agent_id` to access the respective memories
|
||||
- You can continuously enrich existing memory collections by adding new memories to the same `user_id` or `agent_id` in subsequent API calls, either together or separately, allowing for progressive memory building over time
|
||||
- This dual-tagging approach enables personalized experiences for both users and AI agents in your application
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
@@ -314,11 +332,13 @@ curl -X POST "https://api.mem0.ai/v1/memories/" \
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
// memory from user1
|
||||
"id": "c57abfa2-f0ac-48af-896a-21728dbcecee0",
|
||||
"data": {"memory": "Travelling to San Francisco"},
|
||||
"event": "ADD"
|
||||
},
|
||||
{
|
||||
// memory from agent1
|
||||
"id": "0e8c003f-7db7-426a-9fdc-a46f9331a0c2",
|
||||
"data": {"memory": "Going to Dubai next month"},
|
||||
"event": "ADD"
|
||||
@@ -449,7 +469,7 @@ Example 1: Search using user_id and agent_id filters
|
||||
```python Python
|
||||
query = "What do you know about me?"
|
||||
filters = {
|
||||
"AND":[
|
||||
"OR":[
|
||||
{
|
||||
"user_id":"alex"
|
||||
},
|
||||
@@ -469,7 +489,7 @@ client.search(query, version="v2", filters=filters)
|
||||
```javascript JavaScript
|
||||
const query = "What do you know about me?";
|
||||
const filters = {
|
||||
"AND":[
|
||||
"OR":[
|
||||
{
|
||||
"user_id":"alex"
|
||||
},
|
||||
@@ -495,7 +515,7 @@ curl -X POST "https://api.mem0.ai/v1/memories/search/?version=v2" \
|
||||
-d '{
|
||||
"query": "What do you know about me?",
|
||||
"filters": {
|
||||
"AND": [
|
||||
"OR": [
|
||||
{
|
||||
"user_id": "alex"
|
||||
},
|
||||
@@ -674,6 +694,77 @@ curl -X POST "https://api.mem0.ai/v1/memories/search/?version=v2" \
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
Example 4: Search using NOT filters
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
query = "What do you know about me?"
|
||||
filters = {
|
||||
"NOT": [
|
||||
{
|
||||
"categories": {
|
||||
"contains": "food_preferences"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
client.search(query, version="v2", filters=filters)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
const query = "What do you know about me?";
|
||||
const filters = {
|
||||
"NOT": [
|
||||
{
|
||||
"categories": {
|
||||
"contains": "food_preferences"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
client.search(query, { version: "v2", filters })
|
||||
.then(results => console.log(results))
|
||||
.catch(error => console.error(error));
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X POST "https://api.mem0.ai/v1/memories/search/?version=v2" \
|
||||
-H "Authorization: Token your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "What do you know about me?",
|
||||
"filters": {
|
||||
"NOT": [
|
||||
{
|
||||
"categories": {
|
||||
"contains": "food_preferences"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
```json Output
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"id": "123abc-d456-7890-efgh-ijklmnopqrst",
|
||||
"memory": "Lives in San Francisco",
|
||||
"user_id": "alex",
|
||||
"metadata": null,
|
||||
"categories": ["location"],
|
||||
"immutable": false,
|
||||
"expiration_date": null,
|
||||
"created_at": "2024-07-20T01:30:36.275141-07:00",
|
||||
"updated_at": "2024-07-20T01:30:36.275172-07:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
### 4.3 Get All Users
|
||||
|
||||
@@ -1291,6 +1382,121 @@ curl -X GET "https://api.mem0.ai/v1/memories/?version=v2&page=1&page_size=50" \
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
Example 3: Get all memories using NOT filters
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
filters = {
|
||||
"NOT": [
|
||||
{
|
||||
"categories": {
|
||||
"contains": "food_preferences"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Default (No Pagination)
|
||||
client.get_all(version="v2", filters=filters)
|
||||
|
||||
# Pagination (You can also use the page and page_size parameters)
|
||||
client.get_all(version="v2", filters=filters, page=1, page_size=50)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
const filters = {
|
||||
"NOT": [
|
||||
{
|
||||
"categories": {
|
||||
"contains": "food_preferences"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Default (No Pagination)
|
||||
client.getAll({ version: "v2", filters })
|
||||
.then(memories => console.log(memories))
|
||||
.catch(error => console.error(error));
|
||||
|
||||
// Pagination (You can also use the page and page_size parameters)
|
||||
client.getAll({ version: "v2", filters, page: 1, page_size: 50 })
|
||||
.then(memories => console.log(memories))
|
||||
.catch(error => console.error(error));
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
# Default (No Pagination)
|
||||
curl -X GET "https://api.mem0.ai/v1/memories/?version=v2" \
|
||||
-H "Authorization: Token your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"filters": {
|
||||
"NOT": [
|
||||
{
|
||||
"categories": {
|
||||
"contains": "food_preferences"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}'
|
||||
|
||||
# Pagination (You can also use the page and page_size parameters)
|
||||
curl -X GET "https://api.mem0.ai/v1/memories/?version=v2&page=1&page_size=50" \
|
||||
-H "Authorization: Token your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"filters": {
|
||||
"NOT": [
|
||||
{
|
||||
"categories": {
|
||||
"contains": "food_preferences"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
```json Output
|
||||
[
|
||||
{
|
||||
"id": "789xyz-e012-3456-fghi-jklmnopqrstu",
|
||||
"memory": "Works as a software engineer",
|
||||
"user_id": "alex",
|
||||
"metadata": {"job": "tech"},
|
||||
"categories": ["work"],
|
||||
"immutable": false,
|
||||
"expiration_date": null,
|
||||
"created_at": "2024-07-20T01:30:36.275141-07:00",
|
||||
"updated_at": "2024-07-20T01:30:36.275172-07:00"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
```json Output (Paginated)
|
||||
{
|
||||
"count": 1,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": "789xyz-e012-3456-fghi-jklmnopqrstu",
|
||||
"memory": "Works as a software engineer",
|
||||
"user_id": "alex",
|
||||
"metadata": {"job": "tech"},
|
||||
"categories": ["work"],
|
||||
"immutable": false,
|
||||
"expiration_date": null,
|
||||
"created_at": "2024-07-20T01:30:36.275141-07:00",
|
||||
"updated_at": "2024-07-20T01:30:36.275172-07:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
### 4.5 Memory History
|
||||
|
||||
@@ -1513,7 +1719,7 @@ client.delete_users({ user_id: "alex" })
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X DELETE "https://api.mem0.ai/v1/entities/?user_id=alex" \
|
||||
curl -X DELETE "https://api.mem0.ai/v2/entities/user/alex" \
|
||||
-H "Authorization: Token your-api-key"
|
||||
```
|
||||
|
||||
@@ -1531,12 +1737,6 @@ curl -X DELETE "https://api.mem0.ai/v1/entities/?user_id=alex" \
|
||||
client.reset()
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
client.reset()
|
||||
.then(result => console.log(result))
|
||||
.catch(error => console.error(error));
|
||||
```
|
||||
|
||||
```json Output
|
||||
{'message': 'Client reset successful. All users and memories deleted.'}
|
||||
```
|
||||
@@ -1549,11 +1749,17 @@ Fun fact: You can also delete the memory using the `add()` method by passing a n
|
||||
<CodeGroup>
|
||||
|
||||
```python Python
|
||||
client.add("Delete all of my food preferences", user_id="alex")
|
||||
messages = [
|
||||
{"role": "user", "content": "Delete all of my food preferences"}
|
||||
]
|
||||
client.add(messages, user_id="alex")
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
client.add("Delete all of my food preferences", { user_id: "alex" })
|
||||
const messages = [
|
||||
{"role": "user", "content": "Delete all of my food preferences"}
|
||||
]
|
||||
client.add(messages, { user_id: "alex" })
|
||||
.then(result => console.log(result))
|
||||
.catch(error => console.error(error));
|
||||
```
|
||||
|
||||
@@ -5,6 +5,9 @@ iconType: "solid"
|
||||
---
|
||||
|
||||
<Snippet file="paper-release.mdx" />
|
||||
<Note type="info">
|
||||
🎉 We're excited to announce that Claude 4 is now available with Mem0! Check it out [here](components/llms/models/anthropic).
|
||||
</Note>
|
||||
|
||||
|
||||
Mem0 offers two powerful ways to leverage our technology: [our managed platform](#mem0-platform-managed-solution) and [our open source solution](#mem0-open-source).
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ install:
|
||||
# TODO: use a more efficient way to install these packages
|
||||
install_all:
|
||||
poetry install --all-extras
|
||||
poetry run pip install pinecone-text pinecone-client langchain-anthropic "unstructured[local-inference, all-docs]" ollama langchain_together==0.1.3 \
|
||||
poetry run pip install ruff==0.6.9 pinecone-text pinecone-client langchain-anthropic "unstructured[local-inference, all-docs]" ollama langchain_together==0.1.3 \
|
||||
langchain_cohere==0.1.5 deepgram-sdk==3.2.7 langchain-huggingface psutil clarifai==10.0.1 flask==2.3.3 twilio==8.5.0 fastapi-poe==0.0.16 discord==2.3.2 \
|
||||
slack-sdk==3.21.3 huggingface_hub==0.23.0 gitpython==3.1.38 yt_dlp==2023.11.14 PyGithub==1.59.1 feedparser==6.0.10 newspaper3k==0.2.8 listparser==0.19 \
|
||||
modal==0.56.4329 dropbox==11.36.2 boto3==1.34.20 youtube-transcript-api==0.6.1 pytube==15.0.0 beautifulsoup4==4.12.3
|
||||
|
||||
@@ -24,7 +24,7 @@ def merge_metadata_dict(left: Optional[dict[str, Any]], right: Optional[dict[str
|
||||
for k, v in right.items():
|
||||
if k not in merged:
|
||||
merged[k] = v
|
||||
elif type(merged[k]) != type(v):
|
||||
elif type(merged[k]) is not type(v):
|
||||
raise ValueError(f'additional_kwargs["{k}"] already exists in this message,' " but with a different type.")
|
||||
elif isinstance(merged[k], str):
|
||||
merged[k] += v
|
||||
|
||||
@@ -22,10 +22,7 @@ build-backend = "poetry.core.masonry.api"
|
||||
requires = ["poetry-core"]
|
||||
|
||||
[tool.ruff]
|
||||
select = ["ASYNC", "E", "F"]
|
||||
ignore = []
|
||||
fixable = ["ALL"]
|
||||
unfixable = []
|
||||
line-length = 120
|
||||
exclude = [
|
||||
".bzr",
|
||||
".direnv",
|
||||
@@ -49,17 +46,22 @@ exclude = [
|
||||
"node_modules",
|
||||
"venv"
|
||||
]
|
||||
line-length = 120
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
target-version = "py38"
|
||||
|
||||
[tool.ruff.mccabe]
|
||||
max-complexity = 10
|
||||
[tool.ruff.lint]
|
||||
select = ["ASYNC", "E", "F"]
|
||||
ignore = []
|
||||
fixable = ["ALL"]
|
||||
unfixable = []
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
|
||||
[tool.ruff.per-file-ignores]
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"embedchain/__init__.py" = ["E401"]
|
||||
|
||||
[tool.ruff.lint.mccabe]
|
||||
max-complexity = 10
|
||||
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
target-version = ["py38", "py39", "py310", "py311"]
|
||||
|
||||
+34
-34
@@ -1,11 +1,12 @@
|
||||
import json
|
||||
import argparse
|
||||
from metrics.utils import calculate_metrics, calculate_bleu_scores
|
||||
from metrics.llm_judge import evaluate_llm_judge
|
||||
from collections import defaultdict
|
||||
from tqdm import tqdm
|
||||
import concurrent.futures
|
||||
import json
|
||||
import threading
|
||||
from collections import defaultdict
|
||||
|
||||
from metrics.llm_judge import evaluate_llm_judge
|
||||
from metrics.utils import calculate_bleu_scores, calculate_metrics
|
||||
from tqdm import tqdm
|
||||
|
||||
|
||||
def process_item(item_data):
|
||||
@@ -13,46 +14,47 @@ def process_item(item_data):
|
||||
local_results = defaultdict(list)
|
||||
|
||||
for item in v:
|
||||
gt_answer = str(item['answer'])
|
||||
pred_answer = str(item['response'])
|
||||
category = str(item['category'])
|
||||
question = str(item['question'])
|
||||
gt_answer = str(item["answer"])
|
||||
pred_answer = str(item["response"])
|
||||
category = str(item["category"])
|
||||
question = str(item["question"])
|
||||
|
||||
# Skip category 5
|
||||
if category == '5':
|
||||
if category == "5":
|
||||
continue
|
||||
|
||||
metrics = calculate_metrics(pred_answer, gt_answer)
|
||||
bleu_scores = calculate_bleu_scores(pred_answer, gt_answer)
|
||||
llm_score = evaluate_llm_judge(question, gt_answer, pred_answer)
|
||||
|
||||
local_results[k].append({
|
||||
"question": question,
|
||||
"answer": gt_answer,
|
||||
"response": pred_answer,
|
||||
"category": category,
|
||||
"bleu_score": bleu_scores["bleu1"],
|
||||
"f1_score": metrics["f1"],
|
||||
"llm_score": llm_score
|
||||
})
|
||||
local_results[k].append(
|
||||
{
|
||||
"question": question,
|
||||
"answer": gt_answer,
|
||||
"response": pred_answer,
|
||||
"category": category,
|
||||
"bleu_score": bleu_scores["bleu1"],
|
||||
"f1_score": metrics["f1"],
|
||||
"llm_score": llm_score,
|
||||
}
|
||||
)
|
||||
|
||||
return local_results
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Evaluate RAG results')
|
||||
parser.add_argument('--input_file', type=str,
|
||||
default="results/rag_results_500_k1.json",
|
||||
help='Path to the input dataset file')
|
||||
parser.add_argument('--output_file', type=str,
|
||||
default="evaluation_metrics.json",
|
||||
help='Path to save the evaluation results')
|
||||
parser.add_argument('--max_workers', type=int, default=10,
|
||||
help='Maximum number of worker threads')
|
||||
parser = argparse.ArgumentParser(description="Evaluate RAG results")
|
||||
parser.add_argument(
|
||||
"--input_file", type=str, default="results/rag_results_500_k1.json", help="Path to the input dataset file"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output_file", type=str, default="evaluation_metrics.json", help="Path to save the evaluation results"
|
||||
)
|
||||
parser.add_argument("--max_workers", type=int, default=10, help="Maximum number of worker threads")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.input_file, 'r') as f:
|
||||
with open(args.input_file, "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
results = defaultdict(list)
|
||||
@@ -60,18 +62,16 @@ def main():
|
||||
|
||||
# Use ThreadPoolExecutor with specified workers
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=args.max_workers) as executor:
|
||||
futures = [executor.submit(process_item, item_data)
|
||||
for item_data in data.items()]
|
||||
futures = [executor.submit(process_item, item_data) for item_data in data.items()]
|
||||
|
||||
for future in tqdm(concurrent.futures.as_completed(futures),
|
||||
total=len(futures)):
|
||||
for future in tqdm(concurrent.futures.as_completed(futures), total=len(futures)):
|
||||
local_results = future.result()
|
||||
with results_lock:
|
||||
for k, items in local_results.items():
|
||||
results[k].extend(items)
|
||||
|
||||
# Save results to JSON file
|
||||
with open(args.output_file, 'w') as f:
|
||||
with open(args.output_file, "w") as f:
|
||||
json.dump(results, f, indent=4)
|
||||
|
||||
print(f"Results saved to {args.output_file}")
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import pandas as pd
|
||||
import json
|
||||
|
||||
import pandas as pd
|
||||
|
||||
# Load the evaluation metrics data
|
||||
with open('evaluation_metrics.json', 'r') as f:
|
||||
with open("evaluation_metrics.json", "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Flatten the data into a list of question items
|
||||
@@ -14,28 +15,20 @@ for key in data:
|
||||
df = pd.DataFrame(all_items)
|
||||
|
||||
# Convert category to numeric type
|
||||
df['category'] = pd.to_numeric(df['category'])
|
||||
df["category"] = pd.to_numeric(df["category"])
|
||||
|
||||
# Calculate mean scores by category
|
||||
result = df.groupby('category').agg({
|
||||
'bleu_score': 'mean',
|
||||
'f1_score': 'mean',
|
||||
'llm_score': 'mean'
|
||||
}).round(4)
|
||||
result = df.groupby("category").agg({"bleu_score": "mean", "f1_score": "mean", "llm_score": "mean"}).round(4)
|
||||
|
||||
# Add count of questions per category
|
||||
result['count'] = df.groupby('category').size()
|
||||
result["count"] = df.groupby("category").size()
|
||||
|
||||
# Print the results
|
||||
print("Mean Scores Per Category:")
|
||||
print(result)
|
||||
|
||||
# Calculate overall means
|
||||
overall_means = df.agg({
|
||||
'bleu_score': 'mean',
|
||||
'f1_score': 'mean',
|
||||
'llm_score': 'mean'
|
||||
}).round(4)
|
||||
overall_means = df.agg({"bleu_score": "mean", "f1_score": "mean", "llm_score": "mean"}).round(4)
|
||||
|
||||
print("\nOverall Mean Scores:")
|
||||
print(overall_means)
|
||||
print(overall_means)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from openai import OpenAI
|
||||
import argparse
|
||||
import json
|
||||
from collections import defaultdict
|
||||
|
||||
import numpy as np
|
||||
import argparse
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI()
|
||||
|
||||
@@ -32,35 +33,34 @@ Do NOT include both CORRECT and WRONG in your response, or it will break the eva
|
||||
Just return the label CORRECT or WRONG in a json format with the key as "label".
|
||||
"""
|
||||
|
||||
|
||||
def evaluate_llm_judge(question, gold_answer, generated_answer):
|
||||
"""Evaluate the generated answer against the gold answer using an LLM judge."""
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o-mini",
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": ACCURACY_PROMPT.format(
|
||||
question=question,
|
||||
gold_answer=gold_answer,
|
||||
generated_answer=generated_answer
|
||||
)
|
||||
}],
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": ACCURACY_PROMPT.format(
|
||||
question=question, gold_answer=gold_answer, generated_answer=generated_answer
|
||||
),
|
||||
}
|
||||
],
|
||||
response_format={"type": "json_object"},
|
||||
temperature=0.0
|
||||
temperature=0.0,
|
||||
)
|
||||
label = json.loads(response.choices[0].message.content)['label']
|
||||
label = json.loads(response.choices[0].message.content)["label"]
|
||||
return 1 if label == "CORRECT" else 0
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function to evaluate RAG results using LLM judge."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Evaluate RAG results using LLM judge'
|
||||
)
|
||||
parser = argparse.ArgumentParser(description="Evaluate RAG results using LLM judge")
|
||||
parser.add_argument(
|
||||
'--input_file',
|
||||
"--input_file",
|
||||
type=str,
|
||||
default="results/default_run_v4_k30_new_graph.json",
|
||||
help='Path to the input dataset file'
|
||||
help="Path to the input dataset file",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -77,10 +77,10 @@ def main():
|
||||
index = 0
|
||||
for k, v in data.items():
|
||||
for x in v:
|
||||
question = x['question']
|
||||
gold_answer = x['answer']
|
||||
generated_answer = x['response']
|
||||
category = x['category']
|
||||
question = x["question"]
|
||||
gold_answer = x["answer"]
|
||||
generated_answer = x["response"]
|
||||
category = x["category"]
|
||||
|
||||
# Skip category 5
|
||||
if int(category) == 5:
|
||||
@@ -91,13 +91,15 @@ def main():
|
||||
LLM_JUDGE[category].append(label)
|
||||
|
||||
# Store the results
|
||||
RESULTS[index].append({
|
||||
"question": question,
|
||||
"gt_answer": gold_answer,
|
||||
"response": generated_answer,
|
||||
"category": category,
|
||||
"llm_label": label
|
||||
})
|
||||
RESULTS[index].append(
|
||||
{
|
||||
"question": question,
|
||||
"gt_answer": gold_answer,
|
||||
"response": generated_answer,
|
||||
"category": category,
|
||||
"llm_label": label,
|
||||
}
|
||||
)
|
||||
|
||||
# Save intermediate results
|
||||
with open(output_path, "w") as f:
|
||||
@@ -107,8 +109,7 @@ def main():
|
||||
print("All categories accuracy:")
|
||||
for cat, results in LLM_JUDGE.items():
|
||||
if results: # Only print if there are results for this category
|
||||
print(f" Category {cat}: {np.mean(results):.4f} "
|
||||
f"({sum(results)}/{len(results)})")
|
||||
print(f" Category {cat}: {np.mean(results):.4f} " f"({sum(results)}/{len(results)})")
|
||||
print("------------------------------------------")
|
||||
index += 1
|
||||
|
||||
|
||||
+61
-74
@@ -3,97 +3,89 @@ Borrowed from https://github.com/WujiangXu/AgenticMemory/blob/main/utils.py
|
||||
|
||||
@article{xu2025mem,
|
||||
title={A-mem: Agentic memory for llm agents},
|
||||
author={Xu, Wujiang and Liang, Zujie and Mei, Kai and Gao, Hang and Tan, Juntao
|
||||
author={Xu, Wujiang and Liang, Zujie and Mei, Kai and Gao, Hang and Tan, Juntao
|
||||
and Zhang, Yongfeng},
|
||||
journal={arXiv preprint arXiv:2502.12110},
|
||||
year={2025}
|
||||
}
|
||||
"""
|
||||
|
||||
import re
|
||||
import string
|
||||
import numpy as np
|
||||
from typing import List, Dict, Union
|
||||
import statistics
|
||||
from collections import defaultdict
|
||||
from rouge_score import rouge_scorer
|
||||
from nltk.translate.bleu_score import sentence_bleu, SmoothingFunction
|
||||
from bert_score import score as bert_score
|
||||
from typing import Dict, List, Union
|
||||
|
||||
import nltk
|
||||
from bert_score import score as bert_score
|
||||
from nltk.translate.bleu_score import SmoothingFunction, sentence_bleu
|
||||
from nltk.translate.meteor_score import meteor_score
|
||||
from rouge_score import rouge_scorer
|
||||
from sentence_transformers import SentenceTransformer
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from openai import OpenAI
|
||||
|
||||
# from load_dataset import load_locomo_dataset, QA, Turn, Session, Conversation
|
||||
from sentence_transformers.util import pytorch_cos_sim
|
||||
|
||||
# Download required NLTK data
|
||||
try:
|
||||
nltk.download('punkt', quiet=True)
|
||||
nltk.download('wordnet', quiet=True)
|
||||
nltk.download("punkt", quiet=True)
|
||||
nltk.download("wordnet", quiet=True)
|
||||
except Exception as e:
|
||||
print(f"Error downloading NLTK data: {e}")
|
||||
|
||||
# Initialize SentenceTransformer model (this will be reused)
|
||||
try:
|
||||
sentence_model = SentenceTransformer('all-MiniLM-L6-v2')
|
||||
sentence_model = SentenceTransformer("all-MiniLM-L6-v2")
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not load SentenceTransformer model: {e}")
|
||||
sentence_model = None
|
||||
|
||||
|
||||
def simple_tokenize(text):
|
||||
"""Simple tokenization function."""
|
||||
# Convert to string if not already
|
||||
text = str(text)
|
||||
return text.lower().replace('.', ' ').replace(',', ' ').replace('!', ' ').replace('?', ' ').split()
|
||||
return text.lower().replace(".", " ").replace(",", " ").replace("!", " ").replace("?", " ").split()
|
||||
|
||||
|
||||
def calculate_rouge_scores(prediction: str, reference: str) -> Dict[str, float]:
|
||||
"""Calculate ROUGE scores for prediction against reference."""
|
||||
scorer = rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'], use_stemmer=True)
|
||||
scorer = rouge_scorer.RougeScorer(["rouge1", "rouge2", "rougeL"], use_stemmer=True)
|
||||
scores = scorer.score(reference, prediction)
|
||||
return {
|
||||
'rouge1_f': scores['rouge1'].fmeasure,
|
||||
'rouge2_f': scores['rouge2'].fmeasure,
|
||||
'rougeL_f': scores['rougeL'].fmeasure
|
||||
"rouge1_f": scores["rouge1"].fmeasure,
|
||||
"rouge2_f": scores["rouge2"].fmeasure,
|
||||
"rougeL_f": scores["rougeL"].fmeasure,
|
||||
}
|
||||
|
||||
|
||||
def calculate_bleu_scores(prediction: str, reference: str) -> Dict[str, float]:
|
||||
"""Calculate BLEU scores with different n-gram settings."""
|
||||
pred_tokens = nltk.word_tokenize(prediction.lower())
|
||||
ref_tokens = [nltk.word_tokenize(reference.lower())]
|
||||
|
||||
|
||||
weights_list = [(1, 0, 0, 0), (0.5, 0.5, 0, 0), (0.33, 0.33, 0.33, 0), (0.25, 0.25, 0.25, 0.25)]
|
||||
smooth = SmoothingFunction().method1
|
||||
|
||||
|
||||
scores = {}
|
||||
for n, weights in enumerate(weights_list, start=1):
|
||||
try:
|
||||
score = sentence_bleu(ref_tokens, pred_tokens, weights=weights, smoothing_function=smooth)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
print(f"Error calculating BLEU score: {e}")
|
||||
score = 0.0
|
||||
scores[f'bleu{n}'] = score
|
||||
|
||||
scores[f"bleu{n}"] = score
|
||||
|
||||
return scores
|
||||
|
||||
|
||||
def calculate_bert_scores(prediction: str, reference: str) -> Dict[str, float]:
|
||||
"""Calculate BERTScore for semantic similarity."""
|
||||
try:
|
||||
P, R, F1 = bert_score([prediction], [reference], lang='en', verbose=False)
|
||||
return {
|
||||
'bert_precision': P.item(),
|
||||
'bert_recall': R.item(),
|
||||
'bert_f1': F1.item()
|
||||
}
|
||||
P, R, F1 = bert_score([prediction], [reference], lang="en", verbose=False)
|
||||
return {"bert_precision": P.item(), "bert_recall": R.item(), "bert_f1": F1.item()}
|
||||
except Exception as e:
|
||||
print(f"Error calculating BERTScore: {e}")
|
||||
return {
|
||||
'bert_precision': 0.0,
|
||||
'bert_recall': 0.0,
|
||||
'bert_f1': 0.0
|
||||
}
|
||||
return {"bert_precision": 0.0, "bert_recall": 0.0, "bert_f1": 0.0}
|
||||
|
||||
|
||||
def calculate_meteor_score(prediction: str, reference: str) -> float:
|
||||
"""Calculate METEOR score for the prediction."""
|
||||
@@ -103,6 +95,7 @@ def calculate_meteor_score(prediction: str, reference: str) -> float:
|
||||
print(f"Error calculating METEOR score: {e}")
|
||||
return 0.0
|
||||
|
||||
|
||||
def calculate_sentence_similarity(prediction: str, reference: str) -> float:
|
||||
"""Calculate sentence embedding similarity using SentenceBERT."""
|
||||
if sentence_model is None:
|
||||
@@ -111,7 +104,7 @@ def calculate_sentence_similarity(prediction: str, reference: str) -> float:
|
||||
# Encode sentences
|
||||
embedding1 = sentence_model.encode([prediction], convert_to_tensor=True)
|
||||
embedding2 = sentence_model.encode([reference], convert_to_tensor=True)
|
||||
|
||||
|
||||
# Calculate cosine similarity
|
||||
similarity = pytorch_cos_sim(embedding1, embedding2).item()
|
||||
return float(similarity)
|
||||
@@ -119,6 +112,7 @@ def calculate_sentence_similarity(prediction: str, reference: str) -> float:
|
||||
print(f"Error calculating sentence similarity: {e}")
|
||||
return 0.0
|
||||
|
||||
|
||||
def calculate_metrics(prediction: str, reference: str) -> Dict[str, float]:
|
||||
"""Calculate comprehensive evaluation metrics for a prediction."""
|
||||
# Handle empty or None values
|
||||
@@ -135,90 +129,83 @@ def calculate_metrics(prediction: str, reference: str) -> Dict[str, float]:
|
||||
"bleu4": 0.0,
|
||||
"bert_f1": 0.0,
|
||||
"meteor": 0.0,
|
||||
"sbert_similarity": 0.0
|
||||
"sbert_similarity": 0.0,
|
||||
}
|
||||
|
||||
|
||||
# Convert to strings if they're not already
|
||||
prediction = str(prediction).strip()
|
||||
reference = str(reference).strip()
|
||||
|
||||
|
||||
# Calculate exact match
|
||||
exact_match = int(prediction.lower() == reference.lower())
|
||||
|
||||
|
||||
# Calculate token-based F1 score
|
||||
pred_tokens = set(simple_tokenize(prediction))
|
||||
ref_tokens = set(simple_tokenize(reference))
|
||||
common_tokens = pred_tokens & ref_tokens
|
||||
|
||||
|
||||
if not pred_tokens or not ref_tokens:
|
||||
f1 = 0.0
|
||||
else:
|
||||
precision = len(common_tokens) / len(pred_tokens)
|
||||
recall = len(common_tokens) / len(ref_tokens)
|
||||
f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0.0
|
||||
|
||||
|
||||
# Calculate all scores
|
||||
rouge_scores = 0 #calculate_rouge_scores(prediction, reference)
|
||||
bleu_scores = calculate_bleu_scores(prediction, reference)
|
||||
bert_scores = 0 # calculate_bert_scores(prediction, reference)
|
||||
meteor = 0 # calculate_meteor_score(prediction, reference)
|
||||
sbert_similarity = 0 # calculate_sentence_similarity(prediction, reference)
|
||||
|
||||
|
||||
# Combine all metrics
|
||||
metrics = {
|
||||
"exact_match": exact_match,
|
||||
"f1": f1,
|
||||
# **rouge_scores,
|
||||
**bleu_scores,
|
||||
# **bert_scores,
|
||||
# "meteor": meteor,
|
||||
# "sbert_similarity": sbert_similarity
|
||||
}
|
||||
|
||||
return metrics
|
||||
|
||||
def aggregate_metrics(all_metrics: List[Dict[str, float]], all_categories: List[int]) -> Dict[str, Dict[str, Union[float, Dict[str, float]]]]:
|
||||
|
||||
def aggregate_metrics(
|
||||
all_metrics: List[Dict[str, float]], all_categories: List[int]
|
||||
) -> Dict[str, Dict[str, Union[float, Dict[str, float]]]]:
|
||||
"""Calculate aggregate statistics for all metrics, split by category."""
|
||||
if not all_metrics:
|
||||
return {}
|
||||
|
||||
|
||||
# Initialize aggregates for overall and per-category metrics
|
||||
aggregates = defaultdict(list)
|
||||
category_aggregates = defaultdict(lambda: defaultdict(list))
|
||||
|
||||
|
||||
# Collect all values for each metric, both overall and per category
|
||||
for metrics, category in zip(all_metrics, all_categories):
|
||||
for metric_name, value in metrics.items():
|
||||
aggregates[metric_name].append(value)
|
||||
category_aggregates[category][metric_name].append(value)
|
||||
|
||||
|
||||
# Calculate statistics for overall metrics
|
||||
results = {
|
||||
"overall": {}
|
||||
}
|
||||
|
||||
results = {"overall": {}}
|
||||
|
||||
for metric_name, values in aggregates.items():
|
||||
results["overall"][metric_name] = {
|
||||
'mean': statistics.mean(values),
|
||||
'std': statistics.stdev(values) if len(values) > 1 else 0.0,
|
||||
'median': statistics.median(values),
|
||||
'min': min(values),
|
||||
'max': max(values),
|
||||
'count': len(values)
|
||||
"mean": statistics.mean(values),
|
||||
"std": statistics.stdev(values) if len(values) > 1 else 0.0,
|
||||
"median": statistics.median(values),
|
||||
"min": min(values),
|
||||
"max": max(values),
|
||||
"count": len(values),
|
||||
}
|
||||
|
||||
|
||||
# Calculate statistics for each category
|
||||
for category in sorted(category_aggregates.keys()):
|
||||
results[f"category_{category}"] = {}
|
||||
for metric_name, values in category_aggregates[category].items():
|
||||
if values: # Only calculate if we have values for this category
|
||||
results[f"category_{category}"][metric_name] = {
|
||||
'mean': statistics.mean(values),
|
||||
'std': statistics.stdev(values) if len(values) > 1 else 0.0,
|
||||
'median': statistics.median(values),
|
||||
'min': min(values),
|
||||
'max': max(values),
|
||||
'count': len(values)
|
||||
"mean": statistics.mean(values),
|
||||
"std": statistics.stdev(values) if len(values) > 1 else 0.0,
|
||||
"median": statistics.median(values),
|
||||
"min": min(values),
|
||||
"max": max(values),
|
||||
"count": len(values),
|
||||
}
|
||||
|
||||
|
||||
return results
|
||||
|
||||
@@ -144,4 +144,4 @@ ANSWER_PROMPT_ZEP = """
|
||||
|
||||
Question: {{question}}
|
||||
Answer:
|
||||
"""
|
||||
"""
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import argparse
|
||||
import os
|
||||
import json
|
||||
|
||||
from src.langmem import LangMemManager
|
||||
from src.memzero.add import MemoryADD
|
||||
from src.memzero.search import MemorySearch
|
||||
from src.utils import TECHNIQUES, METHODS
|
||||
import argparse
|
||||
from src.rag import RAGManager
|
||||
from src.langmem import LangMemManager
|
||||
from src.zep.search import ZepSearch
|
||||
from src.zep.add import ZepAdd
|
||||
from src.openai.predict import OpenAIPredict
|
||||
from src.rag import RAGManager
|
||||
from src.utils import METHODS, TECHNIQUES
|
||||
from src.zep.add import ZepAdd
|
||||
from src.zep.search import ZepSearch
|
||||
|
||||
|
||||
class Experiment:
|
||||
@@ -21,23 +21,15 @@ class Experiment:
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Run memory experiments')
|
||||
parser.add_argument('--technique_type', choices=TECHNIQUES, default='mem0',
|
||||
help='Memory technique to use')
|
||||
parser.add_argument('--method', choices=METHODS, default='add',
|
||||
help='Method to use')
|
||||
parser.add_argument('--chunk_size', type=int, default=1000,
|
||||
help='Chunk size for processing')
|
||||
parser.add_argument('--output_folder', type=str, default='results/',
|
||||
help='Output path for results')
|
||||
parser.add_argument('--top_k', type=int, default=30,
|
||||
help='Number of top memories to retrieve')
|
||||
parser.add_argument('--filter_memories', action='store_true', default=False,
|
||||
help='Whether to filter memories')
|
||||
parser.add_argument('--is_graph', action='store_true', default=False,
|
||||
help='Whether to use graph-based search')
|
||||
parser.add_argument('--num_chunks', type=int, default=1,
|
||||
help='Number of chunks to process')
|
||||
parser = argparse.ArgumentParser(description="Run memory experiments")
|
||||
parser.add_argument("--technique_type", choices=TECHNIQUES, default="mem0", help="Memory technique to use")
|
||||
parser.add_argument("--method", choices=METHODS, default="add", help="Method to use")
|
||||
parser.add_argument("--chunk_size", type=int, default=1000, help="Chunk size for processing")
|
||||
parser.add_argument("--output_folder", type=str, default="results/", help="Output path for results")
|
||||
parser.add_argument("--top_k", type=int, default=30, help="Number of top memories to retrieve")
|
||||
parser.add_argument("--filter_memories", action="store_true", default=False, help="Whether to filter memories")
|
||||
parser.add_argument("--is_graph", action="store_true", default=False, help="Whether to use graph-based search")
|
||||
parser.add_argument("--num_chunks", type=int, default=1, help="Number of chunks to process")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -46,33 +38,18 @@ def main():
|
||||
|
||||
if args.technique_type == "mem0":
|
||||
if args.method == "add":
|
||||
memory_manager = MemoryADD(
|
||||
data_path='dataset/locomo10.json',
|
||||
is_graph=args.is_graph
|
||||
)
|
||||
memory_manager = MemoryADD(data_path="dataset/locomo10.json", is_graph=args.is_graph)
|
||||
memory_manager.process_all_conversations()
|
||||
elif args.method == "search":
|
||||
output_file_path = os.path.join(
|
||||
args.output_folder,
|
||||
f"mem0_results_top_{args.top_k}_filter_{args.filter_memories}_graph_{args.is_graph}.json"
|
||||
f"mem0_results_top_{args.top_k}_filter_{args.filter_memories}_graph_{args.is_graph}.json",
|
||||
)
|
||||
memory_searcher = MemorySearch(
|
||||
output_file_path,
|
||||
args.top_k,
|
||||
args.filter_memories,
|
||||
args.is_graph
|
||||
)
|
||||
memory_searcher.process_data_file('dataset/locomo10.json')
|
||||
memory_searcher = MemorySearch(output_file_path, args.top_k, args.filter_memories, args.is_graph)
|
||||
memory_searcher.process_data_file("dataset/locomo10.json")
|
||||
elif args.technique_type == "rag":
|
||||
output_file_path = os.path.join(
|
||||
args.output_folder,
|
||||
f"rag_results_{args.chunk_size}_k{args.num_chunks}.json"
|
||||
)
|
||||
rag_manager = RAGManager(
|
||||
data_path="dataset/locomo10_rag.json",
|
||||
chunk_size=args.chunk_size,
|
||||
k=args.num_chunks
|
||||
)
|
||||
output_file_path = os.path.join(args.output_folder, f"rag_results_{args.chunk_size}_k{args.num_chunks}.json")
|
||||
rag_manager = RAGManager(data_path="dataset/locomo10_rag.json", chunk_size=args.chunk_size, k=args.num_chunks)
|
||||
rag_manager.process_all_conversations(output_file_path)
|
||||
elif args.technique_type == "langmem":
|
||||
output_file_path = os.path.join(args.output_folder, "langmem_results.json")
|
||||
@@ -85,11 +62,7 @@ def main():
|
||||
elif args.method == "search":
|
||||
output_file_path = os.path.join(args.output_folder, "zep_search_results.json")
|
||||
zep_manager = ZepSearch()
|
||||
zep_manager.process_data_file(
|
||||
"dataset/locomo10.json",
|
||||
"1",
|
||||
output_file_path
|
||||
)
|
||||
zep_manager.process_data_file("dataset/locomo10.json", "1", output_file_path)
|
||||
elif args.technique_type == "openai":
|
||||
output_file_path = os.path.join(args.output_folder, "openai_results.json")
|
||||
openai_manager = OpenAIPredict()
|
||||
|
||||
+46
-54
@@ -1,28 +1,24 @@
|
||||
import json
|
||||
import multiprocessing as mp
|
||||
import os
|
||||
import time
|
||||
from collections import defaultdict
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from jinja2 import Template
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
from langgraph.store.memory import InMemoryStore
|
||||
from langgraph.utils.config import get_store
|
||||
from langmem import (
|
||||
create_manage_memory_tool,
|
||||
create_search_memory_tool
|
||||
)
|
||||
import time
|
||||
import multiprocessing as mp
|
||||
import json
|
||||
from functools import partial
|
||||
import os
|
||||
from tqdm import tqdm
|
||||
from langmem import create_manage_memory_tool, create_search_memory_tool
|
||||
from openai import OpenAI
|
||||
from collections import defaultdict
|
||||
from dotenv import load_dotenv
|
||||
from prompts import ANSWER_PROMPT
|
||||
from tqdm import tqdm
|
||||
|
||||
load_dotenv()
|
||||
|
||||
client = OpenAI()
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
ANSWER_PROMPT_TEMPLATE = Template(ANSWER_PROMPT)
|
||||
|
||||
|
||||
@@ -32,14 +28,12 @@ def get_answer(question, speaker_1_user_id, speaker_1_memories, speaker_2_user_i
|
||||
speaker_1_user_id=speaker_1_user_id,
|
||||
speaker_1_memories=speaker_1_memories,
|
||||
speaker_2_user_id=speaker_2_user_id,
|
||||
speaker_2_memories=speaker_2_memories
|
||||
speaker_2_memories=speaker_2_memories,
|
||||
)
|
||||
|
||||
t1 = time.time()
|
||||
response = client.chat.completions.create(
|
||||
model=os.getenv("MODEL"),
|
||||
messages=[{"role": "system", "content": prompt}],
|
||||
temperature=0.0
|
||||
model=os.getenv("MODEL"), messages=[{"role": "system", "content": prompt}], temperature=0.0
|
||||
)
|
||||
t2 = time.time()
|
||||
return response.choices[0].message.content, t2 - t1
|
||||
@@ -63,7 +57,9 @@ def prompt(state):
|
||||
|
||||
|
||||
class LangMem:
|
||||
def __init__(self,):
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
self.store = InMemoryStore(
|
||||
index={
|
||||
"dims": 1536,
|
||||
@@ -84,18 +80,12 @@ class LangMem:
|
||||
)
|
||||
|
||||
def add_memory(self, message, config):
|
||||
return self.agent.invoke(
|
||||
{"messages": [{"role": "user", "content": message}]},
|
||||
config=config
|
||||
)
|
||||
return self.agent.invoke({"messages": [{"role": "user", "content": message}]}, config=config)
|
||||
|
||||
def search_memory(self, query, config):
|
||||
try:
|
||||
t1 = time.time()
|
||||
response = self.agent.invoke(
|
||||
{"messages": [{"role": "user", "content": query}]},
|
||||
config=config
|
||||
)
|
||||
response = self.agent.invoke({"messages": [{"role": "user", "content": query}]}, config=config)
|
||||
t2 = time.time()
|
||||
return response["messages"][-1].content, t2 - t1
|
||||
except Exception as e:
|
||||
@@ -106,7 +96,7 @@ class LangMem:
|
||||
class LangMemManager:
|
||||
def __init__(self, dataset_path):
|
||||
self.dataset_path = dataset_path
|
||||
with open(self.dataset_path, 'r') as f:
|
||||
with open(self.dataset_path, "r") as f:
|
||||
self.data = json.load(f)
|
||||
|
||||
def process_all_conversations(self, output_file_path):
|
||||
@@ -127,7 +117,7 @@ class LangMemManager:
|
||||
|
||||
# Identify speakers
|
||||
for conv in chat_history:
|
||||
speakers.add(conv['speaker'])
|
||||
speakers.add(conv["speaker"])
|
||||
|
||||
if len(speakers) != 2:
|
||||
raise ValueError(f"Expected 2 speakers, got {len(speakers)}")
|
||||
@@ -138,50 +128,52 @@ class LangMemManager:
|
||||
# Add memories for each message
|
||||
for conv in tqdm(chat_history, desc=f"Processing messages {key}", leave=False):
|
||||
message = f"{conv['timestamp']} | {conv['speaker']}: {conv['text']}"
|
||||
if conv['speaker'] == speaker1:
|
||||
if conv["speaker"] == speaker1:
|
||||
agent1.add_memory(message, config)
|
||||
elif conv['speaker'] == speaker2:
|
||||
elif conv["speaker"] == speaker2:
|
||||
agent2.add_memory(message, config)
|
||||
else:
|
||||
raise ValueError(f"Expected speaker1 or speaker2, got {conv['speaker']}")
|
||||
|
||||
# Process questions
|
||||
for q in tqdm(questions, desc=f"Processing questions {key}", leave=False):
|
||||
category = q['category']
|
||||
category = q["category"]
|
||||
|
||||
if int(category) == 5:
|
||||
continue
|
||||
|
||||
answer = q['answer']
|
||||
question = q['question']
|
||||
answer = q["answer"]
|
||||
question = q["question"]
|
||||
response1, speaker1_memory_time = agent1.search_memory(question, config)
|
||||
response2, speaker2_memory_time = agent2.search_memory(question, config)
|
||||
|
||||
generated_answer, response_time = get_answer(
|
||||
question, speaker1, response1, speaker2, response2
|
||||
)
|
||||
generated_answer, response_time = get_answer(question, speaker1, response1, speaker2, response2)
|
||||
|
||||
result[key].append({
|
||||
"question": question,
|
||||
"answer": answer,
|
||||
"response1": response1,
|
||||
"response2": response2,
|
||||
"category": category,
|
||||
"speaker1_memory_time": speaker1_memory_time,
|
||||
"speaker2_memory_time": speaker2_memory_time,
|
||||
"response_time": response_time,
|
||||
'response': generated_answer
|
||||
})
|
||||
result[key].append(
|
||||
{
|
||||
"question": question,
|
||||
"answer": answer,
|
||||
"response1": response1,
|
||||
"response2": response2,
|
||||
"category": category,
|
||||
"speaker1_memory_time": speaker1_memory_time,
|
||||
"speaker2_memory_time": speaker2_memory_time,
|
||||
"response_time": response_time,
|
||||
"response": generated_answer,
|
||||
}
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
# Use multiprocessing to process conversations in parallel
|
||||
with mp.Pool(processes=10) as pool:
|
||||
results = list(tqdm(
|
||||
pool.imap(process_conversation, list(self.data.items())),
|
||||
total=len(self.data),
|
||||
desc="Processing conversations"
|
||||
))
|
||||
results = list(
|
||||
tqdm(
|
||||
pool.imap(process_conversation, list(self.data.items())),
|
||||
total=len(self.data),
|
||||
desc="Processing conversations",
|
||||
)
|
||||
)
|
||||
|
||||
# Combine results from all workers
|
||||
for result in results:
|
||||
@@ -189,5 +181,5 @@ class LangMemManager:
|
||||
OUTPUT[key].extend(items)
|
||||
|
||||
# Save final results
|
||||
with open(output_file_path, 'w') as f:
|
||||
with open(output_file_path, "w") as f:
|
||||
json.dump(OUTPUT, f, indent=4)
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
from mem0 import MemoryClient
|
||||
import json
|
||||
import time
|
||||
import os
|
||||
import threading
|
||||
from tqdm import tqdm
|
||||
import time
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from tqdm import tqdm
|
||||
|
||||
from mem0 import MemoryClient
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
# Update custom instructions
|
||||
custom_instructions ="""
|
||||
custom_instructions = """
|
||||
Generate personal memories that follow these guidelines:
|
||||
|
||||
1. Each memory should be self-contained with complete context, including:
|
||||
@@ -45,7 +47,7 @@ class MemoryADD:
|
||||
self.mem0_client = MemoryClient(
|
||||
api_key=os.getenv("MEM0_API_KEY"),
|
||||
org_id=os.getenv("MEM0_ORGANIZATION_ID"),
|
||||
project_id=os.getenv("MEM0_PROJECT_ID")
|
||||
project_id=os.getenv("MEM0_PROJECT_ID"),
|
||||
)
|
||||
|
||||
self.mem0_client.update_project(custom_instructions=custom_instructions)
|
||||
@@ -57,15 +59,16 @@ class MemoryADD:
|
||||
self.load_data()
|
||||
|
||||
def load_data(self):
|
||||
with open(self.data_path, 'r') as f:
|
||||
with open(self.data_path, "r") as f:
|
||||
self.data = json.load(f)
|
||||
return self.data
|
||||
|
||||
def add_memory(self, user_id, message, metadata, retries=3):
|
||||
for attempt in range(retries):
|
||||
try:
|
||||
_ = self.mem0_client.add(message, user_id=user_id, version="v2",
|
||||
metadata=metadata, enable_graph=self.is_graph)
|
||||
_ = self.mem0_client.add(
|
||||
message, user_id=user_id, version="v2", metadata=metadata, enable_graph=self.is_graph
|
||||
)
|
||||
return
|
||||
except Exception as e:
|
||||
if attempt < retries - 1:
|
||||
@@ -76,13 +79,13 @@ class MemoryADD:
|
||||
|
||||
def add_memories_for_speaker(self, speaker, messages, timestamp, desc):
|
||||
for i in tqdm(range(0, len(messages), self.batch_size), desc=desc):
|
||||
batch_messages = messages[i:i+self.batch_size]
|
||||
batch_messages = messages[i : i + self.batch_size]
|
||||
self.add_memory(speaker, batch_messages, metadata={"timestamp": timestamp})
|
||||
|
||||
def process_conversation(self, item, idx):
|
||||
conversation = item['conversation']
|
||||
speaker_a = conversation['speaker_a']
|
||||
speaker_b = conversation['speaker_b']
|
||||
conversation = item["conversation"]
|
||||
speaker_a = conversation["speaker_a"]
|
||||
speaker_b = conversation["speaker_b"]
|
||||
|
||||
speaker_a_user_id = f"{speaker_a}_{idx}"
|
||||
speaker_b_user_id = f"{speaker_b}_{idx}"
|
||||
@@ -92,7 +95,7 @@ class MemoryADD:
|
||||
self.mem0_client.delete_all(user_id=speaker_b_user_id)
|
||||
|
||||
for key in conversation.keys():
|
||||
if key in ['speaker_a', 'speaker_b'] or "date" in key or "timestamp" in key:
|
||||
if key in ["speaker_a", "speaker_b"] or "date" in key or "timestamp" in key:
|
||||
continue
|
||||
|
||||
date_time_key = key + "_date_time"
|
||||
@@ -102,10 +105,10 @@ class MemoryADD:
|
||||
messages = []
|
||||
messages_reverse = []
|
||||
for chat in chats:
|
||||
if chat['speaker'] == speaker_a:
|
||||
if chat["speaker"] == speaker_a:
|
||||
messages.append({"role": "user", "content": f"{speaker_a}: {chat['text']}"})
|
||||
messages_reverse.append({"role": "assistant", "content": f"{speaker_a}: {chat['text']}"})
|
||||
elif chat['speaker'] == speaker_b:
|
||||
elif chat["speaker"] == speaker_b:
|
||||
messages.append({"role": "assistant", "content": f"{speaker_b}: {chat['text']}"})
|
||||
messages_reverse.append({"role": "user", "content": f"{speaker_b}: {chat['text']}"})
|
||||
else:
|
||||
@@ -114,11 +117,11 @@ class MemoryADD:
|
||||
# add memories for the two users on different threads
|
||||
thread_a = threading.Thread(
|
||||
target=self.add_memories_for_speaker,
|
||||
args=(speaker_a_user_id, messages, timestamp, "Adding Memories for Speaker A")
|
||||
args=(speaker_a_user_id, messages, timestamp, "Adding Memories for Speaker A"),
|
||||
)
|
||||
thread_b = threading.Thread(
|
||||
target=self.add_memories_for_speaker,
|
||||
args=(speaker_b_user_id, messages_reverse, timestamp, "Adding Memories for Speaker B")
|
||||
args=(speaker_b_user_id, messages_reverse, timestamp, "Adding Memories for Speaker B"),
|
||||
)
|
||||
|
||||
thread_a.start()
|
||||
@@ -132,10 +135,7 @@ class MemoryADD:
|
||||
if not self.data:
|
||||
raise ValueError("No data loaded. Please set data_path and call load_data() first.")
|
||||
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||
futures = [
|
||||
executor.submit(self.process_conversation, item, idx)
|
||||
for idx, item in enumerate(self.data)
|
||||
]
|
||||
futures = [executor.submit(self.process_conversation, item, idx) for idx, item in enumerate(self.data)]
|
||||
|
||||
for future in futures:
|
||||
future.result()
|
||||
future.result()
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from tqdm import tqdm
|
||||
from mem0 import MemoryClient
|
||||
import json
|
||||
import time
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from jinja2 import Template
|
||||
from openai import OpenAI
|
||||
from prompts import ANSWER_PROMPT_GRAPH, ANSWER_PROMPT
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from prompts import ANSWER_PROMPT, ANSWER_PROMPT_GRAPH
|
||||
from tqdm import tqdm
|
||||
|
||||
from mem0 import MemoryClient
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class MemorySearch:
|
||||
|
||||
def __init__(self, output_path='results.json', top_k=10, filter_memories=False, is_graph=False):
|
||||
def __init__(self, output_path="results.json", top_k=10, filter_memories=False, is_graph=False):
|
||||
self.mem0_client = MemoryClient(
|
||||
api_key=os.getenv("MEM0_API_KEY"),
|
||||
org_id=os.getenv("MEM0_ORGANIZATION_ID"),
|
||||
project_id=os.getenv("MEM0_PROJECT_ID")
|
||||
project_id=os.getenv("MEM0_PROJECT_ID"),
|
||||
)
|
||||
self.top_k = top_k
|
||||
self.openai_client = OpenAI()
|
||||
@@ -40,11 +41,18 @@ class MemorySearch:
|
||||
try:
|
||||
if self.is_graph:
|
||||
print("Searching with graph")
|
||||
memories = self.mem0_client.search(query, user_id=user_id, top_k=self.top_k,
|
||||
filter_memories=self.filter_memories, enable_graph=True, output_format='v1.1')
|
||||
memories = self.mem0_client.search(
|
||||
query,
|
||||
user_id=user_id,
|
||||
top_k=self.top_k,
|
||||
filter_memories=self.filter_memories,
|
||||
enable_graph=True,
|
||||
output_format="v1.1",
|
||||
)
|
||||
else:
|
||||
memories = self.mem0_client.search(query, user_id=user_id, top_k=self.top_k,
|
||||
filter_memories=self.filter_memories)
|
||||
memories = self.mem0_client.search(
|
||||
query, user_id=user_id, top_k=self.top_k, filter_memories=self.filter_memories
|
||||
)
|
||||
break
|
||||
except Exception as e:
|
||||
print("Retrying...")
|
||||
@@ -55,64 +63,86 @@ class MemorySearch:
|
||||
|
||||
end_time = time.time()
|
||||
if not self.is_graph:
|
||||
semantic_memories = [{'memory': memory['memory'],
|
||||
'timestamp': memory['metadata']['timestamp'],
|
||||
'score': round(memory['score'], 2)}
|
||||
for memory in memories]
|
||||
semantic_memories = [
|
||||
{
|
||||
"memory": memory["memory"],
|
||||
"timestamp": memory["metadata"]["timestamp"],
|
||||
"score": round(memory["score"], 2),
|
||||
}
|
||||
for memory in memories
|
||||
]
|
||||
graph_memories = None
|
||||
else:
|
||||
semantic_memories = [{'memory': memory['memory'],
|
||||
'timestamp': memory['metadata']['timestamp'],
|
||||
'score': round(memory['score'], 2)} for memory in memories['results']]
|
||||
graph_memories = [{"source": relation['source'], "relationship": relation['relationship'], "target": relation['target']} for relation in memories['relations']]
|
||||
semantic_memories = [
|
||||
{
|
||||
"memory": memory["memory"],
|
||||
"timestamp": memory["metadata"]["timestamp"],
|
||||
"score": round(memory["score"], 2),
|
||||
}
|
||||
for memory in memories["results"]
|
||||
]
|
||||
graph_memories = [
|
||||
{"source": relation["source"], "relationship": relation["relationship"], "target": relation["target"]}
|
||||
for relation in memories["relations"]
|
||||
]
|
||||
return semantic_memories, graph_memories, end_time - start_time
|
||||
|
||||
def answer_question(self, speaker_1_user_id, speaker_2_user_id, question, answer, category):
|
||||
speaker_1_memories, speaker_1_graph_memories, speaker_1_memory_time = self.search_memory(speaker_1_user_id, question)
|
||||
speaker_2_memories, speaker_2_graph_memories, speaker_2_memory_time = self.search_memory(speaker_2_user_id, question)
|
||||
speaker_1_memories, speaker_1_graph_memories, speaker_1_memory_time = self.search_memory(
|
||||
speaker_1_user_id, question
|
||||
)
|
||||
speaker_2_memories, speaker_2_graph_memories, speaker_2_memory_time = self.search_memory(
|
||||
speaker_2_user_id, question
|
||||
)
|
||||
|
||||
search_1_memory = [f"{item['timestamp']}: {item['memory']}"
|
||||
for item in speaker_1_memories]
|
||||
search_2_memory = [f"{item['timestamp']}: {item['memory']}"
|
||||
for item in speaker_2_memories]
|
||||
search_1_memory = [f"{item['timestamp']}: {item['memory']}" for item in speaker_1_memories]
|
||||
search_2_memory = [f"{item['timestamp']}: {item['memory']}" for item in speaker_2_memories]
|
||||
|
||||
template = Template(self.ANSWER_PROMPT)
|
||||
answer_prompt = template.render(
|
||||
speaker_1_user_id=speaker_1_user_id.split('_')[0],
|
||||
speaker_2_user_id=speaker_2_user_id.split('_')[0],
|
||||
speaker_1_user_id=speaker_1_user_id.split("_")[0],
|
||||
speaker_2_user_id=speaker_2_user_id.split("_")[0],
|
||||
speaker_1_memories=json.dumps(search_1_memory, indent=4),
|
||||
speaker_2_memories=json.dumps(search_2_memory, indent=4),
|
||||
speaker_1_graph_memories=json.dumps(speaker_1_graph_memories, indent=4),
|
||||
speaker_2_graph_memories=json.dumps(speaker_2_graph_memories, indent=4),
|
||||
question=question
|
||||
question=question,
|
||||
)
|
||||
|
||||
t1 = time.time()
|
||||
response = self.openai_client.chat.completions.create(
|
||||
model=os.getenv("MODEL"),
|
||||
messages=[
|
||||
{"role": "system", "content": answer_prompt}
|
||||
],
|
||||
temperature=0.0
|
||||
model=os.getenv("MODEL"), messages=[{"role": "system", "content": answer_prompt}], temperature=0.0
|
||||
)
|
||||
t2 = time.time()
|
||||
response_time = t2 - t1
|
||||
return response.choices[0].message.content, speaker_1_memories, speaker_2_memories, speaker_1_memory_time, speaker_2_memory_time, speaker_1_graph_memories, speaker_2_graph_memories, response_time
|
||||
return (
|
||||
response.choices[0].message.content,
|
||||
speaker_1_memories,
|
||||
speaker_2_memories,
|
||||
speaker_1_memory_time,
|
||||
speaker_2_memory_time,
|
||||
speaker_1_graph_memories,
|
||||
speaker_2_graph_memories,
|
||||
response_time,
|
||||
)
|
||||
|
||||
def process_question(self, val, speaker_a_user_id, speaker_b_user_id):
|
||||
question = val.get('question', '')
|
||||
answer = val.get('answer', '')
|
||||
category = val.get('category', -1)
|
||||
evidence = val.get('evidence', [])
|
||||
adversarial_answer = val.get('adversarial_answer', '')
|
||||
question = val.get("question", "")
|
||||
answer = val.get("answer", "")
|
||||
category = val.get("category", -1)
|
||||
evidence = val.get("evidence", [])
|
||||
adversarial_answer = val.get("adversarial_answer", "")
|
||||
|
||||
response, speaker_1_memories, speaker_2_memories, speaker_1_memory_time, speaker_2_memory_time, speaker_1_graph_memories, speaker_2_graph_memories, response_time = self.answer_question(
|
||||
speaker_a_user_id,
|
||||
speaker_b_user_id,
|
||||
question,
|
||||
answer,
|
||||
category
|
||||
)
|
||||
(
|
||||
response,
|
||||
speaker_1_memories,
|
||||
speaker_2_memories,
|
||||
speaker_1_memory_time,
|
||||
speaker_2_memory_time,
|
||||
speaker_1_graph_memories,
|
||||
speaker_2_graph_memories,
|
||||
response_time,
|
||||
) = self.answer_question(speaker_a_user_id, speaker_b_user_id, question, answer, category)
|
||||
|
||||
result = {
|
||||
"question": question,
|
||||
@@ -123,67 +153,63 @@ class MemorySearch:
|
||||
"adversarial_answer": adversarial_answer,
|
||||
"speaker_1_memories": speaker_1_memories,
|
||||
"speaker_2_memories": speaker_2_memories,
|
||||
'num_speaker_1_memories': len(speaker_1_memories),
|
||||
'num_speaker_2_memories': len(speaker_2_memories),
|
||||
'speaker_1_memory_time': speaker_1_memory_time,
|
||||
'speaker_2_memory_time': speaker_2_memory_time,
|
||||
"num_speaker_1_memories": len(speaker_1_memories),
|
||||
"num_speaker_2_memories": len(speaker_2_memories),
|
||||
"speaker_1_memory_time": speaker_1_memory_time,
|
||||
"speaker_2_memory_time": speaker_2_memory_time,
|
||||
"speaker_1_graph_memories": speaker_1_graph_memories,
|
||||
"speaker_2_graph_memories": speaker_2_graph_memories,
|
||||
"response_time": response_time
|
||||
"response_time": response_time,
|
||||
}
|
||||
|
||||
# Save results after each question is processed
|
||||
with open(self.output_path, 'w') as f:
|
||||
with open(self.output_path, "w") as f:
|
||||
json.dump(self.results, f, indent=4)
|
||||
|
||||
return result
|
||||
|
||||
def process_data_file(self, file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
with open(file_path, "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
for idx, item in tqdm(enumerate(data), total=len(data), desc="Processing conversations"):
|
||||
qa = item['qa']
|
||||
conversation = item['conversation']
|
||||
speaker_a = conversation['speaker_a']
|
||||
speaker_b = conversation['speaker_b']
|
||||
qa = item["qa"]
|
||||
conversation = item["conversation"]
|
||||
speaker_a = conversation["speaker_a"]
|
||||
speaker_b = conversation["speaker_b"]
|
||||
|
||||
speaker_a_user_id = f"{speaker_a}_{idx}"
|
||||
speaker_b_user_id = f"{speaker_b}_{idx}"
|
||||
|
||||
for question_item in tqdm(qa, total=len(qa), desc=f"Processing questions for conversation {idx}", leave=False):
|
||||
result = self.process_question(
|
||||
question_item,
|
||||
speaker_a_user_id,
|
||||
speaker_b_user_id
|
||||
)
|
||||
for question_item in tqdm(
|
||||
qa, total=len(qa), desc=f"Processing questions for conversation {idx}", leave=False
|
||||
):
|
||||
result = self.process_question(question_item, speaker_a_user_id, speaker_b_user_id)
|
||||
self.results[idx].append(result)
|
||||
|
||||
# Save results after each question is processed
|
||||
with open(self.output_path, 'w') as f:
|
||||
with open(self.output_path, "w") as f:
|
||||
json.dump(self.results, f, indent=4)
|
||||
|
||||
# Final save at the end
|
||||
with open(self.output_path, 'w') as f:
|
||||
with open(self.output_path, "w") as f:
|
||||
json.dump(self.results, f, indent=4)
|
||||
|
||||
def process_questions_parallel(self, qa_list, speaker_a_user_id, speaker_b_user_id, max_workers=1):
|
||||
def process_single_question(val):
|
||||
result = self.process_question(val, speaker_a_user_id, speaker_b_user_id)
|
||||
# Save results after each question is processed
|
||||
with open(self.output_path, 'w') as f:
|
||||
with open(self.output_path, "w") as f:
|
||||
json.dump(self.results, f, indent=4)
|
||||
return result
|
||||
|
||||
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||
results = list(tqdm(
|
||||
executor.map(process_single_question, qa_list),
|
||||
total=len(qa_list),
|
||||
desc="Answering Questions"
|
||||
))
|
||||
results = list(
|
||||
tqdm(executor.map(process_single_question, qa_list), total=len(qa_list), desc="Answering Questions")
|
||||
)
|
||||
|
||||
# Final save at the end
|
||||
with open(self.output_path, 'w') as f:
|
||||
with open(self.output_path, "w") as f:
|
||||
json.dump(self.results, f, indent=4)
|
||||
|
||||
return results
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from openai import OpenAI
|
||||
import os
|
||||
import argparse
|
||||
import json
|
||||
from jinja2 import Template
|
||||
from tqdm import tqdm
|
||||
import os
|
||||
import time
|
||||
from collections import defaultdict
|
||||
|
||||
from dotenv import load_dotenv
|
||||
import argparse
|
||||
from jinja2 import Template
|
||||
from openai import OpenAI
|
||||
from tqdm import tqdm
|
||||
|
||||
load_dotenv()
|
||||
|
||||
@@ -58,23 +59,19 @@ class OpenAIPredict:
|
||||
self.results = defaultdict(list)
|
||||
|
||||
def search_memory(self, idx):
|
||||
|
||||
with open(f'memories/{idx}.txt', 'r') as file:
|
||||
with open(f"memories/{idx}.txt", "r") as file:
|
||||
memories = file.read()
|
||||
|
||||
return memories, 0
|
||||
|
||||
def process_question(self, val, idx):
|
||||
question = val.get('question', '')
|
||||
answer = val.get('answer', '')
|
||||
category = val.get('category', -1)
|
||||
evidence = val.get('evidence', [])
|
||||
adversarial_answer = val.get('adversarial_answer', '')
|
||||
question = val.get("question", "")
|
||||
answer = val.get("answer", "")
|
||||
category = val.get("category", -1)
|
||||
evidence = val.get("evidence", [])
|
||||
adversarial_answer = val.get("adversarial_answer", "")
|
||||
|
||||
response, search_memory_time, response_time, context = self.answer_question(
|
||||
idx,
|
||||
question
|
||||
)
|
||||
response, search_memory_time, response_time, context = self.answer_question(idx, question)
|
||||
|
||||
result = {
|
||||
"question": question,
|
||||
@@ -85,7 +82,7 @@ class OpenAIPredict:
|
||||
"adversarial_answer": adversarial_answer,
|
||||
"search_memory_time": search_memory_time,
|
||||
"response_time": response_time,
|
||||
"context": context
|
||||
"context": context,
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -94,43 +91,35 @@ class OpenAIPredict:
|
||||
memories, search_memory_time = self.search_memory(idx)
|
||||
|
||||
template = Template(ANSWER_PROMPT)
|
||||
answer_prompt = template.render(
|
||||
memories=memories,
|
||||
question=question
|
||||
)
|
||||
answer_prompt = template.render(memories=memories, question=question)
|
||||
|
||||
t1 = time.time()
|
||||
response = self.openai_client.chat.completions.create(
|
||||
model=os.getenv("MODEL"),
|
||||
messages=[
|
||||
{"role": "system", "content": answer_prompt}
|
||||
],
|
||||
temperature=0.0
|
||||
model=os.getenv("MODEL"), messages=[{"role": "system", "content": answer_prompt}], temperature=0.0
|
||||
)
|
||||
t2 = time.time()
|
||||
response_time = t2 - t1
|
||||
return response.choices[0].message.content, search_memory_time, response_time, memories
|
||||
|
||||
def process_data_file(self, file_path, output_file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
with open(file_path, "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
for idx, item in tqdm(enumerate(data), total=len(data), desc="Processing conversations"):
|
||||
qa = item['qa']
|
||||
qa = item["qa"]
|
||||
|
||||
for question_item in tqdm(qa, total=len(qa), desc=f"Processing questions for conversation {idx}", leave=False):
|
||||
result = self.process_question(
|
||||
question_item,
|
||||
idx
|
||||
)
|
||||
for question_item in tqdm(
|
||||
qa, total=len(qa), desc=f"Processing questions for conversation {idx}", leave=False
|
||||
):
|
||||
result = self.process_question(question_item, idx)
|
||||
self.results[idx].append(result)
|
||||
|
||||
# Save results after each question is processed
|
||||
with open(output_file_path, 'w') as f:
|
||||
with open(output_file_path, "w") as f:
|
||||
json.dump(self.results, f, indent=4)
|
||||
|
||||
# Final save at the end
|
||||
with open(output_file_path, 'w') as f:
|
||||
with open(output_file_path, "w") as f:
|
||||
json.dump(self.results, f, indent=4)
|
||||
|
||||
|
||||
@@ -140,4 +129,3 @@ if __name__ == "__main__":
|
||||
args = parser.parse_args()
|
||||
openai_predict = OpenAIPredict()
|
||||
openai_predict.process_data_file("../../dataset/locomo10.json", args.output_file_path)
|
||||
|
||||
|
||||
+41
-55
@@ -1,13 +1,14 @@
|
||||
from openai import OpenAI
|
||||
import json
|
||||
import numpy as np
|
||||
from tqdm import tqdm
|
||||
from jinja2 import Template
|
||||
import tiktoken
|
||||
import os
|
||||
import time
|
||||
from collections import defaultdict
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
import tiktoken
|
||||
from dotenv import load_dotenv
|
||||
from jinja2 import Template
|
||||
from openai import OpenAI
|
||||
from tqdm import tqdm
|
||||
|
||||
load_dotenv()
|
||||
|
||||
@@ -32,10 +33,7 @@ class RAGManager:
|
||||
|
||||
def generate_response(self, question, context):
|
||||
template = Template(PROMPT)
|
||||
prompt = template.render(
|
||||
CONTEXT=context,
|
||||
QUESTION=question
|
||||
)
|
||||
prompt = template.render(CONTEXT=context, QUESTION=question)
|
||||
|
||||
max_retries = 3
|
||||
retries = 0
|
||||
@@ -46,19 +44,21 @@ class RAGManager:
|
||||
response = self.client.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=[
|
||||
{"role": "system",
|
||||
"content": "You are a helpful assistant that can answer "
|
||||
"questions based on the provided context."
|
||||
"If the question involves timing, use the conversation date for reference."
|
||||
"Provide the shortest possible answer."
|
||||
"Use words directly from the conversation when possible."
|
||||
"Avoid using subjects in your answer."},
|
||||
{"role": "user", "content": prompt}
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a helpful assistant that can answer "
|
||||
"questions based on the provided context."
|
||||
"If the question involves timing, use the conversation date for reference."
|
||||
"Provide the shortest possible answer."
|
||||
"Use words directly from the conversation when possible."
|
||||
"Avoid using subjects in your answer.",
|
||||
},
|
||||
{"role": "user", "content": prompt},
|
||||
],
|
||||
temperature=0
|
||||
temperature=0,
|
||||
)
|
||||
t2 = time.time()
|
||||
return response.choices[0].message.content.strip(), t2-t1
|
||||
return response.choices[0].message.content.strip(), t2 - t1
|
||||
except Exception as e:
|
||||
retries += 1
|
||||
if retries > max_retries:
|
||||
@@ -68,21 +68,16 @@ class RAGManager:
|
||||
def clean_chat_history(self, chat_history):
|
||||
cleaned_chat_history = ""
|
||||
for c in chat_history:
|
||||
cleaned_chat_history += (f"{c['timestamp']} | {c['speaker']}: "
|
||||
f"{c['text']}\n")
|
||||
cleaned_chat_history += f"{c['timestamp']} | {c['speaker']}: " f"{c['text']}\n"
|
||||
|
||||
return cleaned_chat_history
|
||||
|
||||
def calculate_embedding(self, document):
|
||||
response = self.client.embeddings.create(
|
||||
model=os.getenv("EMBEDDING_MODEL"),
|
||||
input=document
|
||||
)
|
||||
response = self.client.embeddings.create(model=os.getenv("EMBEDDING_MODEL"), input=document)
|
||||
return response.data[0].embedding
|
||||
|
||||
def calculate_similarity(self, embedding1, embedding2):
|
||||
return np.dot(embedding1, embedding2) / (
|
||||
np.linalg.norm(embedding1) * np.linalg.norm(embedding2))
|
||||
return np.dot(embedding1, embedding2) / (np.linalg.norm(embedding1) * np.linalg.norm(embedding2))
|
||||
|
||||
def search(self, query, chunks, embeddings, k=1):
|
||||
"""
|
||||
@@ -100,10 +95,7 @@ class RAGManager:
|
||||
"""
|
||||
t1 = time.time()
|
||||
query_embedding = self.calculate_embedding(query)
|
||||
similarities = [
|
||||
self.calculate_similarity(query_embedding, embedding)
|
||||
for embedding in embeddings
|
||||
]
|
||||
similarities = [self.calculate_similarity(query_embedding, embedding) for embedding in embeddings]
|
||||
|
||||
# Get indices of top-k most similar chunks
|
||||
if k == 1:
|
||||
@@ -117,7 +109,7 @@ class RAGManager:
|
||||
combined_chunks = "\n<->\n".join([chunks[i] for i in top_indices])
|
||||
|
||||
t2 = time.time()
|
||||
return combined_chunks, t2-t1
|
||||
return combined_chunks, t2 - t1
|
||||
|
||||
def create_chunks(self, chat_history, chunk_size=500):
|
||||
"""
|
||||
@@ -138,7 +130,7 @@ class RAGManager:
|
||||
|
||||
# Split into chunks based on token count
|
||||
for i in range(0, len(tokens), chunk_size):
|
||||
chunk_tokens = tokens[i:i+chunk_size]
|
||||
chunk_tokens = tokens[i : i + chunk_size]
|
||||
chunk = encoding.decode(chunk_tokens)
|
||||
chunks.append(chunk)
|
||||
|
||||
@@ -158,13 +150,9 @@ class RAGManager:
|
||||
chat_history = value["conversation"]
|
||||
questions = value["question"]
|
||||
|
||||
chunks, embeddings = self.create_chunks(
|
||||
chat_history, self.chunk_size
|
||||
)
|
||||
chunks, embeddings = self.create_chunks(chat_history, self.chunk_size)
|
||||
|
||||
for item in tqdm(
|
||||
questions, desc="Answering questions", leave=False
|
||||
):
|
||||
for item in tqdm(questions, desc="Answering questions", leave=False):
|
||||
question = item["question"]
|
||||
answer = item.get("answer", "")
|
||||
category = item["category"]
|
||||
@@ -173,22 +161,20 @@ class RAGManager:
|
||||
context = chunks[0]
|
||||
search_time = 0
|
||||
else:
|
||||
context, search_time = self.search(
|
||||
question, chunks, embeddings, k=self.k
|
||||
)
|
||||
response, response_time = self.generate_response(
|
||||
question, context
|
||||
)
|
||||
context, search_time = self.search(question, chunks, embeddings, k=self.k)
|
||||
response, response_time = self.generate_response(question, context)
|
||||
|
||||
FINAL_RESULTS[key].append({
|
||||
"question": question,
|
||||
"answer": answer,
|
||||
"category": category,
|
||||
"context": context,
|
||||
"response": response,
|
||||
"search_time": search_time,
|
||||
"response_time": response_time,
|
||||
})
|
||||
FINAL_RESULTS[key].append(
|
||||
{
|
||||
"question": question,
|
||||
"answer": answer,
|
||||
"category": category,
|
||||
"context": context,
|
||||
"response": response,
|
||||
"search_time": search_time,
|
||||
"response_time": response_time,
|
||||
}
|
||||
)
|
||||
with open(output_file_path, "w+") as f:
|
||||
json.dump(FINAL_RESULTS, f, indent=4)
|
||||
|
||||
|
||||
+2
-11
@@ -1,12 +1,3 @@
|
||||
TECHNIQUES = [
|
||||
"mem0",
|
||||
"rag",
|
||||
"langmem",
|
||||
"zep",
|
||||
"openai"
|
||||
]
|
||||
TECHNIQUES = ["mem0", "rag", "langmem", "zep", "openai"]
|
||||
|
||||
METHODS = [
|
||||
"add",
|
||||
"search"
|
||||
]
|
||||
METHODS = ["add", "search"]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from tqdm import tqdm
|
||||
from zep_cloud import Message
|
||||
@@ -18,12 +19,12 @@ class ZepAdd:
|
||||
self.load_data()
|
||||
|
||||
def load_data(self):
|
||||
with open(self.data_path, 'r') as f:
|
||||
with open(self.data_path, "r") as f:
|
||||
self.data = json.load(f)
|
||||
return self.data
|
||||
|
||||
def process_conversation(self, run_id, item, idx):
|
||||
conversation = item['conversation']
|
||||
conversation = item["conversation"]
|
||||
|
||||
user_id = f"run_id_{run_id}_experiment_user_{idx}"
|
||||
session_id = f"run_id_{run_id}_experiment_session_{idx}"
|
||||
@@ -40,7 +41,7 @@ class ZepAdd:
|
||||
|
||||
print("Starting to add memories... for user", user_id)
|
||||
for key in tqdm(conversation.keys(), desc=f"Processing user {user_id}"):
|
||||
if key in ['speaker_a', 'speaker_b'] or "date" in key:
|
||||
if key in ["speaker_a", "speaker_b"] or "date" in key:
|
||||
continue
|
||||
|
||||
date_time_key = key + "_date_time"
|
||||
@@ -50,11 +51,13 @@ class ZepAdd:
|
||||
for chat in tqdm(chats, desc=f"Adding chats for {key}", leave=False):
|
||||
self.zep_client.memory.add(
|
||||
session_id=session_id,
|
||||
messages=[Message(
|
||||
role=chat['speaker'],
|
||||
role_type="user",
|
||||
content=f"{timestamp}: {chat['text']}",
|
||||
)]
|
||||
messages=[
|
||||
Message(
|
||||
role=chat["speaker"],
|
||||
role_type="user",
|
||||
content=f"{timestamp}: {chat['text']}",
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
def process_all_conversations(self, run_id):
|
||||
@@ -70,4 +73,4 @@ if __name__ == "__main__":
|
||||
parser.add_argument("--run_id", type=str, required=True)
|
||||
args = parser.parse_args()
|
||||
zep_add = ZepAdd(data_path="../../dataset/locomo10.json")
|
||||
zep_add.process_all_conversations(args.run_id)
|
||||
zep_add.process_all_conversations(args.run_id)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
from collections import defaultdict
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from jinja2 import Template
|
||||
from openai import OpenAI
|
||||
from prompts import ANSWER_PROMPT_ZEP
|
||||
from tqdm import tqdm
|
||||
from zep_cloud import EntityEdge, EntityNode
|
||||
from zep_cloud.client import Zep
|
||||
import json
|
||||
import os
|
||||
import pandas as pd
|
||||
import time
|
||||
from prompts import ANSWER_PROMPT_ZEP
|
||||
|
||||
load_dotenv()
|
||||
|
||||
@@ -42,9 +42,9 @@ class ZepSearch:
|
||||
return f"{edge.valid_at if edge.valid_at else 'date unknown'} - {(edge.invalid_at if edge.invalid_at else 'present')}"
|
||||
|
||||
def compose_search_context(self, edges: list[EntityEdge], nodes: list[EntityNode]) -> str:
|
||||
facts = [f' - {edge.fact} ({self.format_edge_date_range(edge)})' for edge in edges]
|
||||
entities = [f' - {node.name}: {node.summary}' for node in nodes]
|
||||
return TEMPLATE.format(facts='\n'.join(facts), entities='\n'.join(entities))
|
||||
facts = [f" - {edge.fact} ({self.format_edge_date_range(edge)})" for edge in edges]
|
||||
entities = [f" - {node.name}: {node.summary}" for node in nodes]
|
||||
return TEMPLATE.format(facts="\n".join(facts), entities="\n".join(entities))
|
||||
|
||||
def search_memory(self, run_id, idx, query, max_retries=3, retry_delay=1):
|
||||
start_time = time.time()
|
||||
@@ -52,9 +52,14 @@ class ZepSearch:
|
||||
while retries < max_retries:
|
||||
try:
|
||||
user_id = f"run_id_{run_id}_experiment_user_{idx}"
|
||||
session_id = f"run_id_{run_id}_experiment_session_{idx}"
|
||||
edges_results = (self.zep_client.graph.search(user_id=user_id, reranker='cross_encoder', query=query, scope='edges', limit=20)).edges
|
||||
node_results = (self.zep_client.graph.search(user_id=user_id, reranker='rrf', query=query, scope='nodes', limit=20)).nodes
|
||||
edges_results = (
|
||||
self.zep_client.graph.search(
|
||||
user_id=user_id, reranker="cross_encoder", query=query, scope="edges", limit=20
|
||||
)
|
||||
).edges
|
||||
node_results = (
|
||||
self.zep_client.graph.search(user_id=user_id, reranker="rrf", query=query, scope="nodes", limit=20)
|
||||
).nodes
|
||||
context = self.compose_search_context(edges_results, node_results)
|
||||
break
|
||||
except Exception as e:
|
||||
@@ -69,17 +74,13 @@ class ZepSearch:
|
||||
return context, end_time - start_time
|
||||
|
||||
def process_question(self, run_id, val, idx):
|
||||
question = val.get('question', '')
|
||||
answer = val.get('answer', '')
|
||||
category = val.get('category', -1)
|
||||
evidence = val.get('evidence', [])
|
||||
adversarial_answer = val.get('adversarial_answer', '')
|
||||
question = val.get("question", "")
|
||||
answer = val.get("answer", "")
|
||||
category = val.get("category", -1)
|
||||
evidence = val.get("evidence", [])
|
||||
adversarial_answer = val.get("adversarial_answer", "")
|
||||
|
||||
response, search_memory_time, response_time, context = self.answer_question(
|
||||
run_id,
|
||||
idx,
|
||||
question
|
||||
)
|
||||
response, search_memory_time, response_time, context = self.answer_question(run_id, idx, question)
|
||||
|
||||
result = {
|
||||
"question": question,
|
||||
@@ -90,7 +91,7 @@ class ZepSearch:
|
||||
"adversarial_answer": adversarial_answer,
|
||||
"search_memory_time": search_memory_time,
|
||||
"response_time": response_time,
|
||||
"context": context
|
||||
"context": context,
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -99,44 +100,35 @@ class ZepSearch:
|
||||
context, search_memory_time = self.search_memory(run_id, idx, question)
|
||||
|
||||
template = Template(ANSWER_PROMPT_ZEP)
|
||||
answer_prompt = template.render(
|
||||
memories=context,
|
||||
question=question
|
||||
)
|
||||
answer_prompt = template.render(memories=context, question=question)
|
||||
|
||||
t1 = time.time()
|
||||
response = self.openai_client.chat.completions.create(
|
||||
model=os.getenv("MODEL"),
|
||||
messages=[
|
||||
{"role": "system", "content": answer_prompt}
|
||||
],
|
||||
temperature=0.0
|
||||
model=os.getenv("MODEL"), messages=[{"role": "system", "content": answer_prompt}], temperature=0.0
|
||||
)
|
||||
t2 = time.time()
|
||||
response_time = t2 - t1
|
||||
return response.choices[0].message.content, search_memory_time, response_time, context
|
||||
|
||||
def process_data_file(self, file_path, run_id, output_file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
with open(file_path, "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
for idx, item in tqdm(enumerate(data), total=len(data), desc="Processing conversations"):
|
||||
qa = item['qa']
|
||||
qa = item["qa"]
|
||||
|
||||
for question_item in tqdm(qa, total=len(qa), desc=f"Processing questions for conversation {idx}", leave=False):
|
||||
result = self.process_question(
|
||||
run_id,
|
||||
question_item,
|
||||
idx
|
||||
)
|
||||
for question_item in tqdm(
|
||||
qa, total=len(qa), desc=f"Processing questions for conversation {idx}", leave=False
|
||||
):
|
||||
result = self.process_question(run_id, question_item, idx)
|
||||
self.results[idx].append(result)
|
||||
|
||||
# Save results after each question is processed
|
||||
with open(output_file_path, 'w') as f:
|
||||
with open(output_file_path, "w") as f:
|
||||
json.dump(self.results, f, indent=4)
|
||||
|
||||
# Final save at the end
|
||||
with open(output_file_path, 'w') as f:
|
||||
with open(output_file_path, "w") as f:
|
||||
json.dump(self.results, f, indent=4)
|
||||
|
||||
|
||||
|
||||
@@ -56,9 +56,7 @@
|
||||
"\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = (\n",
|
||||
" \"\"\n",
|
||||
")"
|
||||
"os.environ[\"OPENAI_API_KEY\"] = \"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -149,7 +147,7 @@
|
||||
" \"role\": \"assistant\",\n",
|
||||
" \"content\": \"Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future.\",\n",
|
||||
" },\n",
|
||||
"]\n"
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -166,9 +164,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Store inferred memories (default behavior)\n",
|
||||
"result = m.add(\n",
|
||||
" messages, user_id=\"alice\", metadata={\"category\": \"movie_recommendations\"}\n",
|
||||
")"
|
||||
"result = m.add(messages, user_id=\"alice\", metadata={\"category\": \"movie_recommendations\"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ApdaLD4Qi30H"
|
||||
},
|
||||
"source": [
|
||||
"# Neo4j as Graph Memory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "l7bi3i21i30I"
|
||||
},
|
||||
"source": [
|
||||
"## Prerequisites\n",
|
||||
"\n",
|
||||
"### 1. Install Mem0 with Graph Memory support\n",
|
||||
"\n",
|
||||
"To use Mem0 with Graph Memory support, install it using pip:\n",
|
||||
"\n",
|
||||
"```bash\n",
|
||||
"pip install \"mem0ai[graph]\"\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"This command installs Mem0 along with the necessary dependencies for graph functionality.\n",
|
||||
"\n",
|
||||
"### 2. Install Neo4j\n",
|
||||
"\n",
|
||||
"To utilize Neo4j as Graph Memory, run it with Docker:\n",
|
||||
"\n",
|
||||
"```bash\n",
|
||||
"docker run \\\n",
|
||||
" -p 7474:7474 -p 7687:7687 \\\n",
|
||||
" -e NEO4J_AUTH=neo4j/password \\\n",
|
||||
" neo4j:5\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"This command starts Neo4j with default credentials (`neo4j` / `password`) and exposes both the HTTP (7474) and Bolt (7687) ports.\n",
|
||||
"\n",
|
||||
"You can access the Neo4j browser at [http://localhost:7474](http://localhost:7474).\n",
|
||||
"\n",
|
||||
"Additional information can be found in the [Neo4j documentation](https://neo4j.com/docs/).\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "DkeBdFEpi30I"
|
||||
},
|
||||
"source": [
|
||||
"## Configuration\n",
|
||||
"\n",
|
||||
"Do all the imports and configure OpenAI (enter your OpenAI API key):"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"id": "d99EfBpii30I"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from mem0 import Memory\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = (\n",
|
||||
" \"\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "QTucZJjIi30J"
|
||||
},
|
||||
"source": [
|
||||
"Set up configuration to use the embedder model and Neo4j as a graph store:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"id": "QSE0RFoSi30J"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"config = {\n",
|
||||
" \"embedder\": {\n",
|
||||
" \"provider\": \"openai\",\n",
|
||||
" \"config\": {\"model\": \"text-embedding-3-large\", \"embedding_dims\": 1536},\n",
|
||||
" },\n",
|
||||
" \"graph_store\": {\n",
|
||||
" \"provider\": \"neo4j\",\n",
|
||||
" \"config\": {\n",
|
||||
" \"url\": \"bolt://54.87.227.131:7687\",\n",
|
||||
" \"username\": \"neo4j\",\n",
|
||||
" \"password\": \"causes-bins-vines\",\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "OioTnv6xi30J"
|
||||
},
|
||||
"source": [
|
||||
"## Graph Memory initializiation\n",
|
||||
"\n",
|
||||
"Initialize Neo4j as a Graph Memory store:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"id": "fX-H9vgNi30J"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"m = Memory.from_config(config_dict=config)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kr1fVMwEi30J"
|
||||
},
|
||||
"source": [
|
||||
"## Store memories\n",
|
||||
"\n",
|
||||
"Create memories:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"id": "sEfogqp_i30J"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"messages = [\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"I'm planning to watch a movie tonight. Any recommendations?\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"assistant\",\n",
|
||||
" \"content\": \"How about a thriller movies? They can be quite engaging.\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"I'm not a big fan of thriller movies but I love sci-fi movies.\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"assistant\",\n",
|
||||
" \"content\": \"Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future.\",\n",
|
||||
" },\n",
|
||||
"]\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "gtBHCyIgi30J"
|
||||
},
|
||||
"source": [
|
||||
"Store memories in Neo4j:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {
|
||||
"id": "BMVGgZMFi30K"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Store inferred memories (default behavior)\n",
|
||||
"result = m.add(\n",
|
||||
" messages, user_id=\"alice\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lQRptOywi30K"
|
||||
},
|
||||
"source": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "LBXW7Gv-i30K"
|
||||
},
|
||||
"source": [
|
||||
"## Search memories"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "UHFDeQBEi30K",
|
||||
"outputId": "2c69de7d-a79a-48f6-e3c4-bd743067857c"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Loves sci-fi movies 0.3153664287340898\n",
|
||||
"Planning to watch a movie tonight 0.09683349296551162\n",
|
||||
"Not a big fan of thriller movies 0.09468540071789466\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for result in m.search(\"what does alice love?\", user_id=\"alice\")[\"results\"]:\n",
|
||||
" print(result[\"memory\"], result[\"score\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"id": "2jXEIma9kK_Q"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.13.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -70,7 +70,7 @@ const retrieveMemories = (memories: any) => {
|
||||
export async function POST(req: Request) {
|
||||
const { messages, system, tools, userId } = await req.json();
|
||||
|
||||
const memories = await getMemories(messages, { user_id: userId, rerank: true, threshold: 0.1 });
|
||||
const memories = await getMemories(messages, { user_id: userId, rerank: true, threshold: 0.1, output_format: "v1.0" });
|
||||
const mem0Instructions = retrieveMemories(memories);
|
||||
|
||||
const result = streamText({
|
||||
|
||||
@@ -45,7 +45,7 @@ const useUserId = () => {
|
||||
export const Assistant = () => {
|
||||
const { userId, resetUserId } = useUserId();
|
||||
const runtime = useChatRuntime({
|
||||
api: "https://demo.mem0.ai/api/chat",
|
||||
api: "/api/chat",
|
||||
body: { userId },
|
||||
});
|
||||
|
||||
@@ -91,8 +91,8 @@ export const Assistant = () => {
|
||||
</button>
|
||||
<GithubButton url="https://github.com/mem0ai/mem0/tree/main/examples" />
|
||||
|
||||
<Link href={"https://app.mem0.ai/"} target="_blank" className="py-2 ml-2 px-4 font-semibold dark:bg-zinc-100 dark:hover:bg-zinc-200 bg-zinc-800 text-white rounded-full hover:bg-zinc-900 dark:text-[#475569]">
|
||||
Save Memories
|
||||
<Link href={"https://app.mem0.ai/"} target="_blank" className="py-1 ml-2 px-4 font-semibold dark:bg-zinc-100 dark:hover:bg-zinc-200 bg-zinc-800 text-white rounded-full hover:bg-zinc-900 dark:text-[#475569]">
|
||||
Playground
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -2,24 +2,6 @@ import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
assetPrefix: "https://demo.mem0.ai",
|
||||
images: {
|
||||
path: "https://demo.mem0.ai",
|
||||
},
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
// matching all API routes
|
||||
source: "/api/:path*",
|
||||
headers: [
|
||||
{ key: "Access-Control-Allow-Credentials", value: "true" },
|
||||
{ key: "Access-Control-Allow-Origin", value: "*" },
|
||||
{ key: "Access-Control-Allow-Methods", value: "GET,DELETE,PATCH,POST,PUT" },
|
||||
{ key: "Access-Control-Allow-Headers", value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" },
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"@assistant-ui/react": "^0.8.2",
|
||||
"@assistant-ui/react-ai-sdk": "^0.8.0",
|
||||
"@assistant-ui/react-markdown": "^0.8.0",
|
||||
"@mem0/vercel-ai-provider": "^1.0.0",
|
||||
"@mem0/vercel-ai-provider": "^1.0.4",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.6",
|
||||
"@radix-ui/react-avatar": "^1.1.3",
|
||||
"@radix-ui/react-popover": "^1.1.6",
|
||||
|
||||
@@ -7,10 +7,11 @@ export OPENAI_API_KEY="your_openai_api_key"
|
||||
export MEM0_API_KEY="your_mem0_api_key"
|
||||
"""
|
||||
|
||||
from mem0 import MemoryClient
|
||||
from agno.agent import Agent
|
||||
from agno.models.openai import OpenAIChat
|
||||
|
||||
from mem0 import MemoryClient
|
||||
|
||||
# Initialize memory
|
||||
memory_client = MemoryClient(api_key="your-mem0-api-key")
|
||||
USER_ID = "Anish"
|
||||
@@ -19,19 +20,19 @@ agent = Agent(
|
||||
name="Fitness Agent",
|
||||
model=OpenAIChat(id="gpt-4o"),
|
||||
description="You are a helpful fitness assistant who remembers past logs and gives personalized suggestions for Anish's training and diet.",
|
||||
markdown=True
|
||||
markdown=True,
|
||||
)
|
||||
|
||||
|
||||
# Store user preferences as memory
|
||||
def store_user_preferences(conversation: list, user_id: str = USER_ID):
|
||||
"""Store user preferences from conversation history"""
|
||||
memory_client.add(conversation, user_id=user_id, output_format='v1.1')
|
||||
memory_client.add(conversation, user_id=user_id, output_format="v1.1")
|
||||
|
||||
|
||||
# Memory-aware assistant function
|
||||
def fitness_coach(user_input: str, user_id: str = USER_ID):
|
||||
memories = memory_client.search(user_input, user_id=user_id) # Search relevant memories bases on user query
|
||||
memories = memory_client.search(user_input, user_id=user_id) # Search relevant memories bases on user query
|
||||
memory_context = "\n".join(f"- {m['memory']}" for m in memories)
|
||||
|
||||
prompt = f"""You are a fitness assistant who helps Anish with his training, recovery, and diet. You have long-term memory of his health, routines, preferences, and past conversations.
|
||||
@@ -47,113 +48,66 @@ User query:
|
||||
memory_client.add(f"User: {user_input}\nAssistant: {response.content}", user_id=user_id)
|
||||
return response.content
|
||||
|
||||
|
||||
# --------------------------------------------------
|
||||
# Store user preferences and memories
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hi, I’m Anish. I'm 26 years old, 5'10\", and weigh 72kg. I started working out 6 months ago with the goal of building lean muscle."
|
||||
"content": "Hi, I’m Anish. I'm 26 years old, 5'10\", and weigh 72kg. I started working out 6 months ago with the goal of building lean muscle.",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Got it — you're 26, 5'10\", 72kg, and on a lean muscle journey. Started gym 6 months ago."
|
||||
"content": "Got it — you're 26, 5'10\", 72kg, and on a lean muscle journey. Started gym 6 months ago.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I follow a push-pull-legs routine and train 5 times a week. My rest days are Wednesday and Sunday."
|
||||
"content": "I follow a push-pull-legs routine and train 5 times a week. My rest days are Wednesday and Sunday.",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Understood — push-pull-legs split, training 5x/week with rest on Wednesdays and Sundays."
|
||||
"content": "Understood — push-pull-legs split, training 5x/week with rest on Wednesdays and Sundays.",
|
||||
},
|
||||
{"role": "user", "content": "After push days, I usually eat high-protein and moderate-carb meals to recover."},
|
||||
{"role": "assistant", "content": "Noted — high-protein, moderate-carb meals after push workouts."},
|
||||
{"role": "user", "content": "For pull days, I take whey protein and eat a banana after training."},
|
||||
{"role": "assistant", "content": "Logged — whey protein and banana post pull workouts."},
|
||||
{"role": "user", "content": "On leg days, I make sure to have complex carbs like rice or oats."},
|
||||
{"role": "assistant", "content": "Noted — complex carbs like rice and oats are part of your leg day meals."},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "After push days, I usually eat high-protein and moderate-carb meals to recover."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Noted — high-protein, moderate-carb meals after push workouts."
|
||||
"content": "I often feel sore after leg days, so I use turmeric milk and magnesium to help with recovery.",
|
||||
},
|
||||
{"role": "assistant", "content": "I'll remember turmeric milk and magnesium as part of your leg day recovery."},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "For pull days, I take whey protein and eat a banana after training."
|
||||
"content": "Last push day, I did 3x8 bench press at 60kg, 4x12 overhead press, and dips. Felt fatigued after.",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Logged — whey protein and banana post pull workouts."
|
||||
"content": "Push day logged — 60kg bench, overhead press, dips. You felt fatigued afterward.",
|
||||
},
|
||||
{"role": "user", "content": "I prefer light dinners post-workout like tofu, soup, and vegetables."},
|
||||
{"role": "assistant", "content": "Got it — light dinners post-workout: tofu, soup, and veggies."},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "On leg days, I make sure to have complex carbs like rice or oats."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Noted — complex carbs like rice and oats are part of your leg day meals."
|
||||
"content": "I have mild lactose intolerance, so I avoid dairy. I use almond milk or lactose-free whey.",
|
||||
},
|
||||
{"role": "assistant", "content": "Understood — avoiding regular dairy, using almond milk and lactose-free whey."},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I often feel sore after leg days, so I use turmeric milk and magnesium to help with recovery."
|
||||
"content": "I get occasional knee pain, so I avoid deep squats and do more hamstring curls and glute bridges on leg days.",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "I'll remember turmeric milk and magnesium as part of your leg day recovery."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Last push day, I did 3x8 bench press at 60kg, 4x12 overhead press, and dips. Felt fatigued after."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Push day logged — 60kg bench, overhead press, dips. You felt fatigued afterward."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I prefer light dinners post-workout like tofu, soup, and vegetables."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Got it — light dinners post-workout: tofu, soup, and veggies."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I have mild lactose intolerance, so I avoid dairy. I use almond milk or lactose-free whey."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Understood — avoiding regular dairy, using almond milk and lactose-free whey."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I get occasional knee pain, so I avoid deep squats and do more hamstring curls and glute bridges on leg days."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Noted — due to knee discomfort, you substitute deep squats with curls and glute bridges."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I track sleep and notice poor performance when I sleep less than 6 hours."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Logged — performance drops when you get under 6 hours of sleep."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I take magnesium supplements to help with muscle recovery and sleep quality."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Remembered — magnesium helps you with recovery and sleep."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I avoid caffeine after 4 PM because it affects my sleep."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Got it — you avoid caffeine post-4 PM to protect your sleep."
|
||||
"content": "Noted — due to knee discomfort, you substitute deep squats with curls and glute bridges.",
|
||||
},
|
||||
{"role": "user", "content": "I track sleep and notice poor performance when I sleep less than 6 hours."},
|
||||
{"role": "assistant", "content": "Logged — performance drops when you get under 6 hours of sleep."},
|
||||
{"role": "user", "content": "I take magnesium supplements to help with muscle recovery and sleep quality."},
|
||||
{"role": "assistant", "content": "Remembered — magnesium helps you with recovery and sleep."},
|
||||
{"role": "user", "content": "I avoid caffeine after 4 PM because it affects my sleep."},
|
||||
{"role": "assistant", "content": "Got it — you avoid caffeine post-4 PM to protect your sleep."},
|
||||
]
|
||||
store_user_preferences(messages)
|
||||
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
import asyncio
|
||||
import warnings
|
||||
|
||||
from google.adk.agents import Agent
|
||||
from google.adk.runners import Runner
|
||||
from google.adk.sessions import InMemorySessionService
|
||||
from google.genai import types
|
||||
|
||||
from mem0 import MemoryClient
|
||||
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||
|
||||
|
||||
# Initialize Mem0 client
|
||||
mem0_client = MemoryClient()
|
||||
|
||||
|
||||
# Define Memory Tools
|
||||
def save_patient_info(information: str) -> dict:
|
||||
"""Saves important patient information to memory."""
|
||||
print(f"Storing patient information: {information[:30]}...")
|
||||
|
||||
# Get user_id from session state or use default
|
||||
user_id = getattr(save_patient_info, "user_id", "default_user")
|
||||
|
||||
# Store in Mem0
|
||||
mem0_client.add(
|
||||
[{"role": "user", "content": information}],
|
||||
user_id=user_id,
|
||||
run_id="healthcare_session",
|
||||
metadata={"type": "patient_information"},
|
||||
)
|
||||
|
||||
return {"status": "success", "message": "Information saved"}
|
||||
|
||||
|
||||
def retrieve_patient_info(query: str) -> str:
|
||||
"""Retrieves relevant patient information from memory."""
|
||||
print(f"Searching for patient information: {query}")
|
||||
|
||||
# Get user_id from session state or use default
|
||||
user_id = getattr(retrieve_patient_info, "user_id", "default_user")
|
||||
|
||||
# Search Mem0
|
||||
results = mem0_client.search(
|
||||
query,
|
||||
user_id=user_id,
|
||||
run_id="healthcare_session",
|
||||
limit=5,
|
||||
threshold=0.7, # Higher threshold for more relevant results
|
||||
)
|
||||
|
||||
if not results:
|
||||
return "I don't have any relevant memories about this topic."
|
||||
|
||||
memories = [f"• {result['memory']}" for result in results]
|
||||
return "Here's what I remember that might be relevant:\n" + "\n".join(memories)
|
||||
|
||||
|
||||
# Define Healthcare Tools
|
||||
def schedule_appointment(date: str, time: str, reason: str) -> dict:
|
||||
"""Schedules a doctor's appointment."""
|
||||
# In a real app, this would connect to a scheduling system
|
||||
appointment_id = f"APT-{hash(date + time) % 10000}"
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"appointment_id": appointment_id,
|
||||
"confirmation": f"Appointment scheduled for {date} at {time} for {reason}",
|
||||
"message": "Please arrive 15 minutes early to complete paperwork.",
|
||||
}
|
||||
|
||||
|
||||
# Create the Healthcare Assistant Agent
|
||||
healthcare_agent = Agent(
|
||||
name="healthcare_assistant",
|
||||
model="gemini-1.5-flash", # Using Gemini for healthcare assistant
|
||||
description="Healthcare assistant that helps patients with health information and appointment scheduling.",
|
||||
instruction="""You are a helpful Healthcare Assistant with memory capabilities.
|
||||
|
||||
Your primary responsibilities are to:
|
||||
1. Remember patient information using the 'save_patient_info' tool when they share symptoms, conditions, or preferences.
|
||||
2. Retrieve past patient information using the 'retrieve_patient_info' tool when relevant to the current conversation.
|
||||
3. Help schedule appointments using the 'schedule_appointment' tool.
|
||||
|
||||
IMPORTANT GUIDELINES:
|
||||
- Always be empathetic, professional, and helpful.
|
||||
- Save important patient information like symptoms, conditions, allergies, and preferences.
|
||||
- Check if you have relevant patient information before asking for details they may have shared previously.
|
||||
- Make it clear you are not a doctor and cannot provide medical diagnosis or treatment.
|
||||
- For serious symptoms, always recommend consulting a healthcare professional.
|
||||
- Keep all patient information confidential.
|
||||
""",
|
||||
tools=[save_patient_info, retrieve_patient_info, schedule_appointment],
|
||||
)
|
||||
|
||||
# Set Up Session and Runner
|
||||
session_service = InMemorySessionService()
|
||||
|
||||
# Define constants for the conversation
|
||||
APP_NAME = "healthcare_assistant_app"
|
||||
USER_ID = "Alex"
|
||||
SESSION_ID = "session_001"
|
||||
|
||||
# Create a session
|
||||
session = session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
|
||||
|
||||
# Create the runner
|
||||
runner = Runner(agent=healthcare_agent, app_name=APP_NAME, session_service=session_service)
|
||||
|
||||
|
||||
# Interact with the Healthcare Assistant
|
||||
async def call_agent_async(query, runner, user_id, session_id):
|
||||
"""Sends a query to the agent and returns the final response."""
|
||||
print(f"\n>>> Patient: {query}")
|
||||
|
||||
# Format the user's message
|
||||
content = types.Content(role="user", parts=[types.Part(text=query)])
|
||||
|
||||
# Set user_id for tools to access
|
||||
save_patient_info.user_id = user_id
|
||||
retrieve_patient_info.user_id = user_id
|
||||
|
||||
# Run the agent
|
||||
async for event in runner.run_async(user_id=user_id, session_id=session_id, new_message=content):
|
||||
if event.is_final_response():
|
||||
if event.content and event.content.parts:
|
||||
response = event.content.parts[0].text
|
||||
print(f"<<< Assistant: {response}")
|
||||
return response
|
||||
|
||||
return "No response received."
|
||||
|
||||
|
||||
# Example conversation flow
|
||||
async def run_conversation():
|
||||
# First interaction - patient introduces themselves with key information
|
||||
await call_agent_async(
|
||||
"Hi, I'm Alex. I've been having headaches for the past week, and I have a penicillin allergy.",
|
||||
runner=runner,
|
||||
user_id=USER_ID,
|
||||
session_id=SESSION_ID,
|
||||
)
|
||||
|
||||
# Request for health information
|
||||
await call_agent_async(
|
||||
"Can you tell me more about what might be causing my headaches?",
|
||||
runner=runner,
|
||||
user_id=USER_ID,
|
||||
session_id=SESSION_ID,
|
||||
)
|
||||
|
||||
# Schedule an appointment
|
||||
await call_agent_async(
|
||||
"I think I should see a doctor. Can you help me schedule an appointment for next Monday at 2pm?",
|
||||
runner=runner,
|
||||
user_id=USER_ID,
|
||||
session_id=SESSION_ID,
|
||||
)
|
||||
|
||||
# Test memory - should remember patient name, symptoms, and allergy
|
||||
await call_agent_async(
|
||||
"What medications should I avoid for my headaches?", runner=runner, user_id=USER_ID, session_id=SESSION_ID
|
||||
)
|
||||
|
||||
|
||||
# Interactive mode
|
||||
async def interactive_mode():
|
||||
"""Run an interactive chat session with the healthcare assistant."""
|
||||
print("=== Healthcare Assistant Interactive Mode ===")
|
||||
print("Enter 'exit' to quit at any time.")
|
||||
|
||||
# Get user information
|
||||
patient_id = input("Enter patient ID (or press Enter for default): ").strip() or USER_ID
|
||||
session_id = f"session_{hash(patient_id) % 1000:03d}"
|
||||
|
||||
# Create session for this user
|
||||
session_service.create_session(app_name=APP_NAME, user_id=patient_id, session_id=session_id)
|
||||
|
||||
print(f"\nStarting conversation with patient ID: {patient_id}")
|
||||
print("Type your message and press Enter.")
|
||||
|
||||
while True:
|
||||
user_input = input("\n>>> Patient: ").strip()
|
||||
if user_input.lower() in ["exit", "quit", "bye"]:
|
||||
print("Ending conversation. Thank you!")
|
||||
break
|
||||
|
||||
await call_agent_async(user_input, runner=runner, user_id=patient_id, session_id=session_id)
|
||||
|
||||
|
||||
# Main execution
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Healthcare Assistant with Memory")
|
||||
parser.add_argument("--demo", action="store_true", help="Run the demo conversation")
|
||||
parser.add_argument("--interactive", action="store_true", help="Run in interactive mode")
|
||||
parser.add_argument("--patient-id", type=str, default=USER_ID, help="Patient ID for the conversation")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.demo:
|
||||
asyncio.run(run_conversation())
|
||||
elif args.interactive:
|
||||
asyncio.run(interactive_mode())
|
||||
else:
|
||||
# Default to demo mode if no arguments provided
|
||||
asyncio.run(run_conversation())
|
||||
@@ -8,32 +8,29 @@ export XAI_API_KEY="your_xai_api_key"
|
||||
export MEM0_API_KEY="your_mem0_api_key"
|
||||
"""
|
||||
|
||||
from mem0 import Memory
|
||||
import os
|
||||
|
||||
from openai import OpenAI
|
||||
|
||||
from mem0 import Memory
|
||||
|
||||
# Configure Mem0 with Grok 3 and Qdrant
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "qdrant",
|
||||
"config": {
|
||||
"embedding_model_dims": 384
|
||||
}
|
||||
},
|
||||
"vector_store": {"provider": "qdrant", "config": {"embedding_model_dims": 384}},
|
||||
"llm": {
|
||||
"provider": "xai",
|
||||
"config": {
|
||||
"model": "grok-3-beta",
|
||||
"temperature": 0.1,
|
||||
"max_tokens": 2000,
|
||||
}
|
||||
},
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "huggingface",
|
||||
"config": {
|
||||
"model": "all-MiniLM-L6-v2" # open embedding model
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# Instantiate memory layer
|
||||
@@ -41,7 +38,7 @@ memory = Memory.from_config(config)
|
||||
|
||||
# Initialize Grok 3 client
|
||||
grok_client = OpenAI(
|
||||
api_key=XAI_API_KEY,
|
||||
api_key=os.getenv("XAI_API_KEY"),
|
||||
base_url="https://api.x.ai/v1",
|
||||
)
|
||||
|
||||
@@ -55,20 +52,14 @@ def recommend_movie_with_memory(user_id: str, user_query: str):
|
||||
prompt += f"\nPreviously, the user mentioned: {past_memories}"
|
||||
|
||||
# Generate movie recommendation using Grok 3
|
||||
response = grok_client.chat.completions.create(
|
||||
model="grok-3-beta",
|
||||
messages=[
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
)
|
||||
response = grok_client.chat.completions.create(model="grok-3-beta", messages=[{"role": "user", "content": prompt}])
|
||||
recommendation = response.choices[0].message.content
|
||||
|
||||
# Store conversation in memory
|
||||
memory.add(
|
||||
[{"role": "user", "content": user_query},
|
||||
{"role": "assistant", "content": recommendation}],
|
||||
[{"role": "user", "content": user_query}, {"role": "assistant", "content": recommendation}],
|
||||
user_id=user_id,
|
||||
metadata={"category": "movie"}
|
||||
metadata={"category": "movie"},
|
||||
)
|
||||
|
||||
return recommendation
|
||||
@@ -79,10 +70,11 @@ if __name__ == "__main__":
|
||||
user_id = "arshi"
|
||||
recommend_movie_with_memory(user_id, "I'm looking for a movie to watch tonight. Any suggestions?")
|
||||
# OUTPUT: You have watched Intersteller last weekend and you don't like horror movies, maybe you can watch "Purple Hearts" today.
|
||||
recommend_movie_with_memory(user_id, "Can we skip the tearjerkers? I really enjoyed Notting Hill and Crazy Rich Asians.")
|
||||
recommend_movie_with_memory(
|
||||
user_id, "Can we skip the tearjerkers? I really enjoyed Notting Hill and Crazy Rich Asians."
|
||||
)
|
||||
# OUTPUT: Got it — no sad endings! You might enjoy "The Proposal" or "Love, Rosie". They’re both light-hearted romcoms with happy vibes.
|
||||
recommend_movie_with_memory(user_id, "Any light-hearted movie I can watch after work today?")
|
||||
# OUTPUT: Since you liked Crazy Rich Asians and The Proposal, how about "The Intern" or "Isn’t It Romantic"? Both are upbeat, funny, and perfect for relaxing.
|
||||
recommend_movie_with_memory(user_id, "I’ve already watched The Intern. Something new maybe?")
|
||||
# OUTPUT: No problem! Try "Your Place or Mine" - romcoms that match your taste and are tear-free!
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ from pathlib import Path
|
||||
from agno.agent import Agent
|
||||
from agno.media import Image
|
||||
from agno.models.openai import OpenAIChat
|
||||
from mem0 import MemoryClient
|
||||
|
||||
from mem0 import MemoryClient
|
||||
|
||||
# Initialize the Mem0 client
|
||||
client = MemoryClient()
|
||||
@@ -23,8 +23,8 @@ agent = Agent(
|
||||
name="Personal Agent",
|
||||
model=OpenAIChat(id="gpt-4o"),
|
||||
description="You are a helpful personal agent that helps me with day to day activities."
|
||||
"You can process both text and images.",
|
||||
markdown=True
|
||||
"You can process both text and images.",
|
||||
markdown=True,
|
||||
)
|
||||
|
||||
|
||||
@@ -35,24 +35,16 @@ def chat_user(user_input: str = None, user_id: str = "user_123", image_path: str
|
||||
base64_image = base64.b64encode(image_file.read()).decode("utf-8")
|
||||
|
||||
# First: the text message
|
||||
text_msg = {
|
||||
"role": "user",
|
||||
"content": user_input
|
||||
}
|
||||
text_msg = {"role": "user", "content": user_input}
|
||||
|
||||
# Second: the image message
|
||||
image_msg = {
|
||||
"role": "user",
|
||||
"content": {
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": f"data:image/jpeg;base64,{base64_image}"
|
||||
}
|
||||
}
|
||||
"content": {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}},
|
||||
}
|
||||
|
||||
# Send both as separate message objects
|
||||
client.add([text_msg, image_msg], user_id=user_id, output_format='v1.1')
|
||||
client.add([text_msg, image_msg], user_id=user_id, output_format="v1.1")
|
||||
print("✅ Image uploaded and stored in memory.")
|
||||
|
||||
if user_input:
|
||||
@@ -92,10 +84,13 @@ print(chat_user("When is my test?", user_id=user_id))
|
||||
# OUTPUT: Your pilot's test is on your birthday, which is in five days. You're turning 25!
|
||||
# Good luck with your preparations, and remember to take some time to relax amidst the studying.
|
||||
|
||||
print(chat_user("This is the picture of what I brought with me in the trip to Bahamas",
|
||||
image_path="travel_items.jpeg", # this will be added to Mem0 memory
|
||||
user_id=user_id))
|
||||
print(chat_user("hey can you quickly tell me if brought my sunglasses to my trip, not able to find",
|
||||
user_id=user_id))
|
||||
print(
|
||||
chat_user(
|
||||
"This is the picture of what I brought with me in the trip to Bahamas",
|
||||
image_path="travel_items.jpeg", # this will be added to Mem0 memory
|
||||
user_id=user_id,
|
||||
)
|
||||
)
|
||||
print(chat_user("hey can you quickly tell me if brought my sunglasses to my trip, not able to find", user_id=user_id))
|
||||
# OUTPUT: Yes, you did bring your sunglasses on your trip to the Bahamas along with your laptop, face masks and other items..
|
||||
# Since you can't find them now, perhaps check the pockets of jackets you wore or in your luggage compartments.
|
||||
|
||||
@@ -7,11 +7,12 @@ In order to run this file, you need to set up your Mem0 API at Mem0 platform and
|
||||
export OPENAI_API_KEY="your_openai_api_key"
|
||||
export MEM0_API_KEY="your_mem0_api_key"
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from mem0 import MemoryClient
|
||||
from agents import Agent, Runner
|
||||
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient()
|
||||
|
||||
@@ -23,25 +24,19 @@ study_agent = Agent(
|
||||
- Identify topics the user has struggled with (e.g., "I'm confused", "this is hard")
|
||||
- Help with spaced repetition by suggesting topics to revisit based on last review time
|
||||
- Personalize answers using stored memories
|
||||
- Summarize PDFs or notes the user uploads""")
|
||||
- Summarize PDFs or notes the user uploads""",
|
||||
)
|
||||
|
||||
|
||||
# Upload and store PDF to Mem0
|
||||
def upload_pdf(pdf_url: str, user_id: str):
|
||||
pdf_message = {
|
||||
"role": "user",
|
||||
"content": {
|
||||
"type": "pdf_url",
|
||||
"pdf_url": {"url": pdf_url}
|
||||
}
|
||||
}
|
||||
pdf_message = {"role": "user", "content": {"type": "pdf_url", "pdf_url": {"url": pdf_url}}}
|
||||
client.add([pdf_message], user_id=user_id)
|
||||
print("✅ PDF uploaded and processed into memory.")
|
||||
|
||||
|
||||
# Main interaction loop with your personal study buddy
|
||||
async def study_buddy(user_id: str, topic: str, user_input: str):
|
||||
|
||||
memories = client.search(f"{topic}", user_id=user_id)
|
||||
memory_context = "n".join(f"- {m['memory']}" for m in memories)
|
||||
|
||||
@@ -56,9 +51,11 @@ Now respond to the user's new question or comment:
|
||||
result = await Runner.run(study_agent, prompt)
|
||||
response = result.final_output
|
||||
|
||||
client.add([
|
||||
{"role": "user", "content": f'''Topic: {topic}nUser: {user_input}nnStudy Assistant: {response}'''}
|
||||
], user_id=user_id, metadata={"topic": topic})
|
||||
client.add(
|
||||
[{"role": "user", "content": f"""Topic: {topic}nUser: {user_input}nnStudy Assistant: {response}"""}],
|
||||
user_id=user_id,
|
||||
metadata={"topic": topic},
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
@@ -78,7 +75,12 @@ async def main():
|
||||
|
||||
# Demonstrate spaced repetition prompting
|
||||
topic = "Momentum Conservation"
|
||||
print(await study_buddy(user_id, topic, "I think we covered this last week. Is it time to review momentum conservation again?"))
|
||||
print(
|
||||
await study_buddy(
|
||||
user_id, topic, "I think we covered this last week. Is it time to review momentum conservation again?"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -21,11 +21,13 @@ You must also have:
|
||||
|
||||
import tempfile
|
||||
import wave
|
||||
|
||||
import pyaudio
|
||||
from elevenlabs.client import ElevenLabs
|
||||
from crewai import Agent, Crew, Process, Task
|
||||
from elevenlabs import play
|
||||
from crewai import Agent, Task, Crew, Process
|
||||
from elevenlabs.client import ElevenLabs
|
||||
from openai import OpenAI
|
||||
|
||||
from mem0 import MemoryClient
|
||||
|
||||
# ------------------ SETUP ------------------
|
||||
@@ -55,7 +57,7 @@ def initialize_memory():
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I prefer brief and concise responses without unnecessary explanations. I get frustrated when assistants are too wordy or repeat information I already know."
|
||||
"content": "I prefer brief and concise responses without unnecessary explanations. I get frustrated when assistants are too wordy or repeat information I already know.",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
@@ -63,7 +65,7 @@ def initialize_memory():
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I like to listen to jazz music when I'm working, especially artists like Miles Davis and John Coltrane. I find it helps me focus and be more productive."
|
||||
"content": "I like to listen to jazz music when I'm working, especially artists like Miles Davis and John Coltrane. I find it helps me focus and be more productive.",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
@@ -71,7 +73,7 @@ def initialize_memory():
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I usually wake up at 7 AM and prefer reminders for meetings 30 minutes in advance. My most productive hours are between 9 AM and noon, so I try to schedule important tasks during that time."
|
||||
"content": "I usually wake up at 7 AM and prefer reminders for meetings 30 minutes in advance. My most productive hours are between 9 AM and noon, so I try to schedule important tasks during that time.",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
@@ -79,7 +81,7 @@ def initialize_memory():
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "My favorite color is navy blue, and I prefer dark mode in all my apps. I'm allergic to peanuts, so please remind me to check ingredients when I ask about recipes or restaurants."
|
||||
"content": "My favorite color is navy blue, and I prefer dark mode in all my apps. I'm allergic to peanuts, so please remind me to check ingredients when I ask about recipes or restaurants.",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
@@ -87,7 +89,7 @@ def initialize_memory():
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "My partner's name is Jamie, and we have a golden retriever named Max who is 3 years old. My parents live in Chicago, and I try to visit them once every two months."
|
||||
"content": "My partner's name is Jamie, and we have a golden retriever named Max who is 3 years old. My parents live in Chicago, and I try to visit them once every two months.",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
@@ -133,11 +135,11 @@ def record_audio(filename="input.wav", record_seconds=5):
|
||||
stream.close()
|
||||
p.terminate()
|
||||
|
||||
with wave.open(filename, 'wb') as wf:
|
||||
with wave.open(filename, "wb") as wf:
|
||||
wf.setnchannels(channels)
|
||||
wf.setsampwidth(p.get_sample_size(fmt))
|
||||
wf.setframerate(rate)
|
||||
wf.writeframes(b''.join(frames))
|
||||
wf.writeframes(b"".join(frames))
|
||||
|
||||
|
||||
# ------------------ STT USING WHISPER ------------------
|
||||
@@ -145,10 +147,7 @@ def transcribe_whisper(audio_path):
|
||||
print("🔎 Transcribing with Whisper...")
|
||||
try:
|
||||
with open(audio_path, "rb") as audio_file:
|
||||
transcript = openai_client.audio.transcriptions.create(
|
||||
model="whisper-1",
|
||||
file=audio_file
|
||||
)
|
||||
transcript = openai_client.audio.transcriptions.create(model="whisper-1", file=audio_file)
|
||||
print(f"🗣️ You said: {transcript.text}")
|
||||
return transcript.text
|
||||
except Exception as e:
|
||||
@@ -163,9 +162,7 @@ def get_agent_response(user_input):
|
||||
|
||||
try:
|
||||
task = Task(
|
||||
description=f"Respond to: {user_input}",
|
||||
expected_output="A short and relevant reply.",
|
||||
agent=voice_agent
|
||||
description=f"Respond to: {user_input}", expected_output="A short and relevant reply.", agent=voice_agent
|
||||
)
|
||||
crew = Crew(
|
||||
agents=[voice_agent],
|
||||
@@ -173,22 +170,19 @@ def get_agent_response(user_input):
|
||||
process=Process.sequential,
|
||||
verbose=True,
|
||||
memory=True,
|
||||
memory_config={
|
||||
"provider": "mem0",
|
||||
"config": {"user_id": USER_ID}
|
||||
}
|
||||
memory_config={"provider": "mem0", "config": {"user_id": USER_ID}},
|
||||
)
|
||||
result = crew.kickoff()
|
||||
|
||||
# Extract the text response from the complex result object
|
||||
if hasattr(result, 'raw'):
|
||||
if hasattr(result, "raw"):
|
||||
return result.raw
|
||||
elif isinstance(result, dict) and 'raw' in result:
|
||||
return result['raw']
|
||||
elif isinstance(result, dict) and 'tasks_output' in result:
|
||||
outputs = result['tasks_output']
|
||||
elif isinstance(result, dict) and "raw" in result:
|
||||
return result["raw"]
|
||||
elif isinstance(result, dict) and "tasks_output" in result:
|
||||
outputs = result["tasks_output"]
|
||||
if outputs and isinstance(outputs, list) and len(outputs) > 0:
|
||||
return outputs[0].get('raw', str(result))
|
||||
return outputs[0].get("raw", str(result))
|
||||
|
||||
# Fallback to string representation if we can't extract the raw response
|
||||
return str(result)
|
||||
@@ -202,10 +196,7 @@ def get_agent_response(user_input):
|
||||
def speak_response(text):
|
||||
print(f"🤖 Agent: {text}")
|
||||
audio = tts_client.text_to_speech.convert(
|
||||
text=text,
|
||||
voice_id="JBFqnCBsd6RMkjVDRZzb",
|
||||
model_id="eleven_multilingual_v2",
|
||||
output_format="mp3_44100_128"
|
||||
text=text, voice_id="JBFqnCBsd6RMkjVDRZzb", model_id="eleven_multilingual_v2", output_format="mp3_44100_128"
|
||||
)
|
||||
play(audio)
|
||||
|
||||
@@ -218,7 +209,7 @@ def run_voice_agent():
|
||||
record_audio(tmp_audio.name)
|
||||
try:
|
||||
user_text = transcribe_whisper(tmp_audio.name)
|
||||
if user_text.lower() in ['exit', 'quit', 'stop']:
|
||||
if user_text.lower() in ["exit", "quit", "stop"]:
|
||||
print("👋 Exiting.")
|
||||
break
|
||||
response = get_agent_response(user_text)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mem0ai",
|
||||
"version": "2.1.22",
|
||||
"version": "2.1.26",
|
||||
"description": "The Memory Layer For Your AI Apps",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
|
||||
+113
-41
@@ -160,15 +160,11 @@ export default class MemoryClient {
|
||||
}
|
||||
|
||||
_preparePayload(
|
||||
messages: string | Array<Message>,
|
||||
messages: Array<Message>,
|
||||
options: MemoryOptions,
|
||||
): object {
|
||||
const payload: any = {};
|
||||
if (typeof messages === "string") {
|
||||
payload.messages = [{ role: "user", content: messages }];
|
||||
} else if (Array.isArray(messages)) {
|
||||
payload.messages = messages;
|
||||
}
|
||||
payload.messages = messages;
|
||||
return { ...payload, ...options };
|
||||
}
|
||||
|
||||
@@ -179,27 +175,45 @@ export default class MemoryClient {
|
||||
}
|
||||
|
||||
async ping(): Promise<void> {
|
||||
const response = await fetch(`${this.host}/v1/ping/`, {
|
||||
headers: {
|
||||
Authorization: `Token ${this.apiKey}`,
|
||||
},
|
||||
});
|
||||
try {
|
||||
const response = await this._fetchWithErrorHandling(
|
||||
`${this.host}/v1/ping/`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Token ${this.apiKey}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const data = await response.json();
|
||||
if (!response || typeof response !== "object") {
|
||||
throw new APIError("Invalid response format from ping endpoint");
|
||||
}
|
||||
|
||||
if (data.status !== "ok") {
|
||||
throw new Error("API Key is invalid");
|
||||
if (response.status !== "ok") {
|
||||
throw new APIError(response.message || "API Key is invalid");
|
||||
}
|
||||
|
||||
const { org_id, project_id, user_email } = response;
|
||||
|
||||
// Only update if values are actually present
|
||||
if (org_id && !this.organizationId) this.organizationId = org_id;
|
||||
if (project_id && !this.projectId) this.projectId = project_id;
|
||||
if (user_email) this.telemetryId = user_email;
|
||||
} catch (error: any) {
|
||||
// Convert generic errors to APIError with meaningful messages
|
||||
if (error instanceof APIError) {
|
||||
throw error;
|
||||
} else {
|
||||
throw new APIError(
|
||||
`Failed to ping server: ${error.message || "Unknown error"}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const { org_id, project_id, user_email } = data;
|
||||
|
||||
this.organizationId = this.organizationId || org_id || null;
|
||||
this.projectId = this.projectId || project_id || null;
|
||||
this.telemetryId = user_email || "";
|
||||
}
|
||||
|
||||
async add(
|
||||
messages: string | Array<Message>,
|
||||
messages: Array<Message>,
|
||||
options: MemoryOptions = {},
|
||||
): Promise<Array<Memory>> {
|
||||
if (this.telemetryId === "") await this.ping();
|
||||
@@ -431,6 +445,9 @@ export default class MemoryClient {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated The method should not be used, use `deleteUsers` instead. This will be removed in version 2.2.0.
|
||||
*/
|
||||
async deleteUser(data: {
|
||||
entity_id: number;
|
||||
entity_type: string;
|
||||
@@ -450,31 +467,86 @@ export default class MemoryClient {
|
||||
return response;
|
||||
}
|
||||
|
||||
async deleteUsers(): Promise<{ message: string }> {
|
||||
async deleteUsers(
|
||||
params: {
|
||||
user_id?: string;
|
||||
agent_id?: string;
|
||||
app_id?: string;
|
||||
run_id?: string;
|
||||
} = {},
|
||||
): Promise<{ message: string }> {
|
||||
if (this.telemetryId === "") await this.ping();
|
||||
this._validateOrgProject();
|
||||
this._captureEvent("delete_users", []);
|
||||
const entities = await this.users();
|
||||
|
||||
for (const entity of entities.results) {
|
||||
let options: MemoryOptions = {};
|
||||
if (this.organizationName != null && this.projectName != null) {
|
||||
options.org_name = this.organizationName;
|
||||
options.project_name = this.projectName;
|
||||
}
|
||||
let to_delete: Array<{ type: string; name: string }> = [];
|
||||
const { user_id, agent_id, app_id, run_id } = params;
|
||||
|
||||
if (this.organizationId != null && this.projectId != null) {
|
||||
options.org_id = this.organizationId;
|
||||
options.project_id = this.projectId;
|
||||
|
||||
if (options.org_name) delete options.org_name;
|
||||
if (options.project_name) delete options.project_name;
|
||||
}
|
||||
await this.client.delete(`/v1/entities/${entity.type}/${entity.id}/`, {
|
||||
params: options,
|
||||
});
|
||||
if (user_id) {
|
||||
to_delete = [{ type: "user", name: user_id }];
|
||||
} else if (agent_id) {
|
||||
to_delete = [{ type: "agent", name: agent_id }];
|
||||
} else if (app_id) {
|
||||
to_delete = [{ type: "app", name: app_id }];
|
||||
} else if (run_id) {
|
||||
to_delete = [{ type: "run", name: run_id }];
|
||||
} else {
|
||||
const entities = await this.users();
|
||||
to_delete = entities.results.map((entity) => ({
|
||||
type: entity.type,
|
||||
name: entity.name,
|
||||
}));
|
||||
}
|
||||
return { message: "All users, agents, and sessions deleted." };
|
||||
|
||||
if (to_delete.length === 0) {
|
||||
throw new Error("No entities to delete");
|
||||
}
|
||||
|
||||
const requestOptions: MemoryOptions = {};
|
||||
if (this.organizationName != null && this.projectName != null) {
|
||||
requestOptions.org_name = this.organizationName;
|
||||
requestOptions.project_name = this.projectName;
|
||||
}
|
||||
|
||||
if (this.organizationId != null && this.projectId != null) {
|
||||
requestOptions.org_id = this.organizationId;
|
||||
requestOptions.project_id = this.projectId;
|
||||
|
||||
if (requestOptions.org_name) delete requestOptions.org_name;
|
||||
if (requestOptions.project_name) delete requestOptions.project_name;
|
||||
}
|
||||
|
||||
// Delete each entity and handle errors
|
||||
for (const entity of to_delete) {
|
||||
try {
|
||||
await this.client.delete(
|
||||
`/v2/entities/${entity.type}/${entity.name}/`,
|
||||
{
|
||||
params: requestOptions,
|
||||
},
|
||||
);
|
||||
} catch (error: any) {
|
||||
throw new APIError(
|
||||
`Failed to delete ${entity.type} ${entity.name}: ${error.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this._captureEvent("delete_users", [
|
||||
{
|
||||
user_id: user_id,
|
||||
agent_id: agent_id,
|
||||
app_id: app_id,
|
||||
run_id: run_id,
|
||||
sync_type: "sync",
|
||||
},
|
||||
]);
|
||||
|
||||
return {
|
||||
message:
|
||||
user_id || agent_id || app_id || run_id
|
||||
? "Entity deleted successfully."
|
||||
: "All users, agents, apps and runs deleted.",
|
||||
};
|
||||
}
|
||||
|
||||
async batchUpdate(memories: Array<MemoryUpdateBody>): Promise<string> {
|
||||
|
||||
@@ -22,12 +22,18 @@ export interface MemoryOptions {
|
||||
custom_categories?: custom_categories[];
|
||||
custom_instructions?: string;
|
||||
timestamp?: number;
|
||||
output_format?: string | OutputFormat;
|
||||
}
|
||||
|
||||
export interface ProjectOptions {
|
||||
fields?: string[];
|
||||
}
|
||||
|
||||
export enum OutputFormat {
|
||||
V1 = "v1.0",
|
||||
V1_1 = "v1.1",
|
||||
}
|
||||
|
||||
export enum API_VERSION {
|
||||
V1 = "v1",
|
||||
V2 = "v2",
|
||||
@@ -47,7 +53,7 @@ export interface MultiModalMessages {
|
||||
}
|
||||
|
||||
export interface Messages {
|
||||
role: string;
|
||||
role: "user" | "assistant";
|
||||
content: string | MultiModalMessages;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import type { TelemetryClient, TelemetryOptions } from "./telemetry.types";
|
||||
|
||||
let version = "2.1.16";
|
||||
let version = "2.1.26";
|
||||
|
||||
// Safely check for process.env in different environments
|
||||
let MEM0_TELEMETRY = true;
|
||||
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
TelemetryEventData,
|
||||
} from "./telemetry.types";
|
||||
|
||||
let version = "2.1.16";
|
||||
let version = "2.1.26";
|
||||
|
||||
// Safely check for process.env in different environments
|
||||
let MEM0_TELEMETRY = true;
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@ import importlib.metadata
|
||||
__version__ = importlib.metadata.version("mem0ai")
|
||||
|
||||
from mem0.client.main import AsyncMemoryClient, MemoryClient # noqa
|
||||
from mem0.memory.main import Memory, AsyncMemory # noqa
|
||||
from mem0.memory.main import AsyncMemory, Memory # noqa
|
||||
|
||||
+258
-109
@@ -1,11 +1,12 @@
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import warnings
|
||||
import hashlib
|
||||
from functools import wraps
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
import httpx
|
||||
import requests
|
||||
|
||||
from mem0.memory.setup import get_user_id, setup_config
|
||||
from mem0.memory.telemetry import capture_client_event
|
||||
@@ -62,6 +63,7 @@ class MemoryClient:
|
||||
host: Optional[str] = None,
|
||||
org_id: Optional[str] = None,
|
||||
project_id: Optional[str] = None,
|
||||
client: Optional[httpx.Client] = None,
|
||||
):
|
||||
"""Initialize the MemoryClient.
|
||||
|
||||
@@ -71,6 +73,8 @@ class MemoryClient:
|
||||
host: The base URL for the Mem0 API. Defaults to "https://api.mem0.ai".
|
||||
org_id: The ID of the organization.
|
||||
project_id: The ID of the project.
|
||||
client: A custom httpx.Client instance. If provided, it will be used instead of creating a new one.
|
||||
Note that base_url and headers will be set/overridden as needed.
|
||||
|
||||
Raises:
|
||||
ValueError: If no API key is provided or found in the environment.
|
||||
@@ -87,11 +91,17 @@ class MemoryClient:
|
||||
# Create MD5 hash of API key for user_id
|
||||
self.user_id = hashlib.md5(self.api_key.encode()).hexdigest()
|
||||
|
||||
self.client = httpx.Client(
|
||||
base_url=self.host,
|
||||
headers={"Authorization": f"Token {self.api_key}", "Mem0-User-ID": self.user_id},
|
||||
timeout=300,
|
||||
)
|
||||
if client is not None:
|
||||
self.client = client
|
||||
# Ensure the client has the correct base_url and headers
|
||||
self.client.base_url = httpx.URL(self.host)
|
||||
self.client.headers.update({"Authorization": f"Token {self.api_key}", "Mem0-User-ID": self.user_id})
|
||||
else:
|
||||
self.client = httpx.Client(
|
||||
base_url=self.host,
|
||||
headers={"Authorization": f"Token {self.api_key}", "Mem0-User-ID": self.user_id},
|
||||
timeout=300,
|
||||
)
|
||||
self.user_email = self._validate_api_key()
|
||||
capture_client_event("client.init", self, {"sync_type": "sync"})
|
||||
|
||||
@@ -119,11 +129,11 @@ class MemoryClient:
|
||||
raise ValueError(f"Error: {error_message}")
|
||||
|
||||
@api_error_handler
|
||||
def add(self, messages: Union[str, List[Dict[str, str]]], **kwargs) -> Dict[str, Any]:
|
||||
def add(self, messages: List[Dict[str, str]], **kwargs) -> Dict[str, Any]:
|
||||
"""Add a new memory.
|
||||
|
||||
Args:
|
||||
messages: Either a string message or a list of message dictionaries.
|
||||
messages: A list of message dictionaries.
|
||||
**kwargs: Additional parameters such as user_id, agent_id, app_id, metadata, filters.
|
||||
|
||||
Returns:
|
||||
@@ -224,7 +234,9 @@ class MemoryClient:
|
||||
response.raise_for_status()
|
||||
if "metadata" in kwargs:
|
||||
del kwargs["metadata"]
|
||||
capture_client_event("client.search", self, {"api_version": version, "keys": list(kwargs.keys()), "sync_type": "sync"})
|
||||
capture_client_event(
|
||||
"client.search", self, {"api_version": version, "keys": list(kwargs.keys()), "sync_type": "sync"}
|
||||
)
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
@@ -332,33 +344,34 @@ class MemoryClient:
|
||||
ValueError: If specified entity not found
|
||||
APIError: If deletion fails
|
||||
"""
|
||||
|
||||
if user_id:
|
||||
to_delete = [{"type": "user", "name": user_id}]
|
||||
elif agent_id:
|
||||
to_delete = [{"type": "agent", "name": agent_id}]
|
||||
elif app_id:
|
||||
to_delete = [{"type": "app", "name": app_id}]
|
||||
elif run_id:
|
||||
to_delete = [{"type": "run", "name": run_id}]
|
||||
else:
|
||||
entities = self.users()
|
||||
# Filter entities based on provided IDs using list comprehension
|
||||
to_delete = [{"type": entity["type"], "name": entity["name"]} for entity in entities["results"]]
|
||||
|
||||
params = self._prepare_params()
|
||||
entities = self.users()
|
||||
|
||||
# Filter entities based on provided IDs using list comprehension
|
||||
to_delete = [
|
||||
entity
|
||||
for entity in entities["results"]
|
||||
if (user_id and entity["type"] == "user" and entity["name"] == user_id)
|
||||
or (agent_id and entity["type"] == "agent" and entity["name"] == agent_id)
|
||||
or (app_id and entity["type"] == "app" and entity["name"] == app_id)
|
||||
or (run_id and entity["type"] == "run" and entity["name"] == run_id)
|
||||
]
|
||||
|
||||
# If filters provided but no matches found, raise error
|
||||
if not to_delete and (user_id or agent_id or app_id or run_id):
|
||||
raise ValueError("No entity found with the provided ID.")
|
||||
# If no filters provided, delete all entities
|
||||
elif not to_delete:
|
||||
to_delete = entities["results"]
|
||||
if not to_delete:
|
||||
raise ValueError("No entities to delete")
|
||||
|
||||
# Delete entities and check response immediately
|
||||
for entity in to_delete:
|
||||
response = self.client.delete(f"/v1/entities/{entity['type']}/{entity['id']}/", params=params)
|
||||
response = self.client.delete(f"/v2/entities/{entity['type']}/{entity['name']}/", params=params)
|
||||
response.raise_for_status()
|
||||
|
||||
capture_client_event(
|
||||
"client.delete_users", self, {"user_id": user_id, "agent_id": agent_id, "app_id": app_id, "run_id": run_id, "sync_type": "sync"}
|
||||
"client.delete_users",
|
||||
self,
|
||||
{"user_id": user_id, "agent_id": agent_id, "app_id": app_id, "run_id": run_id, "sync_type": "sync"},
|
||||
)
|
||||
return {
|
||||
"message": "Entity deleted successfully."
|
||||
@@ -439,7 +452,9 @@ class MemoryClient:
|
||||
"""
|
||||
response = self.client.post("/v1/exports/", json={"schema": schema, **self._prepare_params(kwargs)})
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.create_memory_export", self, {"schema": schema, "keys": list(kwargs.keys()), "sync_type": "sync"})
|
||||
capture_client_event(
|
||||
"client.create_memory_export", self, {"schema": schema, "keys": list(kwargs.keys()), "sync_type": "sync"}
|
||||
)
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
@@ -512,7 +527,11 @@ class MemoryClient:
|
||||
)
|
||||
|
||||
payload = self._prepare_params(
|
||||
{"custom_instructions": custom_instructions, "custom_categories": custom_categories, "retrieval_criteria": retrieval_criteria}
|
||||
{
|
||||
"custom_instructions": custom_instructions,
|
||||
"custom_categories": custom_categories,
|
||||
"retrieval_criteria": retrieval_criteria,
|
||||
}
|
||||
)
|
||||
response = self.client.patch(
|
||||
f"/api/v1/orgs/organizations/{self.org_id}/projects/{self.project_id}/",
|
||||
@@ -522,7 +541,12 @@ class MemoryClient:
|
||||
capture_client_event(
|
||||
"client.update_project",
|
||||
self,
|
||||
{"custom_instructions": custom_instructions, "custom_categories": custom_categories, "retrieval_criteria": retrieval_criteria, "sync_type": "sync"},
|
||||
{
|
||||
"custom_instructions": custom_instructions,
|
||||
"custom_categories": custom_categories,
|
||||
"retrieval_criteria": retrieval_criteria,
|
||||
"sync_type": "sync",
|
||||
},
|
||||
)
|
||||
return response.json()
|
||||
|
||||
@@ -643,7 +667,7 @@ class MemoryClient:
|
||||
return response.json()
|
||||
|
||||
def _prepare_payload(
|
||||
self, messages: Union[str, List[Dict[str, str]], None], kwargs: Dict[str, Any]
|
||||
self, messages: List[Dict[str, str]], kwargs: Dict[str, Any]
|
||||
) -> Dict[str, Any]:
|
||||
"""Prepare the payload for API requests.
|
||||
|
||||
@@ -655,10 +679,7 @@ class MemoryClient:
|
||||
A dictionary containing the prepared payload.
|
||||
"""
|
||||
payload = {}
|
||||
if isinstance(messages, str):
|
||||
payload["messages"] = [{"role": "user", "content": messages}]
|
||||
elif isinstance(messages, list):
|
||||
payload["messages"] = messages
|
||||
payload["messages"] = messages
|
||||
|
||||
payload.update({k: v for k, v in kwargs.items() if v is not None})
|
||||
return payload
|
||||
@@ -694,10 +715,6 @@ class AsyncMemoryClient:
|
||||
|
||||
This class provides asynchronous versions of all MemoryClient methods.
|
||||
It uses httpx.AsyncClient for making non-blocking API requests.
|
||||
|
||||
Attributes:
|
||||
sync_client (MemoryClient): Underlying synchronous client instance.
|
||||
async_client (httpx.AsyncClient): Async HTTP client for making API requests.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
@@ -706,13 +723,119 @@ class AsyncMemoryClient:
|
||||
host: Optional[str] = None,
|
||||
org_id: Optional[str] = None,
|
||||
project_id: Optional[str] = None,
|
||||
client: Optional[httpx.AsyncClient] = None,
|
||||
):
|
||||
self.sync_client = MemoryClient(api_key, host, org_id, project_id)
|
||||
self.async_client = httpx.AsyncClient(
|
||||
base_url=self.sync_client.host,
|
||||
headers=self.sync_client.client.headers,
|
||||
timeout=300,
|
||||
)
|
||||
"""Initialize the AsyncMemoryClient.
|
||||
|
||||
Args:
|
||||
api_key: The API key for authenticating with the Mem0 API. If not provided,
|
||||
it will attempt to use the MEM0_API_KEY environment variable.
|
||||
host: The base URL for the Mem0 API. Defaults to "https://api.mem0.ai".
|
||||
org_id: The ID of the organization.
|
||||
project_id: The ID of the project.
|
||||
client: A custom httpx.AsyncClient instance. If provided, it will be used instead
|
||||
of creating a new one. Note that base_url and headers will be set/overridden
|
||||
as needed.
|
||||
|
||||
Raises:
|
||||
ValueError: If no API key is provided or found in the environment.
|
||||
"""
|
||||
self.api_key = api_key or os.getenv("MEM0_API_KEY")
|
||||
self.host = host or "https://api.mem0.ai"
|
||||
self.org_id = org_id
|
||||
self.project_id = project_id
|
||||
self.user_id = get_user_id()
|
||||
|
||||
if not self.api_key:
|
||||
raise ValueError("Mem0 API Key not provided. Please provide an API Key.")
|
||||
|
||||
# Create MD5 hash of API key for user_id
|
||||
self.user_id = hashlib.md5(self.api_key.encode()).hexdigest()
|
||||
|
||||
if client is not None:
|
||||
self.async_client = client
|
||||
# Ensure the client has the correct base_url and headers
|
||||
self.async_client.base_url = httpx.URL(self.host)
|
||||
self.async_client.headers.update({"Authorization": f"Token {self.api_key}", "Mem0-User-ID": self.user_id})
|
||||
else:
|
||||
self.async_client = httpx.AsyncClient(
|
||||
base_url=self.host,
|
||||
headers={"Authorization": f"Token {self.api_key}", "Mem0-User-ID": self.user_id},
|
||||
timeout=300,
|
||||
)
|
||||
|
||||
self.user_email = self._validate_api_key()
|
||||
capture_client_event("client.init", self, {"sync_type": "async"})
|
||||
|
||||
def _validate_api_key(self):
|
||||
"""Validate the API key by making a test request."""
|
||||
try:
|
||||
params = self._prepare_params()
|
||||
response = requests.get(
|
||||
f"{self.host}/v1/ping/",
|
||||
headers={"Authorization": f"Token {self.api_key}", "Mem0-User-ID": self.user_id},
|
||||
params=params,
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
if data.get("org_id") and data.get("project_id"):
|
||||
self.org_id = data.get("org_id")
|
||||
self.project_id = data.get("project_id")
|
||||
|
||||
return data.get("user_email")
|
||||
|
||||
except requests.HTTPStatusError as e:
|
||||
try:
|
||||
error_data = e.response.json()
|
||||
error_message = error_data.get("detail", str(e))
|
||||
except Exception:
|
||||
error_message = str(e)
|
||||
raise ValueError(f"Error: {error_message}")
|
||||
|
||||
def _prepare_payload(
|
||||
self, messages: List[Dict[str, str]], kwargs: Dict[str, Any]
|
||||
) -> Dict[str, Any]:
|
||||
"""Prepare the payload for API requests.
|
||||
|
||||
Args:
|
||||
messages: The messages to include in the payload.
|
||||
kwargs: Additional keyword arguments to include in the payload.
|
||||
|
||||
Returns:
|
||||
A dictionary containing the prepared payload.
|
||||
"""
|
||||
payload = {}
|
||||
payload["messages"] = messages
|
||||
|
||||
payload.update({k: v for k, v in kwargs.items() if v is not None})
|
||||
return payload
|
||||
|
||||
def _prepare_params(self, kwargs: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
||||
"""Prepare query parameters for API requests.
|
||||
|
||||
Args:
|
||||
kwargs: Keyword arguments to include in the parameters.
|
||||
|
||||
Returns:
|
||||
A dictionary containing the prepared parameters.
|
||||
|
||||
Raises:
|
||||
ValueError: If either org_id or project_id is provided but not both.
|
||||
"""
|
||||
|
||||
if kwargs is None:
|
||||
kwargs = {}
|
||||
|
||||
# Add org_id and project_id if both are available
|
||||
if self.org_id and self.project_id:
|
||||
kwargs["org_id"] = self.org_id
|
||||
kwargs["project_id"] = self.project_id
|
||||
elif self.org_id or self.project_id:
|
||||
raise ValueError("Please provide both org_id and project_id")
|
||||
|
||||
return {k: v for k, v in kwargs.items() if v is not None}
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
@@ -721,90 +844,103 @@ class AsyncMemoryClient:
|
||||
await self.async_client.aclose()
|
||||
|
||||
@api_error_handler
|
||||
async def add(self, messages: Union[str, List[Dict[str, str]]], **kwargs) -> Dict[str, Any]:
|
||||
kwargs = self.sync_client._prepare_params(kwargs)
|
||||
payload = self.sync_client._prepare_payload(messages, kwargs)
|
||||
async def add(self, messages: List[Dict[str, str]], **kwargs) -> Dict[str, Any]:
|
||||
kwargs = self._prepare_params(kwargs)
|
||||
if kwargs.get("output_format") != "v1.1":
|
||||
kwargs["output_format"] = "v1.1"
|
||||
warnings.warn(
|
||||
"output_format='v1.0' is deprecated therefore setting it to 'v1.1' by default."
|
||||
"Check out the docs for more information: https://docs.mem0.ai/platform/quickstart#4-1-create-memories",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
kwargs["version"] = "v2"
|
||||
payload = self._prepare_payload(messages, kwargs)
|
||||
response = await self.async_client.post("/v1/memories/", json=payload)
|
||||
response.raise_for_status()
|
||||
if "metadata" in kwargs:
|
||||
del kwargs["metadata"]
|
||||
capture_client_event("client.add", self.sync_client, {"keys": list(kwargs.keys()), "sync_type": "async"})
|
||||
capture_client_event("client.add", self, {"keys": list(kwargs.keys()), "sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
async def get(self, memory_id: str) -> Dict[str, Any]:
|
||||
params = self.sync_client._prepare_params()
|
||||
params = self._prepare_params()
|
||||
response = await self.async_client.get(f"/v1/memories/{memory_id}/", params=params)
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.get", self.sync_client, {"memory_id": memory_id, "sync_type": "async"})
|
||||
capture_client_event("client.get", self, {"memory_id": memory_id, "sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
async def get_all(self, version: str = "v1", **kwargs) -> List[Dict[str, Any]]:
|
||||
params = self.sync_client._prepare_params(kwargs)
|
||||
params = self._prepare_params(kwargs)
|
||||
if version == "v1":
|
||||
response = await self.async_client.get(f"/{version}/memories/", params=params)
|
||||
elif version == "v2":
|
||||
response = await self.async_client.post(f"/{version}/memories/", json=params)
|
||||
if "page" in params and "page_size" in params:
|
||||
query_params = {"page": params.pop("page"), "page_size": params.pop("page_size")}
|
||||
response = await self.async_client.post(f"/{version}/memories/", json=params, params=query_params)
|
||||
else:
|
||||
response = await self.async_client.post(f"/{version}/memories/", json=params)
|
||||
response.raise_for_status()
|
||||
if "metadata" in kwargs:
|
||||
del kwargs["metadata"]
|
||||
capture_client_event(
|
||||
"client.get_all", self.sync_client, {"api_version": version, "keys": list(kwargs.keys()), "sync_type": "async"}
|
||||
"client.get_all", self, {"api_version": version, "keys": list(kwargs.keys()), "sync_type": "async"}
|
||||
)
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
async def search(self, query: str, version: str = "v1", **kwargs) -> List[Dict[str, Any]]:
|
||||
payload = {"query": query}
|
||||
payload.update(self.sync_client._prepare_params(kwargs))
|
||||
payload.update(self._prepare_params(kwargs))
|
||||
response = await self.async_client.post(f"/{version}/memories/search/", json=payload)
|
||||
response.raise_for_status()
|
||||
if "metadata" in kwargs:
|
||||
del kwargs["metadata"]
|
||||
capture_client_event(
|
||||
"client.search", self.sync_client, {"api_version": version, "keys": list(kwargs.keys()), "sync_type": "async"}
|
||||
"client.search", self, {"api_version": version, "keys": list(kwargs.keys()), "sync_type": "async"}
|
||||
)
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
async def update(self, memory_id: str, data: str) -> Dict[str, Any]:
|
||||
params = self.sync_client._prepare_params()
|
||||
params = self._prepare_params()
|
||||
response = await self.async_client.put(f"/v1/memories/{memory_id}/", json={"text": data}, params=params)
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.update", self.sync_client, {"memory_id": memory_id, "sync_type": "async"})
|
||||
capture_client_event("client.update", self, {"memory_id": memory_id, "sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
async def delete(self, memory_id: str) -> Dict[str, Any]:
|
||||
params = self.sync_client._prepare_params()
|
||||
params = self._prepare_params()
|
||||
response = await self.async_client.delete(f"/v1/memories/{memory_id}/", params=params)
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.delete", self.sync_client, {"memory_id": memory_id, "sync_type": "async"})
|
||||
capture_client_event("client.delete", self, {"memory_id": memory_id, "sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
async def delete_all(self, **kwargs) -> Dict[str, str]:
|
||||
params = self.sync_client._prepare_params(kwargs)
|
||||
params = self._prepare_params(kwargs)
|
||||
response = await self.async_client.delete("/v1/memories/", params=params)
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.delete_all", self.sync_client, {"keys": list(kwargs.keys()), "sync_type": "async"})
|
||||
capture_client_event("client.delete_all", self, {"keys": list(kwargs.keys()), "sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
async def history(self, memory_id: str) -> List[Dict[str, Any]]:
|
||||
params = self.sync_client._prepare_params()
|
||||
params = self._prepare_params()
|
||||
response = await self.async_client.get(f"/v1/memories/{memory_id}/history/", params=params)
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.history", self.sync_client, {"memory_id": memory_id, "sync_type": "async"})
|
||||
capture_client_event("client.history", self, {"memory_id": memory_id, "sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
async def users(self) -> Dict[str, Any]:
|
||||
params = self.sync_client._prepare_params()
|
||||
params = self._prepare_params()
|
||||
response = await self.async_client.get("/v1/entities/", params=params)
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.users", self.sync_client, {"sync_type": "async"})
|
||||
capture_client_event("client.users", self, {"sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
@@ -830,32 +966,34 @@ class AsyncMemoryClient:
|
||||
ValueError: If specified entity not found
|
||||
APIError: If deletion fails
|
||||
"""
|
||||
params = self.sync_client._prepare_params()
|
||||
entities = await self.users()
|
||||
if user_id:
|
||||
to_delete = [{"type": "user", "name": user_id}]
|
||||
elif agent_id:
|
||||
to_delete = [{"type": "agent", "name": agent_id}]
|
||||
elif app_id:
|
||||
to_delete = [{"type": "app", "name": app_id}]
|
||||
elif run_id:
|
||||
to_delete = [{"type": "run", "name": run_id}]
|
||||
else:
|
||||
entities = await self.users()
|
||||
# Filter entities based on provided IDs using list comprehension
|
||||
to_delete = [{"type": entity["type"], "name": entity["name"]} for entity in entities["results"]]
|
||||
|
||||
# Filter entities based on provided IDs using list comprehension
|
||||
to_delete = [
|
||||
entity
|
||||
for entity in entities["results"]
|
||||
if (user_id and entity["type"] == "user" and entity["name"] == user_id)
|
||||
or (agent_id and entity["type"] == "agent" and entity["name"] == agent_id)
|
||||
or (app_id and entity["type"] == "app" and entity["name"] == app_id)
|
||||
or (run_id and entity["type"] == "run" and entity["name"] == run_id)
|
||||
]
|
||||
params = self._prepare_params()
|
||||
|
||||
# If filters provided but no matches found, raise error
|
||||
if not to_delete and (user_id or agent_id or app_id or run_id):
|
||||
raise ValueError("No entity found with the provided ID.")
|
||||
# If no filters provided, delete all entities
|
||||
elif not to_delete:
|
||||
to_delete = entities["results"]
|
||||
if not to_delete:
|
||||
raise ValueError("No entities to delete")
|
||||
|
||||
# Delete entities and check response immediately
|
||||
for entity in to_delete:
|
||||
response = await self.async_client.delete(f"/v1/entities/{entity['type']}/{entity['id']}/", params=params)
|
||||
response = await self.async_client.delete(f"/v2/entities/{entity['type']}/{entity['name']}/", params=params)
|
||||
response.raise_for_status()
|
||||
|
||||
capture_client_event("client.delete_users", self.sync_client, {"sync_type": "async"})
|
||||
capture_client_event(
|
||||
"client.delete_users",
|
||||
self,
|
||||
{"user_id": user_id, "agent_id": agent_id, "app_id": app_id, "run_id": run_id, "sync_type": "async"},
|
||||
)
|
||||
return {
|
||||
"message": "Entity deleted successfully."
|
||||
if (user_id or agent_id or app_id or run_id)
|
||||
@@ -865,7 +1003,7 @@ class AsyncMemoryClient:
|
||||
@api_error_handler
|
||||
async def reset(self) -> Dict[str, str]:
|
||||
await self.delete_users()
|
||||
capture_client_event("client.reset", self.sync_client, {"sync_type": "async"})
|
||||
capture_client_event("client.reset", self, {"sync_type": "async"})
|
||||
return {"message": "Client reset successful. All users and memories deleted."}
|
||||
|
||||
@api_error_handler
|
||||
@@ -886,7 +1024,7 @@ class AsyncMemoryClient:
|
||||
response = await self.async_client.put("/v1/batch/", json={"memories": memories})
|
||||
response.raise_for_status()
|
||||
|
||||
capture_client_event("client.batch_update", self.sync_client, {"sync_type": "async"})
|
||||
capture_client_event("client.batch_update", self, {"sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
@@ -906,7 +1044,7 @@ class AsyncMemoryClient:
|
||||
response = await self.async_client.request("DELETE", "/v1/batch/", json={"memories": memories})
|
||||
response.raise_for_status()
|
||||
|
||||
capture_client_event("client.batch_delete", self.sync_client, {"sync_type": "async"})
|
||||
capture_client_event("client.batch_delete", self, {"sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
@@ -923,7 +1061,7 @@ class AsyncMemoryClient:
|
||||
response = await self.async_client.post("/v1/exports/", json={"schema": schema, **self._prepare_params(kwargs)})
|
||||
response.raise_for_status()
|
||||
capture_client_event(
|
||||
"client.create_memory_export", self.sync_client, {"schema": schema, "keys": list(kwargs.keys()), "sync_type": "async"}
|
||||
"client.create_memory_export", self, {"schema": schema, "keys": list(kwargs.keys()), "sync_type": "async"}
|
||||
)
|
||||
return response.json()
|
||||
|
||||
@@ -939,29 +1077,31 @@ class AsyncMemoryClient:
|
||||
"""
|
||||
response = await self.async_client.post("/v1/exports/get/", json=self._prepare_params(kwargs))
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.get_memory_export", self.sync_client, {"keys": list(kwargs.keys()), "sync_type": "async"})
|
||||
capture_client_event("client.get_memory_export", self, {"keys": list(kwargs.keys()), "sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
async def get_project(self, fields: Optional[List[str]] = None) -> Dict[str, Any]:
|
||||
if not (self.sync_client.org_id and self.sync_client.project_id):
|
||||
if not (self.org_id and self.project_id):
|
||||
raise ValueError("org_id and project_id must be set to access instructions or categories")
|
||||
|
||||
params = self.sync_client._prepare_params({"fields": fields})
|
||||
params = self._prepare_params({"fields": fields})
|
||||
response = await self.async_client.get(
|
||||
f"/api/v1/orgs/organizations/{self.sync_client.org_id}/projects/{self.sync_client.project_id}/",
|
||||
f"/api/v1/orgs/organizations/{self.org_id}/projects/{self.project_id}/",
|
||||
params=params,
|
||||
)
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.get_project", self.sync_client, {"fields": fields, "sync_type": "async"})
|
||||
capture_client_event("client.get_project", self, {"fields": fields, "sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
async def update_project(
|
||||
self, custom_instructions: Optional[str] = None, custom_categories: Optional[List[str]] = None,
|
||||
retrieval_criteria: Optional[List[Dict[str, Any]]] = None
|
||||
self,
|
||||
custom_instructions: Optional[str] = None,
|
||||
custom_categories: Optional[List[str]] = None,
|
||||
retrieval_criteria: Optional[List[Dict[str, Any]]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
if not (self.sync_client.org_id and self.sync_client.project_id):
|
||||
if not (self.org_id and self.project_id):
|
||||
raise ValueError("org_id and project_id must be set to update instructions or categories")
|
||||
|
||||
if custom_instructions is None and custom_categories is None and retrieval_criteria is None:
|
||||
@@ -969,18 +1109,27 @@ class AsyncMemoryClient:
|
||||
"Currently we only support updating custom_instructions or custom_categories or retrieval_criteria, so you must provide at least one of them"
|
||||
)
|
||||
|
||||
payload = self.sync_client._prepare_params(
|
||||
{"custom_instructions": custom_instructions, "custom_categories": custom_categories, "retrieval_criteria": retrieval_criteria}
|
||||
payload = self._prepare_params(
|
||||
{
|
||||
"custom_instructions": custom_instructions,
|
||||
"custom_categories": custom_categories,
|
||||
"retrieval_criteria": retrieval_criteria,
|
||||
}
|
||||
)
|
||||
response = await self.async_client.patch(
|
||||
f"/api/v1/orgs/organizations/{self.sync_client.org_id}/projects/{self.sync_client.project_id}/",
|
||||
f"/api/v1/orgs/organizations/{self.org_id}/projects/{self.project_id}/",
|
||||
json=payload,
|
||||
)
|
||||
response.raise_for_status()
|
||||
capture_client_event(
|
||||
"client.update_project",
|
||||
self.sync_client,
|
||||
{"custom_instructions": custom_instructions, "custom_categories": custom_categories, "retrieval_criteria": retrieval_criteria, "sync_type": "async"},
|
||||
self,
|
||||
{
|
||||
"custom_instructions": custom_instructions,
|
||||
"custom_categories": custom_categories,
|
||||
"retrieval_criteria": retrieval_criteria,
|
||||
"sync_type": "async",
|
||||
},
|
||||
)
|
||||
return response.json()
|
||||
|
||||
@@ -993,7 +1142,7 @@ class AsyncMemoryClient:
|
||||
f"api/v1/webhooks/projects/{project_id}/",
|
||||
)
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.get_webhook", self.sync_client, {"sync_type": "async"})
|
||||
capture_client_event("client.get_webhook", self, {"sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
@@ -1001,7 +1150,7 @@ class AsyncMemoryClient:
|
||||
payload = {"url": url, "name": name, "event_types": event_types}
|
||||
response = await self.async_client.post(f"api/v1/webhooks/projects/{project_id}/", json=payload)
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.create_webhook", self.sync_client, {"sync_type": "async"})
|
||||
capture_client_event("client.create_webhook", self, {"sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
@@ -1015,14 +1164,14 @@ class AsyncMemoryClient:
|
||||
payload = {k: v for k, v in {"name": name, "url": url, "event_types": event_types}.items() if v is not None}
|
||||
response = await self.async_client.put(f"api/v1/webhooks/{webhook_id}/", json=payload)
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.update_webhook", self.sync_client, {"webhook_id": webhook_id, "sync_type": "async"})
|
||||
capture_client_event("client.update_webhook", self, {"webhook_id": webhook_id, "sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
async def delete_webhook(self, webhook_id: int) -> Dict[str, str]:
|
||||
response = await self.async_client.delete(f"api/v1/webhooks/{webhook_id}/")
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.delete_webhook", self.sync_client, {"webhook_id": webhook_id, "sync_type": "async"})
|
||||
capture_client_event("client.delete_webhook", self, {"webhook_id": webhook_id, "sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
@@ -1039,5 +1188,5 @@ class AsyncMemoryClient:
|
||||
|
||||
response = await self.async_client.post("/v1/feedback/", json=data)
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.feedback", self.sync_client, data, {"sync_type": "async"})
|
||||
capture_client_event("client.feedback", self, data, {"sync_type": "async"})
|
||||
return response.json()
|
||||
|
||||
@@ -33,6 +33,10 @@ class BaseEmbedderConfig(ABC):
|
||||
memory_search_embedding_type: Optional[str] = None,
|
||||
# LM Studio specific
|
||||
lmstudio_base_url: Optional[str] = "http://localhost:1234/v1",
|
||||
# AWS Bedrock specific
|
||||
aws_access_key_id: Optional[str] = None,
|
||||
aws_secret_access_key: Optional[str] = None,
|
||||
aws_region: Optional[str] = "us-west-2",
|
||||
):
|
||||
"""
|
||||
Initializes a configuration class instance for the Embeddings.
|
||||
@@ -92,3 +96,8 @@ class BaseEmbedderConfig(ABC):
|
||||
|
||||
# LM Studio specific
|
||||
self.lmstudio_base_url = lmstudio_base_url
|
||||
|
||||
# AWS Bedrock specific
|
||||
self.aws_access_key_id = aws_access_key_id
|
||||
self.aws_secret_access_key = aws_secret_access_key
|
||||
self.aws_region = aws_region
|
||||
|
||||
@@ -39,8 +39,14 @@ class BaseLlmConfig(ABC):
|
||||
deepseek_base_url: Optional[str] = None,
|
||||
# XAI specific
|
||||
xai_base_url: Optional[str] = None,
|
||||
# Sarvam specific
|
||||
sarvam_base_url: Optional[str] = "https://api.sarvam.ai/v1",
|
||||
# LM Studio specific
|
||||
lmstudio_base_url: Optional[str] = "http://localhost:1234/v1",
|
||||
# AWS Bedrock specific
|
||||
aws_access_key_id: Optional[str] = None,
|
||||
aws_secret_access_key: Optional[str] = None,
|
||||
aws_region: Optional[str] = "us-west-2",
|
||||
):
|
||||
"""
|
||||
Initializes a configuration class instance for the LLM.
|
||||
@@ -85,6 +91,8 @@ class BaseLlmConfig(ABC):
|
||||
:type deepseek_base_url: Optional[str], optional
|
||||
:param xai_base_url: XAI base URL to be use, defaults to None
|
||||
:type xai_base_url: Optional[str], optional
|
||||
:param sarvam_base_url: Sarvam base URL to be use, defaults to "https://api.sarvam.ai/v1"
|
||||
:type sarvam_base_url: Optional[str], optional
|
||||
:param lmstudio_base_url: LM Studio base URL to be use, defaults to "http://localhost:1234/v1"
|
||||
:type lmstudio_base_url: Optional[str], optional
|
||||
"""
|
||||
@@ -121,5 +129,13 @@ class BaseLlmConfig(ABC):
|
||||
# XAI specific
|
||||
self.xai_base_url = xai_base_url
|
||||
|
||||
# Sarvam specific
|
||||
self.sarvam_base_url = sarvam_base_url
|
||||
|
||||
# LM Studio specific
|
||||
self.lmstudio_base_url = lmstudio_base_url
|
||||
|
||||
# AWS Bedrock specific
|
||||
self.aws_access_key_id = aws_access_key_id
|
||||
self.aws_secret_access_key = aws_secret_access_key
|
||||
self.aws_region = aws_region
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any, Dict, Optional, Type, Union
|
||||
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
||||
@@ -13,8 +13,11 @@ class OpenSearchConfig(BaseModel):
|
||||
embedding_model_dims: int = Field(1536, description="Dimension of the embedding vector")
|
||||
verify_certs: bool = Field(False, description="Verify SSL certificates (default False for OpenSearch)")
|
||||
use_ssl: bool = Field(False, description="Use SSL for connection (default False for OpenSearch)")
|
||||
auto_create_index: bool = Field(True, description="Automatically create index during initialization")
|
||||
http_auth: Optional[object] = Field(None, description="HTTP authentication method / AWS SigV4")
|
||||
connection_class: Optional[Union[str, Type]] = Field(
|
||||
"RequestsHttpConnection", description="Connection class for OpenSearch"
|
||||
)
|
||||
pool_maxsize: int = Field(20, description="Maximum number of connections in the pool")
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
@@ -23,10 +26,6 @@ class OpenSearchConfig(BaseModel):
|
||||
if not values.get("host"):
|
||||
raise ValueError("Host must be provided for OpenSearch")
|
||||
|
||||
# Authentication: Either API key or user/password must be provided
|
||||
if not any([values.get("api_key"), (values.get("user") and values.get("password")), values.get("http_auth")]):
|
||||
raise ValueError("Either api_key or user/password must be provided for OpenSearch authentication")
|
||||
|
||||
return values
|
||||
|
||||
@model_validator(mode="before")
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import json
|
||||
import os
|
||||
from typing import Literal, Optional
|
||||
|
||||
try:
|
||||
import boto3
|
||||
except ImportError:
|
||||
raise ImportError("The 'boto3' library is required. Please install it using 'pip install boto3'.")
|
||||
|
||||
import numpy as np
|
||||
|
||||
from mem0.configs.embeddings.base import BaseEmbedderConfig
|
||||
from mem0.embeddings.base import EmbeddingBase
|
||||
|
||||
|
||||
class AWSBedrockEmbedding(EmbeddingBase):
|
||||
"""AWS Bedrock embedding implementation.
|
||||
|
||||
This class uses AWS Bedrock's embedding models.
|
||||
"""
|
||||
|
||||
def __init__(self, config: Optional[BaseEmbedderConfig] = None):
|
||||
super().__init__(config)
|
||||
|
||||
self.config.model = self.config.model or "amazon.titan-embed-text-v1"
|
||||
|
||||
# Get AWS config from environment variables or use defaults
|
||||
aws_access_key = os.environ.get("AWS_ACCESS_KEY_ID", "")
|
||||
aws_secret_key = os.environ.get("AWS_SECRET_ACCESS_KEY", "")
|
||||
aws_region = os.environ.get("AWS_REGION", "us-west-2")
|
||||
|
||||
# Check if AWS config is provided in the config
|
||||
if hasattr(self.config, "aws_access_key_id"):
|
||||
aws_access_key = self.config.aws_access_key_id
|
||||
if hasattr(self.config, "aws_secret_access_key"):
|
||||
aws_secret_key = self.config.aws_secret_access_key
|
||||
if hasattr(self.config, "aws_region"):
|
||||
aws_region = self.config.aws_region
|
||||
|
||||
self.client = boto3.client(
|
||||
"bedrock-runtime",
|
||||
region_name=aws_region,
|
||||
aws_access_key_id=aws_access_key if aws_access_key else None,
|
||||
aws_secret_access_key=aws_secret_key if aws_secret_key else None,
|
||||
)
|
||||
|
||||
def _normalize_vector(self, embeddings):
|
||||
"""Normalize the embedding to a unit vector."""
|
||||
emb = np.array(embeddings)
|
||||
norm_emb = emb / np.linalg.norm(emb)
|
||||
return norm_emb.tolist()
|
||||
|
||||
def _get_embedding(self, text):
|
||||
"""Call out to Bedrock embedding endpoint."""
|
||||
|
||||
# Format input body based on the provider
|
||||
provider = self.config.model.split(".")[0]
|
||||
input_body = {}
|
||||
|
||||
if provider == "cohere":
|
||||
input_body["input_type"] = "search_document"
|
||||
input_body["texts"] = [text]
|
||||
else:
|
||||
# Amazon and other providers
|
||||
input_body["inputText"] = text
|
||||
|
||||
body = json.dumps(input_body)
|
||||
|
||||
try:
|
||||
response = self.client.invoke_model(
|
||||
body=body,
|
||||
modelId=self.config.model,
|
||||
accept="application/json",
|
||||
contentType="application/json",
|
||||
)
|
||||
|
||||
response_body = json.loads(response.get("body").read())
|
||||
|
||||
if provider == "cohere":
|
||||
embeddings = response_body.get("embeddings")[0]
|
||||
else:
|
||||
embeddings = response_body.get("embedding")
|
||||
|
||||
return embeddings
|
||||
except Exception as e:
|
||||
raise ValueError(f"Error getting embedding from AWS Bedrock: {e}")
|
||||
|
||||
def embed(self, text, memory_action: Optional[Literal["add", "search", "update"]] = None):
|
||||
"""
|
||||
Get the embedding for the given text using AWS Bedrock.
|
||||
|
||||
Args:
|
||||
text (str): The text to embed.
|
||||
memory_action (optional): The type of embedding to use. Must be one of "add", "search", or "update". Defaults to None.
|
||||
Returns:
|
||||
list: The embedding vector.
|
||||
"""
|
||||
return self._get_embedding(text)
|
||||
@@ -23,6 +23,7 @@ class EmbedderConfig(BaseModel):
|
||||
"together",
|
||||
"lmstudio",
|
||||
"langchain",
|
||||
"aws_bedrock",
|
||||
]:
|
||||
return v
|
||||
else:
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import logging
|
||||
from typing import Literal, Optional
|
||||
|
||||
logging.getLogger("transformers").setLevel(logging.WARNING)
|
||||
logging.getLogger("sentence_transformers").setLevel(logging.WARNING)
|
||||
logging.getLogger("huggingface_hub").setLevel(logging.WARNING)
|
||||
|
||||
from openai import OpenAI
|
||||
from sentence_transformers import SentenceTransformer
|
||||
|
||||
from mem0.configs.embeddings.base import BaseEmbedderConfig
|
||||
from mem0.embeddings.base import EmbeddingBase
|
||||
|
||||
logging.getLogger("transformers").setLevel(logging.WARNING)
|
||||
logging.getLogger("sentence_transformers").setLevel(logging.WARNING)
|
||||
logging.getLogger("huggingface_hub").setLevel(logging.WARNING)
|
||||
|
||||
|
||||
class HuggingFaceEmbedding(EmbeddingBase):
|
||||
def __init__(self, config: Optional[BaseEmbedderConfig] = None):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import os
|
||||
from typing import Literal, Optional
|
||||
|
||||
from mem0.configs.embeddings.base import BaseEmbedderConfig
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user