Compare commits

..

38 Commits

Author SHA1 Message Date
Dev Khant cdb8dcdb9e Add embeding_dims param to FAISS (#2513) 2025-04-07 23:29:55 +05:30
Dev-Khant 2a79add7a5 hotfix: _create_procedural_memory 2025-04-07 16:07:26 +05:30
Dev Khant 9dfa9b4412 Add langchain embedding, update langchain LLM and version bump -> 0.1.84 (#2510) 2025-04-07 15:27:26 +05:30
Dev Khant 5509066925 Doc: changelog update (#2509) 2025-04-07 11:51:52 +05:30
Dev Khant 3712522b14 Fix langchain llm and update changelog (#2508) 2025-04-07 11:49:39 +05:30
Dev Khant 93f34e4116 Formatting and version bump -> 0.1.82 (#2507) 2025-04-07 11:31:16 +05:30
Dev Khant 39e5cbfacc Support for langchain LLMs (#2506) 2025-04-07 11:28:30 +05:30
Dev Khant d30c78c5eb Doc: update output_format (#2498) 2025-04-03 10:46:06 +05:30
Dev Khant 039756abbe Doc: pipecat integration (#2494) 2025-04-02 18:44:04 +05:30
Mrinank Bhowmick cb38c2dae7 Feature/google as new llm and embedder in mem0-ts (#2468)
Co-authored-by: Saket Aryan <94069182+whysosaket@users.noreply.github.com>
2025-04-02 15:56:18 +05:30
Pranav Puranik 4dc9f6fad6 adding lmstudio and together in docs (#2489) 2025-04-02 11:21:27 +05:30
Anusha Yella 1b1f02eb57 fix/failing-unit-tests (#2486) 2025-04-02 10:36:02 +05:30
Dev Khant 1db1105cad Set output_format='v1.1' and update docs (#2480) 2025-04-02 10:35:29 +05:30
Saket Aryan 91a28c7f04 Docs: Flowise Integration Documentation for Mem0 Memory Setup (#2482) 2025-04-01 10:16:53 -07:00
Dev Khant a78e1894b1 Doc: Add llms.txt (#2484) 2025-04-01 22:06:20 +05:30
Sergio Toro 83338295d8 feat: add development docker compose (#2411) 2025-04-01 16:19:30 +05:30
Dev Khant aff70807c6 update faqs (#2477) 2025-04-01 00:40:50 +05:30
Dev Khant 648c86b53d doc: fix lmstudio link (#2476) 2025-04-01 00:28:02 +05:30
Dev Khant de0b2de9c2 doc: fix links (#2475) 2025-03-31 22:20:55 +05:30
Saket Aryan e3f460fba4 Added Mastra Example (#2473) 2025-03-30 11:56:00 -07:00
Saket Aryan fe7b336922 Update Demo Mem0AI (#2471) 2025-03-30 12:49:54 +05:30
Saket Aryan 1033ab227c Introduce Ping in Mem0 Client (#2472) 2025-03-30 12:49:36 +05:30
Saket Aryan 5ed15c3bcd AI SDK Updates (#2470) 2025-03-30 11:10:30 +05:30
Deshraj Yadav c1f5a655ba Minor fixes in procedural memory (#2469) 2025-03-29 17:20:58 -07:00
Deshraj Yadav 72bb631bb5 Add support for procedural memory (#2460) 2025-03-29 15:58:12 -07:00
Dev Khant 2bf9286071 update for faiss doc (#2464) 2025-03-29 13:51:01 +05:30
Dev Khant 884b597312 bump version -> 0.1.79 (#2463) 2025-03-29 13:46:00 +05:30
Dev Khant f1471bcc55 update changelog (#2462) 2025-03-29 13:39:11 +05:30
Dev Khant 9ae23f9c88 Add Faiss Support (#2461) 2025-03-29 13:35:36 +05:30
Dev Khant cbecbb7b64 doc: update email example (#2459) 2025-03-29 00:11:02 +05:30
Dev Khant f8ad0c2b2c Doc: Add example for email processing (#2458) 2025-03-29 00:09:22 +05:30
Dev Khant d0da9a1ae0 Doc: Add changelog (#2455) 2025-03-28 12:18:17 +05:30
Saket Aryan bc4ab3db77 Add Infer Property (#2452) 2025-03-27 21:18:42 +05:30
Dev Khant 0eb53b9f27 doc: update API reference for expiration_date (#2450) 2025-03-27 12:24:26 +05:30
Prateek Chhikara 3cef3ed95e Added Evaluation folder (#2448) 2025-03-26 12:37:54 -07:00
Dev Khant 45e5f2af93 Doc: Update API reference section and Add elevenlabs example (#2447) 2025-03-27 00:13:09 +05:30
Prateek Chhikara 32ba13b3ea Updated multimodal docs (#2446) 2025-03-26 09:56:16 -07:00
Parshva Daftari 69a91d5cbb Mem0 livekit example (#2442) 2025-03-26 16:06:23 +05:30
81 changed files with 9992 additions and 910 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ install:
install_all:
poetry install
poetry run pip install groq together boto3 litellm ollama chromadb weaviate weaviate-client sentence_transformers vertexai \
google-generativeai elasticsearch opensearch-py vecs pinecone pinecone-text
google-generativeai elasticsearch opensearch-py vecs pinecone pinecone-text faiss-cpu langchain-community
# Format code with ruff
format:
+151
View File
@@ -0,0 +1,151 @@
---
title: "Product Updates"
mode: "wide"
---
<Tabs>
<Tab title="Python">
<Update label="2025-04-07" description="v0.1.84">
**New Features:**
- **Langchain Embedder:** Added Langchain embedder integration
**Improvements:**
- **Langchain LLM:** Updated Langchain LLM integration to directly pass the Langchain object LLM
</Update>
<Update label="2025-04-07" description="v0.1.83">
**Bug Fixes:**
- **Langchain LLM:** Fixed issues with Langchain LLM integration
</Update>
<Update label="2025-04-07" description="v0.1.82">
**New Features:**
- **LLM Integrations:** Added support for Langchain LLMs, Google as new LLM and embedder
- **Development:** Added development docker compose
**Improvements:**
- **Output Format:** Set output_format='v1.1' and updated documentation
**Documentation:**
- **Integrations:** Added LMStudio and Together.ai documentation
- **API Reference:** Updated output_format documentation
- **Integrations:** Added PipeCat integration documentation
- **Integrations:** Added Flowise integration documentation for Mem0 memory setup
**Bug Fixes:**
- **Tests:** Fixed failing unit tests
</Update>
<Update label="2025-04-02" description="v0.1.79">
**New Features:**
- **FAISS Support:** Added FAISS vector store support
</Update>
<Update label="2025-04-02" description="v0.1.78">
**New Features:**
- **Livekit Integration:** Added Mem0 livekit example
- **Evaluation:** Added evaluation framework and tools
**Documentation:**
- **Multimodal:** Updated multimodal documentation
- **Examples:** Added examples for email processing
- **API Reference:** Updated API reference section
- **Elevenlabs:** Added Elevenlabs integration example
**Bug Fixes:**
- **OpenAI Environment Variables:** Fixed issues with OpenAI environment variables
- **Deployment Errors:** Added `package.json` file to fix deployment errors
- **Tools:** Fixed tools issues and improved formatting
- **Docs:** Updated API reference section for `expiration date`
</Update>
<Update label="2025-03-26" description="v0.1.77">
**Bug Fixes:**
- **OpenAI Environment Variables:** Fixed issues with OpenAI environment variables
- **Deployment Errors:** Added `package.json` file to fix deployment errors
- **Tools:** Fixed tools issues and improved formatting
- **Docs:** Updated API reference section for `expiration date`
</Update>
<Update label="2025-03-19" description="v0.1.76">
**New Features:**
- **Supabase Vector Store:** Added support for Supabase Vector Store
- **Supabase History DB:** Added Supabase History DB to run Mem0 OSS on Serverless
- **Feedback Method:** Added feedback method to client
**Bug Fixes:**
- **Azure OpenAI:** Fixed issues with Azure OpenAI
- **Azure AI Search:** Fixed test cases for Azure AI Search
</Update>
</Tab>
<Tab title="TypeScript">
<Update label="2025-04-01" description="v2.1.14">
**New Features:**
- **Mastra Example:** Added Mastra example
- **Integrations:** Added Flowise integration documentation for Mem0 memory setup
**Improvements:**
- **Demo:** Updated Demo Mem0AI
- **Client:** Enhanced Ping method in Mem0 Client
- **AI SDK:** Updated AI SDK implementation
</Update>
<Update label="2025-03-29" description="v2.1.13">
**Improvements:**
- **Introuced `ping` method to check if API key is valid and populate org/project id**
</Update>
<Update label="2025-03-29" description="AI SDK v1.0.0">
**New Features:**
- **Vercel AI SDK Update:** Support threshold and rerank
**Improvements:**
- **Made add calls async to avoid blocking**
- **Bump `mem0ai` to use `2.1.12`**
</Update>
<Update label="2025-03-26" description="v2.1.12">
**New Features:**
- **Mem0 OSS:** Support infer param
**Improvements:**
- **Updated Supabase TS Docs**
- **Made package size smaller**
</Update>
<Update label="2025-03-19" description="v2.1.11">
**New Features:**
- **Supabase Vector Store Integration**
- **Feedback Method**
</Update>
</Tab>
<Tab title="Platform">
<Update label="2025-03-28" description="">
- **Updated Playground Prompt**
- **Send Email on User Addition to Org/Proj**
- **Fix Search Entity**
</Update>
<Update label="2025-03-19" description="">
- **General Stability & Performance Improvements**
</Update>
</Tab>
</Tabs>
@@ -0,0 +1,120 @@
---
title: LangChain
---
Mem0 supports LangChain as a provider to access a wide range of embedding models. LangChain is a framework for developing applications powered by language models, making it easy to integrate various embedding providers through a consistent interface.
For a complete list of available embedding models supported by LangChain, refer to the [LangChain Text Embedding documentation](https://python.langchain.com/docs/integrations/text_embedding/).
## Usage
<CodeGroup>
```python Python
import os
from mem0 import Memory
from langchain_openai import OpenAIEmbeddings
# Set necessary environment variables for your chosen LangChain provider
os.environ["OPENAI_API_KEY"] = "your-api-key"
# Initialize a LangChain embeddings model directly
openai_embeddings = OpenAIEmbeddings(
model="text-embedding-3-small",
dimensions=1536
)
# Pass the initialized model to the config
config = {
"embedder": {
"provider": "langchain",
"config": {
"model": openai_embeddings
}
}
}
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", metadata={"category": "movies"})
```
</CodeGroup>
## Supported LangChain Embedding Providers
LangChain supports a wide range of embedding providers, including:
- OpenAI (`OpenAIEmbeddings`)
- Cohere (`CohereEmbeddings`)
- Google (`VertexAIEmbeddings`)
- Hugging Face (`HuggingFaceEmbeddings`)
- Sentence Transformers (`HuggingFaceEmbeddings`)
- Azure OpenAI (`AzureOpenAIEmbeddings`)
- Ollama (`OllamaEmbeddings`)
- Together (`TogetherEmbeddings`)
- And many more
You can use any of these model instances directly in your configuration. For a complete and up-to-date list of available embedding providers, refer to the [LangChain Text Embedding documentation](https://python.langchain.com/docs/integrations/text_embedding/).
## Provider-Specific Configuration
When using LangChain as an embedder provider, you'll need to:
1. Set the appropriate environment variables for your chosen embedding provider
2. Import and initialize the specific model class you want to use
3. Pass the initialized model instance to the config
### Examples with Different Providers
#### HuggingFace Embeddings
```python
from langchain_huggingface import HuggingFaceEmbeddings
# Initialize a HuggingFace embeddings model
hf_embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-small-en-v1.5",
encode_kwargs={"normalize_embeddings": True}
)
config = {
"embedder": {
"provider": "langchain",
"config": {
"model": hf_embeddings
}
}
}
```
#### Ollama Embeddings
```python
from langchain_ollama import OllamaEmbeddings
# Initialize an Ollama embeddings model
ollama_embeddings = OllamaEmbeddings(
model="nomic-embed-text"
)
config = {
"embedder": {
"provider": "langchain",
"config": {
"model": ollama_embeddings
}
}
}
```
<Note>
Make sure to install the necessary LangChain packages and any provider-specific dependencies.
</Note>
## Config
All available parameters for the `langchain` embedder config are present in [Master List of All Params in Config](../config).
+1
View File
@@ -23,6 +23,7 @@ See the list of supported embedders below.
<Card title="Vertex AI" href="/components/embedders/models/vertexai"></Card>
<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>
</CardGroup>
## Usage
+77
View File
@@ -0,0 +1,77 @@
---
title: LangChain
---
Mem0 supports LangChain as a provider to access a wide range of LLM models. LangChain is a framework for developing applications powered by language models, making it easy to integrate various LLM providers through a consistent interface.
For a complete list of available chat models supported by LangChain, refer to the [LangChain Chat Models documentation](https://python.langchain.com/docs/integrations/chat).
## Usage
<CodeGroup>
```python Python
import os
from mem0 import Memory
from langchain_openai import ChatOpenAI
# Set necessary environment variables for your chosen LangChain provider
os.environ["OPENAI_API_KEY"] = "your-api-key"
# Initialize a LangChain model directly
openai_model = ChatOpenAI(
model="gpt-4o",
temperature=0.2,
max_tokens=2000
)
# Pass the initialized model to the config
config = {
"llm": {
"provider": "langchain",
"config": {
"model": openai_model
}
}
}
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", metadata={"category": "movies"})
```
</CodeGroup>
## Supported LangChain Providers
LangChain supports a wide range of LLM providers, including:
- OpenAI (`ChatOpenAI`)
- Anthropic (`ChatAnthropic`)
- Google (`ChatGoogleGenerativeAI`, `ChatGooglePalm`)
- Mistral (`ChatMistralAI`)
- Ollama (`ChatOllama`)
- Azure OpenAI (`AzureChatOpenAI`)
- HuggingFace (`HuggingFaceChatEndpoint`)
- And many more
You can use any of these model instances directly in your configuration. For a complete and up-to-date list of available providers, refer to the [LangChain Chat Models documentation](https://python.langchain.com/docs/integrations/chat).
## Provider-Specific Configuration
When using LangChain as a provider, you'll need to:
1. Set the appropriate environment variables for your chosen LLM provider
2. Import and initialize the specific model class you want to use
3. Pass the initialized model instance to the config
<Note>
Make sure to install the necessary LangChain packages and any provider-specific dependencies.
</Note>
## Config
All available parameters for the `langchain` config are present in [Master List of All Params in Config](../config).
+1
View File
@@ -33,6 +33,7 @@ To view all supported llms, visit the [Supported LLMs](./models).
<Card title="DeepSeek" href="/components/llms/models/deepseek" />
<Card title="xAI" href="/components/llms/models/xAI" />
<Card title="LM Studio" href="/components/llms/models/lmstudio" />
<Card title="Langchain" href="/components/llms/models/langchain" />
</CardGroup>
## Structured vs Unstructured Outputs
@@ -1,3 +1,7 @@
---
title: Azure AI Search
---
[Azure AI Search](https://learn.microsoft.com/azure/search/search-what-is-azure-search/) (formerly known as "Azure Cognitive Search") provides secure information retrieval at scale over user-owned content in traditional and generative AI search applications.
## Usage
+72
View File
@@ -0,0 +1,72 @@
[FAISS](https://github.com/facebookresearch/faiss) is a library for efficient similarity search and clustering of dense vectors. It is designed to work with large-scale datasets and provides a high-performance search engine for vector data. FAISS is optimized for memory usage and search speed, making it an excellent choice for production environments.
### Usage
```python
import os
from mem0 import Memory
os.environ["OPENAI_API_KEY"] = "sk-xx"
config = {
"vector_store": {
"provider": "faiss",
"config": {
"collection_name": "test",
"path": "/tmp/faiss_memories",
"distance_strategy": "euclidean"
}
}
}
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", metadata={"category": "movies"})
```
### Installation
To use FAISS in your mem0 project, you need to install the appropriate FAISS package for your environment:
```bash
# For CPU version
pip install faiss-cpu
# For GPU version (requires CUDA)
pip install faiss-gpu
```
### Config
Here are the parameters available for configuring FAISS:
| Parameter | Description | Default Value |
| --- | --- | --- |
| `collection_name` | The name of the collection | `mem0` |
| `path` | Path to store FAISS index and metadata | `/tmp/faiss/<collection_name>` |
| `distance_strategy` | Distance metric strategy to use (options: 'euclidean', 'inner_product', 'cosine') | `euclidean` |
| `normalize_L2` | Whether to normalize L2 vectors (only applicable for euclidean distance) | `False` |
### Performance Considerations
FAISS offers several advantages for vector search:
1. **Efficiency**: FAISS is optimized for memory usage and speed, making it suitable for large-scale applications.
2. **Offline Support**: FAISS works entirely locally, with no need for external servers or API calls.
3. **Storage Options**: Vectors can be stored in-memory for maximum speed or persisted to disk.
4. **Multiple Index Types**: FAISS supports different index types optimized for various use cases (though mem0 currently uses the basic flat index).
### Distance Strategies
FAISS in mem0 supports three distance strategies:
- **euclidean**: L2 distance, suitable for most embedding models
- **inner_product**: Dot product similarity, useful for some specialized embeddings
- **cosine**: Cosine similarity, best for comparing semantic similarity regardless of vector magnitude
When using `cosine` or `inner_product` with normalized vectors, you may want to set `normalize_L2=True` for better results.
@@ -1,4 +1,6 @@
## Google Cloud Vertex AI Vector Search
---
title: Vertex AI Vector Search
---
### Usage
+3 -2
View File
@@ -20,13 +20,14 @@ See the list of supported vector databases below.
<Card title="Pgvector" href="/components/vectordbs/dbs/pgvector"></Card>
<Card title="Milvus" href="/components/vectordbs/dbs/milvus"></Card>
<Card title="Pinecone" href="/components/vectordbs/dbs/pinecone"></Card>
<Card title="Azure AI Search" href="/components/vectordbs/dbs/azure_ai_search"></Card>
<Card title="Azure" href="/components/vectordbs/dbs/azure"></Card>
<Card title="Redis" href="/components/vectordbs/dbs/redis"></Card>
<Card title="Elasticsearch" href="/components/vectordbs/dbs/elasticsearch"></Card>
<Card title="OpenSearch" href="/components/vectordbs/dbs/opensearch"></Card>
<Card title="Supabase" href="/components/vectordbs/dbs/supabase"></Card>
<Card title="Vertex AI Vector Search" href="/components/vectordbs/dbs/vertex_ai_vector_search"></Card>
<Card title="Vertex AI" href="/components/vectordbs/dbs/vertex_ai"></Card>
<Card title="Weaviate" href="/components/vectordbs/dbs/weaviate"></Card>
<Card title="FAISS" href="/components/vectordbs/dbs/faiss"></Card>
</CardGroup>
## Usage
+45 -7
View File
@@ -110,7 +110,9 @@
"components/llms/models/aws_bedrock",
"components/llms/models/gemini",
"components/llms/models/deepseek",
"components/llms/models/xAI"
"components/llms/models/xAI",
"components/llms/models/lmstudio",
"components/llms/models/langchain"
]
}
]
@@ -130,13 +132,14 @@
"components/vectordbs/dbs/pgvector",
"components/vectordbs/dbs/milvus",
"components/vectordbs/dbs/pinecone",
"components/vectordbs/dbs/azure_ai_search",
"components/vectordbs/dbs/azure",
"components/vectordbs/dbs/redis",
"components/vectordbs/dbs/elasticsearch",
"components/vectordbs/dbs/opensearch",
"components/vectordbs/dbs/supabase",
"components/vectordbs/dbs/vertex_ai_vector_search",
"components/vectordbs/dbs/weaviate"
"components/vectordbs/dbs/vertex_ai",
"components/vectordbs/dbs/weaviate",
"components/vectordbs/dbs/faiss"
]
}
]
@@ -156,7 +159,10 @@
"components/embedders/models/ollama",
"components/embedders/models/huggingface",
"components/embedders/models/vertexai",
"components/embedders/models/gemini"
"components/embedders/models/gemini",
"components/embedders/models/lmstudio",
"components/embedders/models/together",
"components/embedders/models/langchain"
]
}
]
@@ -183,6 +189,7 @@
"examples/overview",
"examples/mem0-demo",
"examples/ai_companion_js",
"examples/mem0-mastra",
"examples/mem0-with-ollama",
"examples/personal-ai-tutor",
"examples/customer-support-agent",
@@ -194,7 +201,9 @@
"examples/personalized-deep-research",
"examples/mem0-agentic-tool",
"examples/openai-inbuilt-tools",
"examples/mem0-openai-voice-demo"
"examples/mem0-openai-voice-demo",
"examples/mem0-livekit-voice-agent",
"examples/email_processing"
]
}
]
@@ -208,6 +217,7 @@
"pages": [
"integrations/overview",
"integrations/vercel-ai-sdk",
"integrations/flowise",
"integrations/crewai",
"integrations/autogen",
"integrations/langchain",
@@ -215,7 +225,10 @@
"integrations/llama-index",
"integrations/langchain-tools",
"integrations/dify",
"integrations/mcp-server"
"integrations/mcp-server",
"integrations/livekit",
"integrations/elevenlabs",
"integrations/pipecat"
]
}
]
@@ -270,6 +283,18 @@
"api-reference/organization/delete-org"
]
},
{
"group": "Project APIs",
"icon": "folder",
"pages": [
"api-reference/project/create-project",
"api-reference/project/get-projects",
"api-reference/project/get-project",
"api-reference/project/get-project-members",
"api-reference/project/add-project-member",
"api-reference/project/delete-project"
]
},
{
"group": "Webhook APIs",
"icon": "webhook",
@@ -283,6 +308,19 @@
]
}
]
},
{
"tab": "Changelog",
"icon": "clock",
"groups": [
{
"group": "Product Updates",
"icon": "rocket",
"pages": [
"changelog/overview"
]
}
]
}
]
},
+186
View File
@@ -0,0 +1,186 @@
---
title: Email Processing with Mem0
---
This guide demonstrates how to build an intelligent email processing system using Mem0's memory capabilities. You'll learn how to store, categorize, retrieve, and analyze emails to create a smart email management solution.
## Overview
Email overload is a common challenge for many professionals. By leveraging Mem0's memory capabilities, you can build an intelligent system that:
- Stores emails as searchable memories
- Categorizes emails automatically
- Retrieves relevant past conversations
- Prioritizes messages based on importance
- Generates summaries and action items
## Setup
Before you begin, ensure you have the required dependencies installed:
```bash
pip install mem0ai openai
```
## Implementation
### Basic Email Memory System
The following example shows how to create a basic email processing system with Mem0:
```python
import os
from mem0 import MemoryClient
from email.parser import Parser
# Configure API keys
os.environ["MEM0_API_KEY"] = "your-mem0-api-key"
# Initialize Mem0 client
client = MemoryClient()
class EmailProcessor:
def __init__(self):
"""Initialize the Email Processor with Mem0 memory client"""
self.client = client
def process_email(self, email_content, user_id):
"""
Process an email and store it in Mem0 memory
Args:
email_content (str): Raw email content
user_id (str): User identifier for memory association
"""
# Parse email
parser = Parser()
email = parser.parsestr(email_content)
# Extract email details
sender = email['from']
recipient = email['to']
subject = email['subject']
date = email['date']
body = self._get_email_body(email)
# Create message object for Mem0
message = {
"role": "user",
"content": f"Email from {sender}: {subject}\n\n{body}"
}
# Create metadata for better retrieval
metadata = {
"email_type": "incoming",
"sender": sender,
"recipient": recipient,
"subject": subject,
"date": date
}
# Store in Mem0 with appropriate categories
response = self.client.add(
messages=[message],
user_id=user_id,
metadata=metadata,
categories=["email", "correspondence"],
version="v2"
)
return response
def _get_email_body(self, email):
"""Extract the body content from an email"""
# Simplified extraction - in real-world, handle multipart emails
if email.is_multipart():
for part in email.walk():
if part.get_content_type() == "text/plain":
return part.get_payload(decode=True).decode()
else:
return email.get_payload(decode=True).decode()
def search_emails(self, query, user_id):
"""
Search through stored emails
Args:
query (str): Search query
user_id (str): User identifier
"""
# Search Mem0 for relevant emails
results = self.client.search(
query=query,
user_id=user_id,
categories=["email"],
output_format="v1.1",
version="v2"
)
return results
def get_email_thread(self, subject, user_id):
"""
Retrieve all emails in a thread based on subject
Args:
subject (str): Email subject to match
user_id (str): User identifier
"""
filters = {
"AND": [
{"user_id": user_id},
{"categories": {"contains": "email"}},
{"metadata": {"subject": {"contains": subject}}}
]
}
thread = self.client.get_all(
version="v2",
filters=filters,
output_format="v1.1"
)
return thread
# Initialize the processor
processor = EmailProcessor()
# Example raw email
sample_email = """From: alice@example.com
To: bob@example.com
Subject: Meeting Schedule Update
Date: Mon, 15 Jul 2024 14:22:05 -0700
Hi Bob,
I wanted to update you on the schedule for our upcoming project meeting.
We'll be meeting this Thursday at 2pm instead of Friday.
Could you please prepare your section of the presentation?
Thanks,
Alice
"""
# Process and store the email
user_id = "bob@example.com"
processor.process_email(sample_email, user_id)
# Later, search for emails about meetings
meeting_emails = processor.search_emails("meeting schedule", user_id)
print(f"Found {len(meeting_emails['results'])} relevant emails")
```
## Key Features and Benefits
- **Long-term Email Memory**: Store and retrieve email conversations across long periods
- **Semantic Search**: Find relevant emails even if they don't contain exact keywords
- **Intelligent Categorization**: Automatically sort emails into meaningful categories
- **Action Item Extraction**: Identify and track tasks mentioned in emails
- **Priority Management**: Focus on important emails based on AI-determined priority
- **Context Awareness**: Maintain thread context for more relevant interactions
## Conclusion
By combining Mem0's memory capabilities with email processing, you can create intelligent email management systems that help users organize, prioritize, and act on their inbox effectively. The advanced capabilities like automatic categorization, action item extraction, and priority management can significantly reduce the time spent on email management, allowing users to focus on more important tasks.
+126
View File
@@ -0,0 +1,126 @@
---
title: Mem0 with Mastra
---
In this example you'll learn how to use the Mem0 to add long-term memory capabilities to [Mastra's agent](https://mastra.ai/) via tool-use.
This memory integration can work alongside Mastra's [agent memory features](https://mastra.ai/docs/agents/01-agent-memory).
You can find the complete example code in the [Mastra repository](https://github.com/mastra-ai/mastra/tree/main/examples/memory-with-mem0).
## Overview
This guide will show you how to integrate Mem0 with Mastra to add long-term memory capabilities to your agents. We'll create tools that allow agents to save and retrieve memories using Mem0's API.
### Installation
1. **Install the Integration Package**
To install the Mem0 integration, run:
```bash
npm install @mastra/mem0
```
2. **Add the Integration to Your Project**
Create a new file for your integrations and import the integration:
```typescript integrations/index.ts
import { Mem0Integration } from "@mastra/mem0";
export const mem0 = new Mem0Integration({
config: {
apiKey: process.env.MEM0_API_KEY!,
userId: "alice",
},
});
```
3. **Use the Integration in Tools or Workflows**
You can now use the integration when defining tools for your agents or in workflows.
```typescript tools/index.ts
import { createTool } from "@mastra/core";
import { z } from "zod";
import { mem0 } from "../integrations";
export 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,
};
},
});
export 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 };
},
});
```
4. **Create a new agent**
```typescript agents/index.ts
import { openai } from '@ai-sdk/openai';
import { Agent } from '@mastra/core/agent';
import { mem0MemorizeTool, mem0RememberTool } from '../tools';
export const mem0Agent = new Agent({
name: 'Mem0 Agent',
instructions: `
You are a helpful assistant that has the ability to memorize and remember facts using Mem0.
`,
model: openai('gpt-4o'),
tools: { mem0RememberTool, mem0MemorizeTool },
});
```
5. **Run the agent**
```typescript index.ts
import { Mastra } from '@mastra/core/mastra';
import { createLogger } from '@mastra/core/logger';
import { mem0Agent } from './agents';
export const mastra = new Mastra({
agents: { mem0Agent },
logger: createLogger({
name: 'Mastra',
level: 'error',
}),
});
```
In the example above:
- We import the `@mastra/mem0` integration.
- We define two tools that uses the Mem0 API client to create new memories and recall previously saved memories.
- The tool accepts `question` as an input and returns the memory as a string.
+7 -3
View File
@@ -55,7 +55,7 @@ Explore how **Mem0** can power real-world applications and bring personalized, i
Supercharge AI with **Mem0's multimodal memory** — blend text, images, and more for richer, context-aware interactions.
</Card>
<Card title="Personalized Research Agent" icon="robot" href="/examples/personalized-deep-research">
<Card title="Personalized Research Agent" icon="magnifying-glass" href="/examples/personalized-deep-research">
Build a **Deep Research AI** that remembers your research goals and compiles insights from vast information sources.
</Card>
@@ -67,7 +67,11 @@ Explore how **Mem0** can power real-world applications and bring personalized, i
Use Mem0's memory capabilities with OpenAI's Inbuilt Tools to create AI agents with persistent memory.
</Card>
<Card title="Mem0 OpenAI Voice Demo" icon="robot" href="/examples/mem0-openai-voice-demo">
<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="Email Processing" icon="envelope" href="/examples/email_processing">
Use Mem0's memory capabilities to process emails and create AI agents with persistent memory.
</Card>
</CardGroup>
+17
View File
@@ -125,6 +125,23 @@ iconType: "solid"
This flexibility allows you to create highly contextually aware AI applications that can adapt to specific user needs and situations. Metadata provides an additional dimension for memory retrieval, enabling more precise and relevant responses.
</Accordion>
<Accordion title="How do I disable telemetry in Mem0?">
To disable telemetry in Mem0, you can set the `MEM0_TELEMETRY` environment variable to `False`:
```bash
MEM0_TELEMETRY=False
```
You can also disable telemetry programmatically in your code:
```python
import os
os.environ["MEM0_TELEMETRY"] = "False"
```
Setting this environment variable will prevent Mem0 from collecting and sending any usage data, ensuring complete privacy for your application.
</Accordion>
</AccordionGroup>
+142 -8
View File
@@ -1,15 +1,15 @@
---
title: Multimodal Support
description: Integrate images into your interactions with Mem0
description: Integrate images and documents into your interactions with Mem0
icon: "image"
iconType: "solid"
---
Mem0 extends its capabilities beyond text by supporting multimodal data, including images. With this feature, users can seamlessly integrate images into their interactions—allowing Mem0 to extract relevant information from visual content and enrich the memory system.
Mem0 extends its capabilities beyond text by supporting multimodal data, including images and documents. With this feature, users can seamlessly integrate visual and document content into their interactions—allowing Mem0 to extract relevant information from various media types and enrich the memory system.
## 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 visual inputs.
When a user submits an image or document, 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
@@ -90,11 +90,18 @@ await client.add(messages, { user_id: "alice" })
```
</CodeGroup>
## Image Integration Methods
## Supported Media Types
Mem0 supports incorporating images into user interactions using two primary methods: by providing an image URL or by using a Base64-encoded image. The examples below demonstrate both approaches.
Mem0 currently supports the following media types:
## 1. Using an Image URL (Recommended)
1. **Images** - JPG, PNG, and other common image formats
2. **Documents** - MDX, TXT, and PDF files
## Integration Methods
### 1. Images
#### Using an Image URL (Recommended)
You can include an image by providing its direct URL. This method is simple and efficient for online images.
@@ -115,7 +122,7 @@ image_message = {
client.add([image_message], user_id="alice")
```
## 2. Using Base64 Image Encoding for Local Files
#### Using Base64 Image Encoding for Local Files
For local images—or when embedding the image directly is preferable—you can use a Base64-encoded string.
@@ -164,7 +171,134 @@ const imageMessage = {
await client.add([imageMessage], { user_id: "alice" })
```
</CodeGroup>
Using these methods, you can seamlessly incorporate images into your interactions, further enhancing Mem0's multimodal capabilities.
### 2. Text Documents (MDX/TXT)
Mem0 supports both online and local text documents in MDX or TXT format.
#### Using a Document URL
```python
# Define the document URL
document_url = "https://www.w3.org/TR/2003/REC-PNG-20031110/iso_8859-1.txt"
# Create the message dictionary with the document URL
document_message = {
"role": "user",
"content": {
"type": "mdx_url",
"mdx_url": {
"url": document_url
}
}
}
client.add([document_message], user_id="alice")
```
#### Using Base64 Encoding for Local Documents
```python
import base64
# Path to the document file
document_path = "path/to/your/document.txt"
# Function to convert file to Base64
def file_to_base64(file_path):
with open(file_path, "rb") as file:
return base64.b64encode(file.read()).decode('utf-8')
# Encode the document in Base64
base64_document = file_to_base64(document_path)
# Create the message dictionary with the Base64-encoded document
document_message = {
"role": "user",
"content": {
"type": "mdx_url",
"mdx_url": {
"url": base64_document
}
}
}
client.add([document_message], user_id="alice")
```
### 3. PDF Documents
Mem0 supports PDF documents via URL.
```python
# Define the PDF URL
pdf_url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
# Create the message dictionary with the PDF URL
pdf_message = {
"role": "user",
"content": {
"type": "pdf_url",
"pdf_url": {
"url": pdf_url
}
}
}
client.add([pdf_message], user_id="alice")
```
## Complete Example with Multiple File Types
Here's a comprehensive example showing how to work with different file types:
```python
import base64
from mem0 import MemoryClient
client = MemoryClient()
def file_to_base64(file_path):
with open(file_path, "rb") as file:
return base64.b64encode(file.read()).decode('utf-8')
# Example 1: Using an image URL
image_message = {
"role": "user",
"content": {
"type": "image_url",
"image_url": {
"url": "https://example.com/sample-image.jpg"
}
}
}
# Example 2: Using a text document URL
text_message = {
"role": "user",
"content": {
"type": "mdx_url",
"mdx_url": {
"url": "https://www.w3.org/TR/2003/REC-PNG-20031110/iso_8859-1.txt"
}
}
}
# Example 3: Using a PDF URL
pdf_message = {
"role": "user",
"content": {
"type": "pdf_url",
"pdf_url": {
"url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
}
}
}
# Add each message to the memory system
client.add([image_message], user_id="alice")
client.add([text_message], user_id="alice")
client.add([pdf_message], user_id="alice")
```
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:
+454
View File
@@ -0,0 +1,454 @@
---
title: ElevenLabs
---
Create voice-based conversational AI agents with memory capabilities by integrating ElevenLabs and Mem0. This integration enables persistent, context-aware voice interactions that remember past conversations.
## Overview
In this guide, we'll build a voice agent that:
1. Uses ElevenLabs Conversational AI for voice interaction
2. Leverages Mem0 to store and retrieve memories from past conversations
3. Provides personalized responses based on user history
## Setup and Configuration
Install necessary libraries:
```bash
pip install elevenlabs mem0 python-dotenv
```
Configure your environment variables:
<Note>You'll need both an ElevenLabs API key and a Mem0 API key to use this integration.</Note>
```bash
# Create a .env file with these variables
AGENT_ID=your-agent-id
USER_ID=unique-user-identifier
ELEVENLABS_API_KEY=your-elevenlabs-api-key
MEM0_API_KEY=your-mem0-api-key
```
## Integration Code Breakdown
Let's break down the implementation into manageable parts:
### 1. Imports and Environment Setup
First, we import required libraries and set up the environment:
```python
import os
import signal
import sys
from mem0 import AsyncMemoryClient
from elevenlabs.client import ElevenLabs
from elevenlabs.conversational_ai.conversation import Conversation
from elevenlabs.conversational_ai.default_audio_interface import DefaultAudioInterface
from elevenlabs.conversational_ai.conversation import ClientTools
```
These imports provide:
- Standard Python libraries for system operations and signal handling
- `AsyncMemoryClient` from Mem0 for memory operations
- ElevenLabs components for voice interaction
### 2. Environment Variables and Validation
Next, we validate the required environment variables:
```python
def main():
# Required environment variables
AGENT_ID = os.environ.get('AGENT_ID')
USER_ID = os.environ.get('USER_ID')
API_KEY = os.environ.get('ELEVENLABS_API_KEY')
MEM0_API_KEY = os.environ.get('MEM0_API_KEY')
# Validate required environment variables
if not AGENT_ID:
sys.stderr.write("AGENT_ID environment variable must be set\n")
sys.exit(1)
if not USER_ID:
sys.stderr.write("USER_ID environment variable must be set\n")
sys.exit(1)
if not API_KEY:
sys.stderr.write("ELEVENLABS_API_KEY not set, assuming the agent is public\n")
if not MEM0_API_KEY:
sys.stderr.write("MEM0_API_KEY environment variable must be set\n")
sys.exit(1)
# Set up Mem0 API key in the environment
os.environ['MEM0_API_KEY'] = MEM0_API_KEY
```
This section:
- Retrieves required environment variables
- Performs validation to ensure required variables are present
- Exits the application with an error message if required variables are missing
- Sets the Mem0 API key in the environment for the Mem0 client to use
### 3. Client Initialization
Initialize both the ElevenLabs and Mem0 clients:
```python
# Initialize ElevenLabs client
client = ElevenLabs(api_key=API_KEY)
# Initialize memory client and tools
client_tools = ClientTools()
mem0_client = AsyncMemoryClient()
```
Here we:
- Create an ElevenLabs client with the API key
- Initialize a ClientTools object for registering function tools
- Create an AsyncMemoryClient instance for Mem0 interactions
### 4. Memory Function Definitions
Define the two key memory functions that will be registered as tools:
```python
# Define memory-related functions for the agent
async def add_memories(parameters):
"""Add a message to the memory store"""
message = parameters.get("message")
await mem0_client.add(
messages=message,
user_id=USER_ID,
output_format="v1.1",
version="v2"
)
return "Memory added successfully"
async def retrieve_memories(parameters):
"""Retrieve relevant memories based on the input message"""
message = parameters.get("message")
# Set up filters to retrieve memories for this specific user
filters = {
"AND": [
{
"user_id": USER_ID
}
]
}
# Search for relevant memories using the message as a query
results = await mem0_client.search(
query=message,
version="v2",
filters=filters
)
# Extract and join the memory texts
memories = ' '.join([result["memory"] for result in results])
print("[ Memories ]", memories)
if memories:
return memories
return "No memories found"
```
These functions:
#### `add_memories`:
- Takes a message parameter containing information to remember
- Stores the message in Mem0 using the `add` method
- Associates the memory with the specific USER_ID
- Returns a success message to the agent
#### `retrieve_memories`:
- Takes a message parameter as the search query
- Sets up filters to only retrieve memories for the current user
- Uses semantic search to find relevant memories
- Joins all retrieved memories into a single text
- Prints retrieved memories to the console for debugging
- Returns the memories or a "No memories found" message if none are found
### 5. Registering Memory Functions as Tools
Register the memory functions with the ElevenLabs ClientTools system:
```python
# Register the memory functions as tools for the agent
client_tools.register("addMemories", add_memories, is_async=True)
client_tools.register("retrieveMemories", retrieve_memories, is_async=True)
```
This allows the ElevenLabs agent to:
- Access these functions through function calling
- Wait for asynchronous results (is_async=True)
- Call these functions by name ("addMemories" and "retrieveMemories")
### 6. Conversation Setup
Configure the conversation with ElevenLabs:
```python
# Initialize the conversation
conversation = Conversation(
client,
AGENT_ID,
# Assume auth is required when API_KEY is set
requires_auth=bool(API_KEY),
audio_interface=DefaultAudioInterface(),
client_tools=client_tools,
callback_agent_response=lambda response: print(f"Agent: {response}"),
callback_agent_response_correction=lambda original, corrected: print(f"Agent: {original} -> {corrected}"),
callback_user_transcript=lambda transcript: print(f"User: {transcript}"),
# callback_latency_measurement=lambda latency: print(f"Latency: {latency}ms"),
)
```
This sets up the conversation with:
- The ElevenLabs client and Agent ID
- Authentication requirements based on API key presence
- DefaultAudioInterface for handling audio I/O
- The client_tools with our memory functions
- Callback functions for:
- Displaying agent responses
- Showing corrected responses (when the agent self-corrects)
- Displaying user transcripts for debugging
- (Commented out) Latency measurements
### 7. Conversation Management
Start and manage the conversation:
```python
# Start the conversation
print(f"Starting conversation with user_id: {USER_ID}")
conversation.start_session()
# Handle Ctrl+C to gracefully end the session
signal.signal(signal.SIGINT, lambda sig, frame: conversation.end_session())
# Wait for the conversation to end and get the conversation ID
conversation_id = conversation.wait_for_session_end()
print(f"Conversation ID: {conversation_id}")
if __name__ == '__main__':
main()
```
This final section:
- Prints a message indicating the conversation has started
- Starts the conversation session
- Sets up a signal handler to gracefully end the session on Ctrl+C
- Waits for the session to end and gets the conversation ID
- Prints the conversation ID for reference
## Memory Tools Overview
This integration provides two key memory functions to your conversational AI agent:
### 1. Adding Memories (`addMemories`)
The `addMemories` tool allows your agent to store important information during a conversation, including:
- User preferences
- Important facts shared by the user
- Decisions or commitments made during the conversation
- Action items to follow up on
When the agent identifies information worth remembering, it calls this function to store it in the Mem0 database with the appropriate user ID.
#### How it works:
1. The agent identifies information that should be remembered
2. It formats the information as a message string
3. It calls the `addMemories` function with this message
4. The function stores the memory in Mem0 linked to the user's ID
5. Later conversations can retrieve this memory
#### Example usage in agent prompt:
```
When the user shares important information like preferences or personal details,
use the addMemories function to store this information for future reference.
```
### 2. Retrieving Memories (`retrieveMemories`)
The `retrieveMemories` tool allows your agent to search for and retrieve relevant memories from previous conversations. The agent can:
- Search for context related to the current topic
- Recall user preferences
- Remember previous interactions on similar topics
- Create continuity across multiple sessions
#### How it works:
1. The agent needs context for the current conversation
2. It calls `retrieveMemories` with the current conversation topic or question
3. The function performs a semantic search in Mem0
4. Relevant memories are returned to the agent
5. The agent incorporates these memories into its response
#### Example usage in agent prompt:
```
At the beginning of each conversation turn, use retrieveMemories to check if we've
discussed this topic before or if the user has shared relevant preferences.
```
## Configuring Your ElevenLabs Agent
To enable your agent to effectively use memory:
1. Add function calling capabilities to your agent in the ElevenLabs platform:
- Go to your agent settings in the ElevenLabs platform
- Navigate to the "Tools" section
- Enable function calling for your agent
- Add the memory tools as described below
2. Add the `addMemories` and `retrieveMemories` tools to your agent with these specifications:
For `addMemories`:
```json
{
"name": "addMemories",
"description": "Stores important information from the conversation to remember for future interactions",
"parameters": {
"type": "object",
"properties": {
"message": {
"type": "string",
"description": "The important information to remember"
}
},
"required": ["message"]
}
}
```
For `retrieveMemories`:
```json
{
"name": "retrieveMemories",
"description": "Retrieves relevant information from past conversations",
"parameters": {
"type": "object",
"properties": {
"message": {
"type": "string",
"description": "The query to search for in past memories"
}
},
"required": ["message"]
}
}
```
3. Update your agent's prompt to instruct it to use these memory functions. For example:
```
You are a helpful voice assistant that remembers past conversations with the user.
You have access to memory tools that allow you to remember important information:
- Use retrieveMemories at the beginning of the conversation to recall relevant context from prior conversations
- Use addMemories to store new important information such as:
* User preferences
* Personal details the user shares
* Important decisions made
* Tasks or follow-ups promised to the user
Before responding to complex questions, always check for relevant memories first.
When the user shares important information, make sure to store it for future reference.
```
## Example Conversation Flow
Here's how a typical conversation with memory might flow:
1. **User speaks**: "Hi, do you remember my favorite color?"
2. **Agent retrieves memories**:
```python
# Agent calls retrieve_memories
memories = retrieve_memories({"message": "user's favorite color"})
# If found: "The user's favorite color is blue"
```
3. **Agent processes with context**:
- If memories found: Prepares a personalized response
- If no memories: Prepares to ask and store the information
4. **Agent responds**:
- With memory: "Yes, your favorite color is blue!"
- Without memory: "I don't think you've told me your favorite color before. What is it?"
5. **User responds**: "It's actually green."
6. **Agent stores new information**:
```python
# Agent calls add_memories
add_memories({"message": "The user's favorite color is green"})
```
7. **Agent confirms**: "Thanks, I'll remember that your favorite color is green."
## Example Use Cases
- **Personal Assistant** - Remember user preferences, past requests, and important dates
```
User: "What restaurants did I say I liked last time?"
Agent: *retrieves memories* "You mentioned enjoying Bella Italia and The Golden Dragon."
```
- **Customer Support** - Recall previous issues a customer has had
```
User: "I'm having that same problem again!"
Agent: *retrieves memories* "Is this related to the login issue you reported last week?"
```
- **Educational AI** - Track student progress and tailor teaching accordingly
```
User: "Let's continue our math lesson."
Agent: *retrieves memories* "Last time we were working on quadratic equations. Would you like to continue with that?"
```
- **Healthcare Assistant** - Remember symptoms, medications, and health concerns
```
User: "Have I told you about my allergy medication?"
Agent: *retrieves memories* "Yes, you mentioned you're taking Claritin for your pollen allergies."
```
## Troubleshooting
- **Missing API Keys**:
- Error: "API_KEY environment variable must be set"
- Solution: Ensure all environment variables are set correctly in your .env file or system environment
- **Connection Issues**:
- Error: "Failed to connect to API"
- Solution: Check your network connection and API key permissions. Verify the API keys are valid and have the necessary permissions.
- **Empty Memory Results**:
- Symptom: Agent always responds with "No memories found"
- Solution: This is normal for new users. The memory database builds up over time as conversations occur. It's also possible your query isn't semantically similar to stored memories - try different phrasing.
- **Agent Not Using Memories**:
- Symptom: The agent retrieves memories but doesn't incorporate them in responses
- Solution: Update the agent's prompt to explicitly instruct it to use the retrieved memories in its responses
## Conclusion
By integrating ElevenLabs Conversational AI with Mem0, you can create voice agents that maintain context across conversations and provide personalized responses based on user history. This powerful combination enables:
- More natural, context-aware conversations
- Personalized user experiences that improve over time
- Reduced need for users to repeat information
- Long-term relationship building between users and AI agents
## Help
- For more details on ElevenLabs, visit the [ElevenLabs Conversational AI Documentation](https://elevenlabs.io/docs/api-reference/conversational-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" />
+126
View File
@@ -0,0 +1,126 @@
---
title: Flowise
---
The [**Mem0 Memory**](https://github.com/mem0ai/mem0) integration with [Flowise](https://github.com/FlowiseAI/Flowise) enables persistent memory capabilities for your AI chatflows. [Flowise](https://flowiseai.com/) is an open-source low-code tool for developers to build customized LLM orchestration flows & AI agents using a drag & drop interface.
## Overview
1. 🧠 Provides persistent memory storage for Flowise chatflows
2. 🔄 Seamless integration with existing Flowise templates
3. 🚀 Compatible with various LLM nodes in Flowise
4. 📝 Supports custom memory configurations
5. ⚡ Easy to set up and manage
## Prerequisites
Before setting up Mem0 with Flowise, ensure you have:
1. [Flowise installed](https://github.com/FlowiseAI/Flowise#⚡quick-start) (NodeJS >= 18.15.0 required):
```bash
npm install -g flowise
npx flowise start
```
2. Access to the Flowise UI at http://localhost:3000
3. Basic familiarity with [Flowise's LLM orchestration](https://flowiseai.com/#features) concepts
## Setup and Configuration
### 1. Set Up Flowise
1. Open the Flowise application and create a new canvas, or select a template from the Flowise marketplace.
2. In this example, we use the **Conversation Chain** template.
3. Replace the default **Buffer Memory** with **Mem0 Memory**.
![Flowise Memory Integration](https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/flowise-flow.png)
### 2. Obtain Your Mem0 API Key
1. Navigate to the [Mem0 API Key dashboard](https://app.mem0.ai/dashboard/api-keys).
2. Generate or copy your existing Mem0 API Key.
![Mem0 API Key](https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/api-key.png)
### 3. Configure Mem0 Credentials
1. Enter the **Mem0 API Key** in the Mem0 Credentials section.
2. Configure additional settings as needed:
```typescript
{
"apiKey": "m0-xxx",
"userId": "user-123", // Optional: Specify user ID
"projectId": "proj-xxx", // Optional: Specify project ID
"orgId": "org-xxx" // Optional: Specify organization ID
}
```
<figure>
<img src="https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/creds.png" alt="Mem0 Credentials" />
<figcaption>Configure API Credentials</figcaption>
</figure>
## Memory Features
### 1. Basic Memory Storage
Test your memory configuration:
1. Save your Flowise configuration
2. Run a test chat and store some information
3. Verify the stored memories in the [Mem0 Dashboard](https://app.mem0.ai/dashboard/requests)
![Flowise Test Chat](https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/flowise-chat-1.png)
### 2. Memory Retention
Validate memory persistence:
1. Clear the chat history in Flowise
2. Ask a question about previously stored information
3. Confirm that the AI remembers the context
![Testing Memory Retention](https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/flowise-chat-2.png)
## Advanced Configuration
### Memory Settings
![Mem0 Settings](https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/settings.png)
Available settings include:
1. **Search Only Mode**: Enable memory retrieval without creating new memories
2. **Mem0 Entities**: Configure identifiers:
- `user_id`: Unique identifier for each user
- `run_id`: Specific conversation session ID
- `app_id`: Application identifier
- `agent_id`: AI agent identifier
3. **Project ID**: Assign memories to specific projects
4. **Organization ID**: Organize memories by organization
### Platform Configuration
Additional settings available in [Mem0 Project Settings](https://app.mem0.ai/dashboard/project-settings):
1. **Custom Instructions**: Define memory extraction rules
2. **Expiration Date**: Set automatic memory cleanup periods
![Mem0 Project Settings](https://raw.githubusercontent.com/FlowiseAI/FlowiseDocs/main/en/.gitbook/assets/mem0/mem0-settings.png)
## Best Practices
1. **User Identification**: Use consistent `user_id` values for reliable memory retrieval
2. **Memory Organization**: Utilize projects and organizations for better memory management
3. **Regular Maintenance**: Monitor and clean up unused memories periodically
## Help & Resources
- [Flowise Documentation](https://flowiseai.com/docs)
- [Flowise GitHub Repository](https://github.com/FlowiseAI/Flowise)
- [Flowise Website](https://flowiseai.com/)
- [Mem0 Platform](https://app.mem0.ai/)
- Need assistance? Reach out through:
<Snippet file="get-help.mdx" />
+353
View File
@@ -0,0 +1,353 @@
---
title: Livekit
---
This guide demonstrates how to create a memory-enabled voice assistant using LiveKit, Deepgram, OpenAI, and Mem0, focusing on creating an intelligent, context-aware travel planning agent.
## Prerequisites
Before you begin, make sure you have:
1. Installed Livekit Agents SDK with voice dependencies of silero and deepgram:
```bash
pip install livekit \
livekit-agents \
livekit-plugins-silero \
livekit-plugins-deepgram \
livekit-plugins-openai
```
2. Installed Mem0 SDK:
```bash
pip install mem0ai
```
3. Set up your API keys in a `.env` file:
```sh
LIVEKIT_URL=your_livekit_url
LIVEKIT_API_KEY=your_livekit_api_key
LIVEKIT_API_SECRET=your_livekit_api_secret
DEEPGRAM_API_KEY=your_deepgram_api_key
MEM0_API_KEY=your_mem0_api_key
OPENAI_API_KEY=your_openai_api_key
```
> **Note**: Make sure to have a Livekit and Deepgram account. You can find these variables `LIVEKIT_URL` , `LIVEKIT_API_KEY` and `LIVEKIT_API_SECRET` from [LiveKit Cloud Console](https://cloud.livekit.io/) and for more information you can refer this website [LiveKit Documentation](https://docs.livekit.io/home/cloud/keys-and-tokens/). For `DEEPGRAM_API_KEY` you can get from [Deepgram Console](https://console.deepgram.com/) refer this website [Deepgram Documentation](https://developers.deepgram.com/docs/create-additional-api-keys) for more details.
## Code Breakdown
Let's break down the key components of this implementation:
### 1. Setting Up Dependencies and Environment
```python
import asyncio
import logging
import os
from typing import List, Dict, Any, Annotated
import aiohttp
from dotenv import load_dotenv
from livekit.agents import (
AutoSubscribe,
JobContext,
JobProcess,
WorkerOptions,
cli,
llm,
metrics,
)
from livekit import rtc, api
from livekit.agents.pipeline import VoicePipelineAgent
from livekit.plugins import deepgram, openai, silero
from mem0 import AsyncMemoryClient
# Load environment variables
load_dotenv()
# Configure logging
logger = logging.getLogger("memory-assistant")
logger.setLevel(logging.INFO)
# Define a global user ID for simplicity
USER_ID = "voice_user"
# Initialize Mem0 client
mem0 = AsyncMemoryClient()
```
This section handles:
- Importing required modules
- Loading environment variables
- Setting up logging
- Extracting user identification
- Initializing the Mem0 client
### 2. Memory Enrichment Function
```python
async def _enrich_with_memory(agent: VoicePipelineAgent, chat_ctx: llm.ChatContext):
"""Add memories and Augment chat context with relevant memories"""
if not chat_ctx.messages:
return
# Store user message in Mem0
user_msg = chat_ctx.messages[-1]
await mem0.add(
[{"role": "user", "content": user_msg.content}],
user_id=USER_ID
)
# Search for relevant memories
results = await mem0.search(
user_msg.content,
user_id=USER_ID,
)
# Augment context with retrieved memories
if results:
memories = ' '.join([result["memory"] for result in results])
logger.info(f"Enriching with memory: {memories}")
rag_msg = llm.ChatMessage.create(
text=f"Relevant Memory: {memories}\n",
role="assistant",
)
# Modify chat context with retrieved memories
chat_ctx.messages[-1] = rag_msg
chat_ctx.messages.append(user_msg)
```
This function:
- Stores user messages in Mem0
- Performs semantic search for relevant memories
- Augments the chat context with retrieved memories
- Enables contextually aware responses
### 3. Prewarm and Entrypoint Functions
```python
def prewarm_process(proc: JobProcess):
# Preload silero VAD in memory to speed up session start
proc.userdata["vad"] = silero.VAD.load()
async def entrypoint(ctx: JobContext):
# Connect to LiveKit room
await ctx.connect(auto_subscribe=AutoSubscribe.AUDIO_ONLY)
# Wait for participant
participant = await ctx.wait_for_participant()
# Initialize Mem0 client
mem0 = AsyncMemoryClient()
# Define initial system context
initial_ctx = llm.ChatContext().append(
role="system",
text=(
"""
You are a helpful voice assistant.
You are a travel guide named George and will help the user to plan a travel trip of their dreams.
You should help the user plan for various adventures like work retreats, family vacations or solo backpacking trips.
You should be careful to not suggest anything that would be dangerous, illegal or inappropriate.
You can remember past interactions and use them to inform your answers.
Use semantic memory retrieval to provide contextually relevant responses.
"""
),
)
# Create VoicePipelineAgent with memory capabilities
agent = VoicePipelineAgent(
chat_ctx=initial_ctx,
vad=silero.VAD.load(),
stt=deepgram.STT(),
llm=openai.LLM(model="gpt-4o-mini"),
tts=openai.TTS(),
before_llm_cb=_enrich_with_memory,
)
# Start agent and initial greeting
agent.start(ctx.room, participant)
await agent.say(
"Hello! I'm George. Can I help you plan an upcoming trip? ",
allow_interruptions=True
)
# Run the application
if __name__ == "__main__":
cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint, prewarm_fnc=prewarm_process))
```
The entrypoint function:
- Connects to LiveKit room
- Initializes Mem0 memory client
- Sets up initial system context
- Creates a VoicePipelineAgent with memory enrichment
- Starts the agent with an initial greeting
## Create a Memory-Enabled Voice Agent
Now that we've explained each component, here's the complete implementation that combines OpenAI Agents SDK for voice with Mem0's memory capabilities:
```python
import asyncio
import logging
import os
from typing import List, Dict, Any, Annotated
import aiohttp
from dotenv import load_dotenv
from livekit.agents import (
AutoSubscribe,
JobContext,
JobProcess,
WorkerOptions,
cli,
llm,
metrics,
)
from livekit import rtc, api
from livekit.agents.pipeline import VoicePipelineAgent
from livekit.plugins import deepgram, openai, silero
from mem0 import AsyncMemoryClient
# Load environment variables
load_dotenv()
# Configure logging
logger = logging.getLogger("memory-assistant")
logger.setLevel(logging.INFO)
# Define a global user ID for simplicity
USER_ID = "voice_user"
# Initialize Mem0 memory client
mem0 = AsyncMemoryClient()
def prewarm_process(proc: JobProcess):
# Preload silero VAD in memory to speed up session start
proc.userdata["vad"] = silero.VAD.load()
async def entrypoint(ctx: JobContext):
# Connect to LiveKit room
await ctx.connect(auto_subscribe=AutoSubscribe.AUDIO_ONLY)
# Wait for participant
participant = await ctx.wait_for_participant()
async def _enrich_with_memory(agent: VoicePipelineAgent, chat_ctx: llm.ChatContext):
"""Add memories and Augment chat context with relevant memories"""
if not chat_ctx.messages:
return
# Store user message in Mem0
user_msg = chat_ctx.messages[-1]
await mem0.add(
[{"role": "user", "content": user_msg.content}],
user_id=USER_ID
)
# Search for relevant memories
results = await mem0.search(
user_msg.content,
user_id=USER_ID,
)
# Augment context with retrieved memories
if results:
memories = ' '.join([result["memory"] for result in results])
logger.info(f"Enriching with memory: {memories}")
rag_msg = llm.ChatMessage.create(
text=f"Relevant Memory: {memories}\n",
role="assistant",
)
# Modify chat context with retrieved memories
chat_ctx.messages[-1] = rag_msg
chat_ctx.messages.append(user_msg)
# Define initial system context
initial_ctx = llm.ChatContext().append(
role="system",
text=(
"""
You are a helpful voice assistant.
You are a travel guide named George and will help the user to plan a travel trip of their dreams.
You should help the user plan for various adventures like work retreats, family vacations or solo backpacking trips.
You should be careful to not suggest anything that would be dangerous, illegal or inappropriate.
You can remember past interactions and use them to inform your answers.
Use semantic memory retrieval to provide contextually relevant responses.
"""
),
)
# Create VoicePipelineAgent with memory capabilities
agent = VoicePipelineAgent(
chat_ctx=initial_ctx,
vad=silero.VAD.load(),
stt=deepgram.STT(),
llm=openai.LLM(model="gpt-4o-mini"),
tts=openai.TTS(),
before_llm_cb=_enrich_with_memory,
)
# Start agent and initial greeting
agent.start(ctx.room, participant)
await agent.say(
"Hello! I'm George. Can I help you plan an upcoming trip? ",
allow_interruptions=True
)
# Run the application
if __name__ == "__main__":
cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint, prewarm_fnc=prewarm_process))
```
## Key Features of This Implementation
1. **Semantic Memory Retrieval**: Uses Mem0 to store and retrieve contextually relevant memories
2. **Voice Interaction**: Leverages LiveKit for voice communication
3. **Intelligent Context Management**: Augments conversations with past interactions
4. **Travel Planning Specialization**: Focused on creating a helpful travel guide assistant
## Running the Example
To run this example:
1. Install all required dependencies
2. Set up your `.env` file with the necessary API keys
3. Ensure your microphone and audio setup are configured
4. Run the script with Python 3.11 or newer and with the following command:
```sh
python mem0-livekit-voice-agent.py start
```
5. After the script starts, you can interact with the voice agent using [Livekit's Agent Platform](https://agents-playground.livekit.io/) and Connect to the agent inorder to start conversations.
## Best Practices for Voice Agents with Memory
1. **Context Preservation**: Store enough context with each memory for effective retrieval
2. **Privacy Considerations**: Implement secure memory management
3. **Relevant Memory Filtering**: Use semantic search to retrieve only the most pertinent memories
4. **Error Handling**: Implement robust error handling for memory operations
## Debugging Function Tools
- To run the script in debug mode simply start the assistant with `dev` mode:
```sh
python mem0-livekit-voice-agent.py dev
```
- When working with memory-enabled voice agents, use Python's `logging` module for effective debugging:
```python
import logging
# Set up logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("memory_voice_agent")
```
+66
View File
@@ -220,4 +220,70 @@ Here are the available integrations for Mem0:
>
Integrate Mem0 as an MCP Server in Cursor.
</Card>
<Card
title="Livekit"
icon={
<svg
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
>
<text
x="12"
y="16"
fontFamily="Arial"
fontSize="12"
textAnchor="middle"
fill="currentColor"
fontWeight="bold"
>
LK
</text>
</svg>
}
href="/integrations/livekit"
>
Integrate Mem0 with Livekit for voice agents.
</Card>
<Card
title="ElevenLabs"
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
>
<rect width="24" height="24" fill="white"/>
<rect x="8" y="4" width="2" height="16" fill="black"/>
<rect x="14" y="4" width="2" height="16" fill="black"/>
</svg>
}
href="/integrations/elevenlabs"
>
Build voice agents with memory using ElevenLabs Conversational AI.
</Card>
<Card
title="Pipecat"
icon={
<svg
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" fill="currentColor"/>
<circle cx="8.5" cy="9" r="1.5" fill="currentColor"/>
<circle cx="15.5" cy="9" r="1.5" fill="currentColor"/>
<path d="M12 16c1.66 0 3-1.34 3-3H9c0 1.66 1.34 3 3 3z" fill="currentColor"/>
<path d="M17.5 12c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" fill="currentColor"/>
<path d="M6.5 12c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9s1.5.67 1.5 1.5S7.33 12 6.5 12z" fill="currentColor"/>
</svg>
}
href="/integrations/pipecat"
>
Build conversational AI agents with memory using Pipecat.
</Card>
</CardGroup>
+218
View File
@@ -0,0 +1,218 @@
---
title: 'Pipecat'
description: 'Integrate Mem0 with Pipecat for conversational memory in AI agents'
---
# Pipecat Integration
Mem0 seamlessly integrates with [Pipecat](https://pipecat.ai), providing long-term memory capabilities for conversational AI agents. This integration allows your Pipecat-powered applications to remember past conversations and provide personalized responses based on user history.
## Installation
To use Mem0 with Pipecat, install the required dependencies:
```bash
pip install "pipecat-ai[mem0]"
```
You'll also need to set up your Mem0 API key as an environment variable:
```bash
export MEM0_API_KEY=your_mem0_api_key
```
You can obtain a Mem0 API key by signing up at [mem0.ai](https://mem0.ai).
## Configuration
Mem0 integration is provided through the `Mem0MemoryService` class in Pipecat. Here's how to configure it:
```python
from pipecat.services.mem0 import Mem0MemoryService
memory = Mem0MemoryService(
api_key=os.getenv("MEM0_API_KEY"), # Your Mem0 API key
user_id="unique_user_id", # Unique identifier for the end user
agent_id="my_agent", # Identifier for the agent using the memory
run_id="session_123", # Optional: specific conversation session ID
params={ # Optional: configuration parameters
"search_limit": 10, # Maximum memories to retrieve per query
"search_threshold": 0.1, # Relevance threshold (0.0 to 1.0)
"system_prompt": "Here are your past memories:", # Custom prefix for memories
"add_as_system_message": True, # Add memories as system (True) or user (False) message
"position": 1, # Position in context to insert memories
}
)
```
## Pipeline Integration
The `Mem0MemoryService` should be positioned between your context aggregator and LLM service in the Pipecat pipeline:
```python
pipeline = Pipeline([
transport.input(),
stt, # Speech-to-text for audio input
user_context, # User context aggregator
memory, # Mem0 Memory service enhances context here
llm, # LLM for response generation
tts, # Optional: Text-to-speech
transport.output(),
assistant_context # Assistant context aggregator
])
```
## Example: Voice Agent with Memory
Here's a complete example of a Pipecat voice agent with Mem0 memory integration:
```python
import asyncio
import os
from fastapi import FastAPI, WebSocket
from pipecat.frames.frames import TextFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.task import PipelineTask
from pipecat.pipeline.runner import PipelineRunner
from pipecat.services.mem0 import Mem0MemoryService
from pipecat.services.openai import OpenAILLMService, OpenAIUserContextAggregator, OpenAIAssistantContextAggregator
from pipecat.transports.network.fastapi_websocket import (
FastAPIWebsocketTransport,
FastAPIWebsocketParams
)
from pipecat.serializers.protobuf import ProtobufFrameSerializer
from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.services.whisper import WhisperSTTService
app = FastAPI()
@app.websocket("/chat")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
# Basic setup with minimal configuration
user_id = "user123"
# WebSocket transport
transport = FastAPIWebsocketTransport(
websocket=websocket,
params=FastAPIWebsocketParams(
audio_out_enabled=True,
vad_enabled=True,
vad_analyzer=SileroVADAnalyzer(),
vad_audio_passthrough=True,
serializer=ProtobufFrameSerializer(),
)
)
# Core services
user_context = OpenAIUserContextAggregator()
assistant_context = OpenAIAssistantContextAggregator()
stt = WhisperSTTService(api_key=os.getenv("OPENAI_API_KEY"))
# Memory service - the key component
memory = Mem0MemoryService(
api_key=os.getenv("MEM0_API_KEY"),
user_id=user_id,
agent_id="fastapi_memory_bot"
)
# LLM for response generation
llm = OpenAILLMService(
api_key=os.getenv("OPENAI_API_KEY"),
model="gpt-3.5-turbo",
system_prompt="You are a helpful assistant that remembers past conversations."
)
# Simple pipeline
pipeline = Pipeline([
transport.input(),
stt, # Speech-to-text for audio input
user_context,
memory, # Memory service enhances context here
llm,
transport.output(),
assistant_context
])
# Run the pipeline
runner = PipelineRunner()
task = PipelineTask(pipeline)
# Event handlers for WebSocket connections
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
# Send welcome message when client connects
await task.queue_frame(TextFrame("Hello! I'm a memory bot. I'll remember our conversation."))
@transport.event_handler("on_client_disconnected")
async def on_client_disconnected(transport, client):
# Clean up when client disconnects
await task.cancel()
await runner.run(task)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
```
## How It Works
When integrated with Pipecat, Mem0 provides two key functionalities:
### 1. Message Storage
All conversation messages are automatically stored in Mem0 for future reference:
- Captures the full message history from context frames
- Associates messages with the specified user, agent, and run IDs
- Stores metadata to enable efficient retrieval
### 2. Memory Retrieval
When a new user message is detected:
1. The message is used as a search query to find relevant past memories
2. Relevant memories are retrieved from Mem0's database
3. Memories are formatted and added to the conversation context
4. The enhanced context is passed to the LLM for response generation
## Additional Configuration Options
### Memory Search Parameters
You can customize how memories are retrieved and used:
```python
memory = Mem0MemoryService(
api_key=os.getenv("MEM0_API_KEY"),
user_id="user123",
params={
"search_limit": 5, # Retrieve up to 5 memories
"search_threshold": 0.2, # Higher threshold for more relevant matches
"api_version": "v2", # Mem0 API version
}
)
```
### Memory Presentation Options
Control how memories are presented to the LLM:
```python
memory = Mem0MemoryService(
api_key=os.getenv("MEM0_API_KEY"),
user_id="user123",
params={
"system_prompt": "Previous conversations with this user:",
"add_as_system_message": True, # Add as system message instead of user message
"position": 0, # Insert at the beginning of the context
}
)
```
## Resources
- [Mem0 Pipecat Integration](https://docs.pipecat.ai/server/services/memory/mem0)
- [Pipecat Documentation](https://docs.pipecat.ai)
+14 -1
View File
@@ -10,7 +10,6 @@ The [**Mem0 AI SDK Provider**](https://www.npmjs.com/package/@mem0/vercel-ai-pro
## Overview
In this guide, we'll create a Travel Agent AI that:
1. 🧠 Offers persistent memory storage for conversational AI
2. 🔄 Enables smooth integration with the Vercel AI SDK
3. 🚀 Ensures compatibility with multiple LLM providers
@@ -191,6 +190,20 @@ npm install @mem0/vercel-ai-provider
console.log(result);
```
### 5. Get sources from memory
```typescript
const { text, sources } = await generateText({
model: mem0("gpt-4-turbo"),
prompt: "Suggest me a good car to buy!",
});
console.log(sources);
```
The same can be done for `streamText` as well.
## Key Features
- `createMem0()`: Initializes a new Mem0 provider instance.
+122
View File
@@ -0,0 +1,122 @@
# Mem0
## High Level
[What is Mem0?](https://docs.mem0.ai/overview): consider using Mem0, when building conversational AI agents with memory. The page discusses the main reasons to use Mem0: Context Management, Smart Retrieval System, Simple API Integration and Dual Storage Architecture(vector and graph database).
[How is Mem0 different from traditional RAG?](https://docs.mem0.ai/faqs#how-mem0-is-different-from-traditional-rag): Mem0's memory for LLMs offers superior entity relationship understanding, contextual continuity, and adaptive learning compared to RAG. It retains information across sessions, dynamically updates with new data, and personalizes interactions, making it ideal for context-aware AI applications.
## Concepts
[Memory Types](https://docs.mem0.ai/core-concepts/memory-types): Mem0 implements both short-term memory (for conversation history and immediate context) and long-term memory (for persistent storage of factual, episodic, and semantic information) to maintain context and personalization across interactions.
[Memory Operations](https://docs.mem0.ai/core-concepts/memory-operations): Two core operations power Mem0's functionality: `add` Processes and stores conversations through information extraction, conflict resolution, and dual storage and `search` Retrieves relevant memories using semantic search with query processing and result ranking
[Information Processing](https://docs.mem0.ai/core-concepts/memory-operations): When adding memories, Mem0 uses LLMs to extract relevant information, identify entities and relationships, and resolve conflicts with existing data.
[Storage Architecture](https://docs.mem0.ai/core-concepts/memory-types): Mem0 combines vector embeddings for semantic information storage with efficient retrieval mechanisms, enabling fast access to relevant past interactions while maintaining user-specific context across sessions.
## How-to guides
### Installation
[Mem0 Installation](https://docs.mem0.ai/quickstart): Mem0 offers two installation options: Mem0 Platform (Managed Solution) and Mem0 OSS.
[How to: install Mem0 Platform (Managed Solution)](https://docs.mem0.ai/quickstart#mem0-platform-managed-solution): The easiest way to get started with Mem0 is to use the managed platform. This approach eliminates the need to set up and maintain your own infrastructure.
[How to: install Mem0 OSS](https://docs.mem0.ai/quickstart#mem0-open-source): If you prefer to manage your own infrastructure, you can install Mem0 OSS. This option requires setting up your own infrastructure and managing your own vector database.
## Platform
### Usage
[Initialize Client](https://docs.mem0.ai/platform/quickstart#3-instantiate-client): Mem0 gives two ways to initialize the client: Synchronous -> MemoryClient and Asynchronous -> AsyncMemoryClient.
[How to: add memories](https://docs.mem0.ai/platform/quickstart#4-1-create-memories): Add memories to Mem0 using messages or a simple string. The `add` method allows adding memories to a specific memory by providing `user_id`, `agent_id`, `run_id`, or `app_id`.
[How to: search memories](https://docs.mem0.ai/platform/quickstart#4-2-search-memories): Search memories in Mem0 by passing a query string and optional parameters. The `search` method returns a list of memories sorted by relevance. Csutom filters can also be passed to make the search more specific.
[How to: get all memories](https://docs.mem0.ai/platform/quickstart#4-4-get-all-memories): Get all the memories by passing a `user_id`, `agent_id`, `run_id`, or `app_id`. Also custom filters can be passed to filter the memories.
[How to: get memory history](https://docs.mem0.ai/platform/quickstart#4-5-get-memory-history): Get the history of a specific memory by passing `memory_id` after adding memories using the `add` method.
[How to: update memory](https://docs.mem0.ai/platform/quickstart#4-6-update-memory): Update a memory by passing `memory_id` after adding memories using the `add` method.
[How to: delete memory](https://docs.mem0.ai/platform/quickstart#4-7-delete-memories): Delete memories by passing `memory_id` after adding memories using the `add` method.
[How to: reset client](https://docs.mem0.ai/platform/quickstart#4-8-reset-client): Reset the client where all the memories, users, agents, sessions and runs are deleted.
[How to: batch update memories](https://docs.mem0.ai/platform/quickstart#4-9-batch-update-memories): Batch update memories by passing a list of memories to the `batch_update` method.
[How to: batch delete memories](https://docs.mem0.ai/platform/quickstart#4-10-batch-delete-memories): Batch delete memories by passing a list of memories to the `batch_delete` method.
## 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.
## OSS
### Usage
#### Python
[Initialize python client](https://docs.mem0.ai/open-source/python-quickstart#installation): Install Mem0 with `pip install mem0ai`, then initialize the client with `from mem0 import Memory; m = Memory()` (requires OpenAI API key). For advanced usage, configure with custom parameters or enable graph memory with `Memory(enable_graph=True)`.
[Configuration Parameters](https://docs.mem0.ai/open-source/python-quickstart#configuration-parameters): Mem0 offers extensive configuration options for vector stores (provider, host, port), LLMs (provider, model, temperature, API keys), embedders (provider, model), graph stores (provider, URL, credentials), and general settings (history path, API version, custom prompts) - all customizable through a comprehensive configuration dictionary.
[How to: add memories](https://docs.mem0.ai/open-source/python-quickstart#store-a-memory): Add memories to Mem0 using the Python OSS client's `add` method with messages or a simple string. This method allows adding memories to a specific memory by providing `user_id`, `agent_id`, `run_id`, or `app_id`.
[How to: search memories](https://docs.mem0.ai/open-source/python-quickstart#search-memories): Search memories in Mem0 using the Python OSS client by passing a query string and optional parameters. The `search` method returns a list of memories sorted by relevance. Custom filters can also be passed to make the search more specific.
[How to: get all memories](https://docs.mem0.ai/open-source/python-quickstart#retrieve-memories): Get all the memories using the Python OSS client by passing a `user_id`, `agent_id`, `run_id`, or `app_id`. Also custom filters can be passed to filter the memories.
[How to: get memory history](https://docs.mem0.ai/open-source/python-quickstart#memory-history): Get the history of a specific memory in the Python OSS client by passing `memory_id` after adding memories using the `add` method.
[How to: update memory](https://docs.mem0.ai/open-source/python-quickstart#update-a-memory): Update a memory in the Python OSS client by passing `memory_id` after adding memories using the `add` method.
[How to: delete memory](https://docs.mem0.ai/open-source/python-quickstart#delete-memory): Delete memories in the Python OSS client by passing `memory_id` after adding memories using the `add` method.
#### Node.js
[Initialize node client](https://docs.mem0.ai/open-source/node-quickstart#installation): Install Mem0 with `npm install mem0ai`, then initialize the client with `import { Memory } from 'mem0ai'; const m = new Memory();` (requires OpenAI API key). For advanced usage, configure with custom parameters or enable graph memory with `new Memory({ enableGraph: true })`.
[How to: add memories](https://docs.mem0.ai/open-source/node-quickstart#store-a-memory): Add memories to Mem0 using the Node.js OSS client's `add` method with messages or a simple string. This method allows adding memories to a specific memory by providing `user_id`, `agent_id`, `run_id`, or `app_id`.
[How to: search memories](https://docs.mem0.ai/open-source/node-quickstart#search-memories): Search memories in Mem0 using the Node.js OSS client by passing a query string and optional parameters. The `search` method returns a list of memories sorted by relevance. Custom filters can also be passed to make the search more specific.
[How to: get all memories](https://docs.mem0.ai/open-source/node-quickstart#retrieve-memories): Get all the memories using the Node.js OSS client by passing a `user_id`, `agent_id`, `run_id`, or `app_id`. Also custom filters can be passed to filter the memories.
[How to: get memory history](https://docs.mem0.ai/open-source/node-quickstart#memory-history): Get the history of a specific memory in the Node.js OSS client by passing `memory_id` after adding memories using the `add` method.
[How to: update memory](https://docs.mem0.ai/open-source/node-quickstart#update-a-memory): Update a memory in the Node.js OSS client by passing `memory_id` after adding memories using the `add` method.
[How to: delete memory](https://docs.mem0.ai/open-source/node-quickstart#delete-memory): Delete memories in the Node.js OSS client by passing `memory_id` after adding memories using the `add` method.
### 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.
[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.
### Components
#### LLMs
[OpenAI](https://docs.mem0.ai/components/llms/models/openai): Integrate OpenAI LLM models by setting OPENAI_API_KEY and configuring the Memory client with provider settings - supports both standard models (like gpt-4) and structured-outputs, with options for temperature, max tokens and Openrouter integration.
[Anthropic](https://docs.mem0.ai/components/llms/models/anthropic): Integrate Anthropic LLM models by setting ANTHROPIC_API_KEY from your Account Settings Page and configuring the Memory client with provider settings - supports models like claude-3-7-sonnet-latest with customizable temperature and max_tokens parameters.
[Google AI](https://docs.mem0.ai/components/llms/models/google_AI): Integrate Gemini LLM models by setting GEMINI_API_KEY from Google Maker Suite and configuring the Memory client with litellm provider - supports models like gemini-pro with customizable temperature and max_tokens parameters.
[Groq](https://docs.mem0.ai/components/llms/models/groq): Integrate Groq's Language Processing Unit (LPU) optimized models by setting GROQ_API_KEY and configuring the Memory client with provider settings - supports models like mixtral-8x7b-32768 with customizable temperature and max_tokens parameters for high-performance AI inference.
[Together](https://docs.mem0.ai/components/llms/models/together): Integrate Together LLM models by setting TOGETHER_API_KEY and configuring the Memory client with provider settings - supports both standard models (like together-llama-3-8b-instant) and structured-outputs, with options for temperature, max tokens and Openrouter integration.
[Deepseek](https://docs.mem0.ai/components/llms/models/deepseek): Integrate Deepseek LLM models by setting DEEPSEEK_API_KEY and configuring the Memory client with provider settings - supports both standard models (like deepseek-chat) and structured-outputs, with options for temperature, max tokens and Openrouter integration.
[xAI](https://docs.mem0.ai/components/llms/models/xai): Integrate xAI LLM models by setting XAI_API_KEY and configuring the Memory client with provider settings - supports both standard models (like xai-chat) and structured-outputs, with options for temperature, max tokens and Openrouter integration.
[LM Studio](https://docs.mem0.ai/components/llms/models/lmstudio): Run Mem0 locally with LM Studio by configuring the Memory client with provider settings and a local LM Studio server - supports using LM Studio for both LLM inference and embeddings, requiring no external API keys when running fully locally with appropriate models loaded.
[Ollama](https://docs.mem0.ai/components/llms/models/ollama): Run Mem0 locally with Ollama LLM models by configuring the Memory client with provider settings like model (e.g. mixtral:8x7b), temperature and max_tokens - supports tool calling and requires only OpenAI API key for embeddings.
[AWS Bedrock](https://docs.mem0.ai/components/llms/models/aws_bedrock): Integrate AWS Bedrock LLM models by setting AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY and configuring the Memory client with provider settings - supports both standard models (like bedrock-anthropic-claude-3-5-sonnet) and structured-outputs, with options for temperature, max tokens and Openrouter integration.
[Azure OpenAI](https://docs.mem0.ai/components/llms/models/azure_openai): Integrate Azure OpenAI LLM models by setting LLM_AZURE_OPENAI_API_KEY, LLM_AZURE_ENDPOINT, LLM_AZURE_DEPLOYMENT and LLM_AZURE_API_VERSION environment variables and configuring the Memory client with provider settings - supports both standard and structured-output models with customizable deployment, API version, endpoint and headers (note: some features like parallel tool calling and temperature are currently unsupported).
[LiteLLM](https://docs.mem0.ai/components/llms/models/litellm): Integrate LiteLLM LLM models by setting LITELLM_API_KEY and configuring the Memory client with provider settings - supports both standard models (like llama3.1:8b) and structured-outputs, with options for temperature, max tokens and Openrouter integration.
[Mistral](https://docs.mem0.ai/components/llms/models/mistral): Integrate Mistral LLM models by setting MISTRAL_API_KEY and configuring the Memory client with provider settings - supports both standard models (like mistral-large-latest) and structured-outputs, with options for temperature, max tokens and Openrouter integration.
#### Embedders
[OpenAI](https://docs.mem0.ai/components/embedders/models/openai): Integrate OpenAI embedding models by setting OPENAI_API_KEY and configuring the Memory client with provider settings - supports models like text-embedding-3-small (default) and text-embedding-3-large with customizable dimensions.
[Azure OpenAI](https://docs.mem0.ai/components/embedders/models/azure_openai): Integrate Azure OpenAI embedding models by setting EMBEDDING_AZURE_OPENAI_API_KEY, EMBEDDING_AZURE_ENDPOINT, EMBEDDING_AZURE_DEPLOYMENT and EMBEDDING_AZURE_API_VERSION environment variables and configuring the Memory client with provider settings - supports models like text-embedding-3-large with customizable dimensions and Azure-specific configurations through azure_kwargs.
[Vertex AI](https://docs.mem0.ai/components/embedders/models/google_ai): Integrate Google Cloud's Vertex AI embedding models by setting GOOGLE_APPLICATION_CREDENTIALS environment variable to your service account credentials JSON file and configuring the Memory client with provider settings - supports models like text-embedding-004 with customizable embedding types (RETRIEVAL_DOCUMENT, RETRIEVAL_QUERY, etc.) and dimensions.
[Groq](https://docs.mem0.ai/components/embedders/models/groq): Integrate Groq embedding models by setting GROQ_API_KEY and configuring the Memory client with provider settings - supports models like text-embedding-3-small (default) and text-embedding-3-large with customizable dimensions.
[Hugging Face](https://docs.mem0.ai/components/embedders/models/hugging_face): Run Mem0 locally with Hugging Face embedding models by configuring the Memory client with provider settings like model (e.g. multi-qa-MiniLM-L6-cos-v1), embedding dimensions and model_kwargs - requires only OpenAI API key for LLM functionality.
[Ollama](https://docs.mem0.ai/components/embedders/models/ollama): Run Mem0 locally with Ollama embedding models by configuring the Memory client with provider settings like model (e.g. nomic-embed-text), embedding dimensions (default 512) and custom base URL - requires only OpenAI API key for LLM functionality.
[Gemini](https://docs.mem0.ai/components/embedders/models/gemini): Integrate Gemini embedding models by setting GOOGLE_API_KEY and configuring the Memory client with provider settings - supports models like text-embedding-004 with customizable dimensions (default 768) and requires OpenAI API key for LLM functionality.
#### Vector Stores
[Qdrant](https://docs.mem0.ai/components/vectordbs/dbs/qdrant): Integrate Qdrant vector database by configuring the Memory client with provider settings like collection_name, host, port, and other parameters - supports both local and remote deployments with options for persistent storage and custom client configurations.
[Pinecone](https://docs.mem0.ai/components/vectordbs/dbs/pinecone): Integrate Pinecone's managed vector database by configuring the Memory client with serverless or pod deployment options, supporting high-performance vector search with customizable embedding dimensions, distance metrics, and cloud providers (AWS/GCP/Azure) - requires PINECONE_API_KEY and matching embedding model dimensions (e.g. 1536 for OpenAI).
[Milvus](https://docs.mem0.ai/components/vectordbs/dbs/milvus): Integrate Milvus open-source vector database by configuring the Memory client with provider settings like url (default localhost:19530), token (for Zilliz cloud), collection_name, embedding_model_dims (default 1536) and metric_type - supports both local and cloud deployments for AI applications of any scale.
[Weaviate](https://docs.mem0.ai/components/vectordbs/dbs/weaviate): Integrate Weaviate open-source vector search engine by configuring the Memory client with provider settings like collection_name (default: mem0), cluster_url, auth_client_secret and embedding_model_dims (default: 1536) - enables efficient storage and retrieval of high-dimensional vector embeddings.
[Chroma](https://docs.mem0.ai/components/vectordbs/dbs/chroma): Integrate Chroma AI-native open-source vector database by configuring the Memory client with provider settings like collection_name (default: mem0), path (default: db), host, port, and client - enables simple storage and search of embeddings with focus on speed and ease of use.
[Faiss](https://docs.mem0.ai/components/vectordbs/dbs/faiss): Integrate Faiss, a high-performance library for similarity search and clustering of dense vectors, by configuring the Memory client with settings like collection_name, path, and distance_strategy (euclidean/cosine/inner_product) - supports efficient local vector search with in-memory or persistent storage options and is optimized for large-scale production use.
[PGVector](https://docs.mem0.ai/components/vectordbs/dbs/pgvector): Integrate Postgres vector similarity search by configuring the Memory client with database connection settings (user, password, host, port), collection name, embedding dimensions and indexing options (diskann/hnsw) - requires creating vector extension in Postgres and supports both local and cloud deployments.
[Elasticsearch](https://docs.mem0.ai/components/vectordbs/dbs/elasticsearch): Integrate Elasticsearch vector database by configuring host, port, collection name and authentication settings - supports k-NN vector search, cloud/local deployments, custom search queries, and requires `pip install elasticsearch>=8.0.0`.
[Redis](https://docs.mem0.ai/components/vectordbs/dbs/redis): Integrate Redis vector database for real-time vector storage and search by configuring collection name, embedding dimensions (default 1536), and Redis URL - supports both local Docker deployment and remote Redis Stack instances.
[Supabase](https://docs.mem0.ai/components/vectordbs/dbs/supabase): Integrate Supabase's PostgreSQL database with pgvector extension by configuring connection string, collection name, and optional index settings (hnsw/ivfflat) - enables efficient vector similarity search with support for different distance measures (cosine/l2/l1) and requires SQL migrations to enable vector functionality.
[Azure AI Search](https://docs.mem0.ai/components/vectordbs/dbs/azure): Integrate Azure AI Search (formerly Azure Cognitive Search) by configuring service_name, api_key and collection_name - supports vector compression (none/scalar/binary), hybrid search modes, and customizable vector dimensions with automatic extraction of filterable fields like user_id.
[Vertex AI Search](https://docs.mem0.ai/components/vectordbs/dbs/vertex_ai): Integrate Google Cloud's Vertex AI Vector Search by configuring endpoint_id, index_id, deployment_index_id, project details and optional region/credentials - enables efficient vector similarity search through Google Cloud's managed service with support for both get and search operations.
+63 -40
View File
@@ -7,7 +7,7 @@ iconType: "solid"
Mem0 provides a REST API server (written using FastAPI). Users can perform all operations through REST endpoints. The API also includes OpenAPI documentation, accessible at `/docs` when the server is running.
<Frame caption="APIs supported by Mem0 REST API Server">
<img src="/images/rest-api-server.png" />
<img src="/images/rest-api-server.png"/>
</Frame>
## Features
@@ -23,67 +23,90 @@ Mem0 provides a REST API server (written using FastAPI). Users can perform all o
## Running Locally
<Tabs>
<Tab title="With Docker">
<Tab title="With Docker Compose">
The Development Docker Compose comes pre-configured with postgres pgvector, neo4j and a `server/history/history.db` volume for the history database.
1. Create a `.env` file in the current directory and set your environment variables. For example:
The only required environment variable to run the server is `OPENAI_API_KEY`.
```txt
OPENAI_API_KEY=your-openai-api-key
```
1. Create a `.env` file in the `server/` directory and set your environment variables. For example:
2. Either pull the docker image from docker hub or build the docker image locally.
```txt
OPENAI_API_KEY=your-openai-api-key
```
<Tabs>
<Tab title="Pull from Docker Hub">
2. Run the Docker container using Docker Compose:
```bash
docker pull mem0/mem0-api-server
```
```bash
cd server
docker compose up
```
</Tab>
3. Access the API at http://localhost:8888.
<Tab title="Build Locally">
4. Making changes to the server code or the library code will automatically reload the server.
</Tab>
```bash
docker build -t mem0-api-server .
```
<Tab title="With Docker">
</Tab>
</Tabs>
1. Create a `.env` file in the current directory and set your environment variables. For example:
3. Run the Docker container:
```txt
OPENAI_API_KEY=your-openai-api-key
```
``` bash
docker run -p 8000:8000 mem0-api-server --env-file .env
```
2. Either pull the docker image from docker hub or build the docker image locally.
4. Access the API at http://localhost:8000.
<Tabs>
<Tab title="Pull from Docker Hub">
</Tab>
```bash
docker pull mem0/mem0-api-server
```
<Tab title="Without Docker">
</Tab>
1. Create a `.env` file in the current directory and set your environment variables. For example:
<Tab title="Build Locally">
```txt
OPENAI_API_KEY=your-openai-api-key
```
```bash
docker build -t mem0-api-server .
```
2. Install dependencies:
</Tab>
</Tabs>
```bash
pip install -r requirements.txt
```
3. Run the Docker container:
3. Start the FastAPI server:
``` bash
docker run -p 8000:8000 mem0-api-server --env-file .env
```
```bash
uvicorn main:app --reload
```
4. Access the API at http://localhost:8000.
4. Access the API at http://localhost:8000.
</Tab>
</Tab>
<Tab title="Without Docker">
1. Create a `.env` file in the current directory and set your environment variables. For example:
```txt
OPENAI_API_KEY=your-openai-api-key
```
2. Install dependencies:
```bash
pip install -r requirements.txt
```
3. Start the FastAPI server:
```bash
uvicorn main:app --reload
```
4. Access the API at http://localhost:8000.
</Tab>
</Tabs>
## Usage
+22 -15
View File
@@ -786,9 +786,10 @@
"expiration_date": {
"type": "string",
"format": "date-time",
"description": "The date and time when the memory will expire. The default expiration date is 30 days from the date of creation. Format: YYYY-MM-DD",
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD",
"title": "Expiration date",
"nullable": true
"nullable": true,
"default": null
},
"organization": {
"type": "string"
@@ -1193,9 +1194,10 @@
"expiration_date": {
"type": "string",
"format": "date-time",
"description": "The date and time when the memory will expire. The default expiration date is 30 days from the date of creation. Format: YYYY-MM-DD",
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD",
"title": "Expiration date",
"nullable": true
"nullable": true,
"default": null
},
"organization": {
"type": "string"
@@ -1341,9 +1343,10 @@
"expiration_date": {
"type": "string",
"format": "date-time",
"description": "The date and time when the memory will expire. The default expiration date is 30 days from the date of creation. Format: YYYY-MM-DD",
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD",
"title": "Expiration date",
"nullable": true
"nullable": true,
"default": null
},
"created_at": {
"type": "string",
@@ -1388,11 +1391,11 @@
"x-code-samples": [
{
"lang": "Python",
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\nquery = \"Your search query here\"\n\nresults = client.search(query, user_id=\"<user_id>\", output_format=\"v1.0\")\nprint(results)"
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\nquery = \"Your search query here\"\n\nresults = client.search(query, user_id=\"<user_id>\", output_format=\"v1.1\")\nprint(results)"
},
{
"lang": "JavaScript",
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst query = \"Your search query here\";\n\nclient.search(query, { user_id: \"<user_id>\", output_format: \"v1.0\" })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst query = \"Your search query here\";\n\nclient.search(query, { user_id: \"<user_id>\", output_format: \"v1.1\" })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
},
{
"lang": "cURL",
@@ -1475,9 +1478,10 @@
"expiration_date": {
"type": "string",
"format": "date-time",
"description": "The date and time when the memory will expire. The default expiration date is 30 days from the date of creation. Format: YYYY-MM-DD",
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD",
"title": "Expiration date",
"nullable": true
"nullable": true,
"default": null
},
"created_at": {
"type": "string",
@@ -4889,10 +4893,11 @@
"default": true
},
"output_format": {
"description": "It two output formats: `v1.0` (default) and `v1.1`. To enable the latest format, which provides enhanced detail for each memory operation, set the output_format parameter to `v1.1`. Note that `v1.0` will be deprecated in version 0.1.35.",
"description": "It two output formats: `v1.0` (default) and `v1.1`. We recommend using `v1.1` as `v1.0` will be deprecated soon.",
"title": "Output format",
"type": "string",
"nullable": true
"nullable": true,
"default": "v1.0"
},
"custom_categories": {
"description": "A list of categories with category name and it's description.",
@@ -4916,10 +4921,11 @@
"default": false
},
"expiration_date": {
"description": "The date and time when the memory will expire. The default expiration date is 30 days from the date of creation. Format: YYYY-MM-DD",
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD",
"title": "Expiration date",
"type": "string",
"nullable": true
"nullable": true,
"default": null
},
"org_id": {
"description": "The unique identifier of the organization associated with this memory.",
@@ -5016,7 +5022,8 @@
"title": "Output format",
"type": "string",
"nullable": true,
"description": "The search method supports two output formats: `v1.0` (default) and `v1.1`."
"default": "v1.0",
"description": "The search method supports two output formats: `v1.0` (default) and `v1.1`. We recommend using `v1.1` as `v1.0` will be deprecated soon."
},
"org_id": {
"title": "Organization id",
+111 -330
View File
@@ -86,11 +86,7 @@ messages = [
{"role": "assistant", "content": "Hello Alex! I've noted that you're a vegetarian and have a nut allergy. I'll keep this in mind for any food-related recommendations or discussions."}
]
# The default output_format is v1.0
client.add(messages, user_id="alex", output_format="v1.0", version="v2")
# To use the latest output_format, set the output_format parameter to "v1.1"
client.add(messages, user_id="alex", output_format="v1.1", metadata={"food": "vegan"}, version="v2")
client.add(messages, user_id="alex", metadata={"food": "vegan"})
```
```javascript JavaScript
@@ -98,7 +94,7 @@ const messages = [
{"role": "user", "content": "Hi, I'm Alex. I'm a vegetarian and I'm allergic to nuts."},
{"role": "assistant", "content": "Hello Alex! I've noted that you're a vegetarian and have a nut allergy. I'll keep this in mind for any food-related recommendations or discussions."}
];
client.add(messages, { user_id: "alex", output_format: "v1.1", metadata: { food: "vegan" }, version: "v2" })
client.add(messages, { user_id: "alex", metadata: { food: "vegan" } })
.then(response => console.log(response))
.catch(error => console.error(error));
```
@@ -113,40 +109,13 @@ curl -X POST "https://api.mem0.ai/v1/memories/" \
{"role": "assistant", "content": "Hello Alex! I've noted that you're a vegetarian and have a nut allergy. I'll keep this in mind for any food-related recommendations or discussions."}
],
"user_id": "alex",
"output_format": "v1.1",
"metadata": {
"food": "vegan"
},
"version": "v2"
}
}'
```
```json Output (v1.0)
[
{
"id": "a1b2c3d4-e5f6-4g7h-8i9j-k0l1m2n3o4p5",
"data": {
"memory": "Name is Alex"
},
"event": "ADD"
},
{
"id": "b2c3d4e5-f6g7-8h9i-j0k1-l2m3n4o5p6q7",
"data": {
"memory": "Is a vegetarian"
},
"event": "ADD"
},
{
"id": "c3d4e5f6-g7h8-9i0j-k1l2-m3n4o5p6q7r8",
"data": {
"memory": "Is allergic to nuts"
},
"event": "ADD"
}
]
```
```json Output (v1.1)
```json Output
{
"results": [
{
@@ -167,9 +136,6 @@ curl -X POST "https://api.mem0.ai/v1/memories/" \
</CodeGroup>
<Note> The `add` method offers support for two output formats: `v1.0` (default) and `v1.1`. To enable the latest format, which provides enhanced detail for each memory operation, set the `output_format` parameter to `v1.1`. </Note>
<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.
</Note>
@@ -191,11 +157,7 @@ messages = [
{"role": "assistant", "content": "Great! I'll remember that you're interested in vegetarian restaurants in Tokyo for your upcoming trip. I'll prepare a list for you in our next interaction."}
]
# The default output_format is v1.0
client.add(messages, user_id="alex", run_id="trip-planning-2024", output_format="v1.0", version="v2")
# To use the latest output_format, set the output_format parameter to "v1.1"
client.add(messages, user_id="alex", run_id="trip-planning-2024", output_format="v1.1", version="v2")
client.add(messages, user_id="alex", run_id="trip-planning-2024")
```
```javascript JavaScript
@@ -205,7 +167,7 @@ const messages = [
{"role": "user", "content": "Yes, please! Especially in Tokyo."},
{"role": "assistant", "content": "Great! I'll remember that you're interested in vegetarian restaurants in Tokyo for your upcoming trip. I'll prepare a list for you in our next interaction."}
];
client.add(messages, { user_id: "alex", run_id: "trip-planning-2024", output_format: "v1.1", version: "v2" })
client.add(messages, { user_id: "alex", run_id: "trip-planning-2024" })
.then(response => console.log(response))
.catch(error => console.error(error));
```
@@ -222,32 +184,11 @@ curl -X POST "https://api.mem0.ai/v1/memories/" \
{"role": "assistant", "content": "Great! I'll remember that you're interested in vegetarian restaurants in Tokyo for your upcoming trip. I'll prepare a list for you in our next interaction."}
],
"user_id": "alex",
"run_id": "trip-planning-2024",
"output_format": "v1.1",
"version": "v2"
"run_id": "trip-planning-2024"
}'
```
```json Output (v1.0)
[
{
"id": "f2968654-5cd8-4d58-9f40-57ee339846b6",
"data": {
"memory": "Interested in vegetarian restaurants in Tokyo"
},
"event": "ADD"
},
{
"id": "f2968654-5cd8-4d58-9f40-57ee339846b6",
"data": {
"memory": "Planning a trip to Japan next month"
},
"event": "ADD"
}
]
```
```json Output (v1.1)
```json Output
{
"results": [
{
@@ -275,11 +216,7 @@ messages = [
{"role": "assistant", "content": "Understood. I'm an AI tutor with a personality. My name is Alice."}
]
# The default output_format is v1.0
client.add(messages, agent_id="ai-tutor", output_format="v1.0", version="v2")
# To use the latest output_format, set the output_format parameter to "v1.1"
client.add(messages, agent_id="ai-tutor", output_format="v1.1", version="v2")
client.add(messages, agent_id="ai-tutor")
```
```javascript JavaScript
@@ -287,7 +224,7 @@ const messages = [
{"role": "system", "content": "You are an AI tutor with a personality. Give yourself a name for the user."},
{"role": "assistant", "content": "Understood. I'm an AI tutor with a personality. My name is Alice."}
];
client.add(messages, { agent_id: "ai-tutor", output_format: "v1.1", version: "v2" })
client.add(messages, { agent_id: "ai-tutor" })
.then(response => console.log(response))
.catch(error => console.error(error));
```
@@ -301,39 +238,11 @@ curl -X POST "https://api.mem0.ai/v1/memories/" \
{"role": "system", "content": "You are an AI tutor with a personality. Give yourself a name for the user."},
{"role": "assistant", "content": "Understood. I'm an AI tutor with a personality. My name is Alice."}
],
"agent_id": "ai-tutor",
"output_format": "v1.1",
"version": "v2"
"agent_id": "ai-tutor"
}'
```
```json Output (v1.0)
[
{
"id": "a1b2c3d4-e5f6-4g7h-8i9j-k0l1m2n3o4p5",
"data": {
"memory": "Name is Alex"
},
"event": "ADD"
},
{
"id": "b2c3d4e5-f6g7-8h9i-j0k1-l2m3n4o5p6q7",
"data": {
"memory": "Is a vegetarian"
},
"event": "ADD"
},
{
"id": "c3d4e5f6-g7h8-9i0j-k1l2-m3n4o5p6q7r8",
"data": {
"memory": "Is allergic to nuts"
},
"event": "ADD"
}
]
```
```json Output (v1.1)
```json Output
{
"results": [
{
@@ -371,7 +280,7 @@ messages = [
{"role": "assistant", "content": "That's great! I'm going to Dubai next month."},
]
client.add(messages=messages, user_id="user1", agent_id="agent1", version="v2")
client.add(messages=messages, user_id="user1", agent_id="agent1")
```
```javascript JavaScript
@@ -380,7 +289,7 @@ const messages = [
{"role": "assistant", "content": "That's great! I'm going to Dubai next month."},
]
client.add(messages, { user_id: "user1", agent_id: "agent1", version: "v2" })
client.add(messages, { user_id: "user1", agent_id: "agent1" })
.then(response => console.log(response))
.catch(error => console.error(error));
```
@@ -395,23 +304,25 @@ curl -X POST "https://api.mem0.ai/v1/memories/" \
{"role": "assistant", "content": "That's great! I'm going to Dubai next month."},
],
"user_id": "user1",
"agent_id": "agent1",
"version": "v2"
"agent_id": "agent1"
}'
```
```json Output
[
{
'id': 'c57abfa2-f0ac-48af-896a-21728dbcecee0',
'data': {'memory': 'Travelling to San Francisco'},
'event': 'ADD'
},
{ 'id': '0e8c003f-7db7-426a-9fdc-a46f9331a0c2',
'data': {'memory': 'Going to Dubai next month'},
'event': 'ADD'
}
]
{
"results": [
{
"id": "c57abfa2-f0ac-48af-896a-21728dbcecee0",
"data": {"memory": "Travelling to San Francisco"},
"event": "ADD"
},
{
"id": "0e8c003f-7db7-426a-9fdc-a46f9331a0c2",
"data": {"memory": "Going to Dubai next month"},
"event": "ADD"
}
]
}
```
</CodeGroup>
@@ -483,15 +394,17 @@ curl -X POST "https://api.mem0.ai/v1/memories/" \
```
```json Output
[
{
"id": "a1b2c3d4-e5f6-4g7h-8i9j-k0l1m2n3o4p5",
"data": {
"memory": "In San Francisco until August 31st"
},
"event": "ADD"
}
]
{
"results": [
{
"id": "a1b2c3d4-e5f6-4g7h-8i9j-k0l1m2n3o4p5",
"data": {
"memory": "In San Francisco until August 31st"
},
"event": "ADD"
}
]
}
```
</CodeGroup>
@@ -519,11 +432,7 @@ Pass user messages, interactions, and queries into our search method to retrieve
```python Python
query = "What should I cook for dinner today?"
# The default output_format is v1.0
client.search(query, user_id="alex", output_format="v1.0")
# To use the latest output_format, set the output_format parameter to "v1.1"
client.search(query, user_id="alex", output_format="v1.1")
client.search(query, user_id="alex")
```
```javascript JavaScript
@@ -544,23 +453,7 @@ curl -X POST "https://api.mem0.ai/v1/memories/search/" \
}'
```
```json Output (v1.0)
[
{
"id": "7f165f7e-b411-4afe-b7e5-35789b72c4a5",
"memory": "Vegetarian. Allergic to nuts.",
"user_id": "alex",
"metadata": {"food": "vegan"},
"categories": ["food_preferences"],
"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 (v1.1)
```json Output
{
"results": [
{
@@ -608,19 +501,21 @@ curl -X POST "https://api.mem0.ai/v1/memories/search/?version=v2" \
```
```json Output
[
{
"id": "7f165f7e-b411-4afe-b7e5-35789b72c4a5",
"memory": "Name: Alex. Vegetarian. Allergic to nuts.",
"user_id": "alex",
"metadata": {"food": "vegan"},
"categories": ["food_preferences"],
"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"
}
]
{
"results": [
{
"id": "7f165f7e-b411-4afe-b7e5-35789b72c4a5",
"memory": "Name: Alex. Vegetarian. Allergic to nuts.",
"user_id": "alex",
"metadata": {"food": "vegan"},
"categories": ["food_preferences"],
"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>
@@ -699,19 +594,21 @@ curl -X POST "https://api.mem0.ai/v1/memories/search/?version=v2" \
```
```json Output
[
{
"id": "7f165f7e-b411-4afe-b7e5-35789b72c4a5",
"memory": "Name: Alex. Vegetarian. Allergic to nuts.",
"user_id": "alex",
"metadata": null,
"categories": ["food_preferences"],
"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"
}
]
{
"results": [
{
"id": "7f165f7e-b411-4afe-b7e5-35789b72c4a5",
"memory": "Name: Alex. Vegetarian. Allergic to nuts.",
"user_id": "alex",
"metadata": null,
"categories": ["food_preferences"],
"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>
@@ -765,19 +662,21 @@ curl -X POST "https://api.mem0.ai/v1/memories/search/?version=v2" \
```
```json Output
[
{
"id": "7f165f7e-b411-4afe-b7e5-35789b72c4a5",
"memory": "Name: Alex. Vegetarian. Allergic to nuts.",
"user_id": "alex",
"metadata": null,
"categories": ["food_preferences"],
"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"
}
]
{
"results": [
{
"id": "7f165f7e-b411-4afe-b7e5-35789b72c4a5",
"memory": "Name: Alex. Vegetarian. Allergic to nuts.",
"user_id": "alex",
"metadata": null,
"categories": ["food_preferences"],
"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>
@@ -840,19 +739,21 @@ curl -X POST "https://api.mem0.ai/v1/memories/search/?version=v2" \
```
```json Output
[
{
"id": "654fee-b411-4afe-b7e5-35789b72c4a5",
"memory": "Name: Alex. Vegetarian. Allergic to nuts.",
"user_id": "alex",
"metadata": {"food": "vegan"},
"categories": ["food_preferences"],
"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"
}
]
{
"results": [
{
"id": "654fee-b411-4afe-b7e5-35789b72c4a5",
"memory": "Name: Alex. Vegetarian. Allergic to nuts.",
"user_id": "alex",
"metadata": {"food": "vegan"},
"categories": ["food_preferences"],
"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>
@@ -936,48 +837,13 @@ curl -X GET "https://api.mem0.ai/v1/memories/?user_id=alex&page=1&page_size=50"
-H "Authorization: Token your-api-key"
```
```json Output (v1.0)
{
"count": 204,
"next": "https://api.mem0.ai/v1/memories/?user_id=alex&page=2&page_size=50",
"previous": null,
"results": [
{
"id":"f38b689d-6b24-45b7-bced-17fbb4d8bac7",
"memory":"是素食主义者,对坚果过敏。",
"agent_id":"travel-assistant",
"hash":"62bc074f56d1f909f1b4c2b639f56f6a",
"metadata":None,
"immutable": false,
"expiration_date": null,
"created_at":"2024-07-25T23:57:00.108347-07:00",
"updated_at":"2024-07-25T23:57:00.108367-07:00",
"categories":None
},
{
"id":"0a14d8f0-e364-4f5c-b305-10da1f0d0878",
"memory":"Will maintain personalized travel preferences for each user. Provide customized recommendations based on dietary restrictions, interests, and past interactions.",
"agent_id":"travel-assistant",
"hash":"35a305373d639b0bffc6c2a3e2eb4244",
"metadata":"None",
"immutable": false,
"expiration_date": null,
"created_at":"2024-07-26T00:31:03.543759-07:00",
"updated_at":"2024-07-26T00:31:03.543778-07:00",
"categories":None
}
... (remaining 48 memories)
]
}
```
```json Output (v1.1)
{
"count": 204,
"next": "https://api.mem0.ai/v1/memories/?user_id=alex&output_format=v1.1&page=2&page_size=50",
"previous": null,
"results": {
"results": [
"results":
[
{
"id":"f38b689d-6b24-45b7-bced-17fbb4d8bac7",
"memory":"是素食主义者,对坚果过敏。",
@@ -1003,8 +869,7 @@ curl -X GET "https://api.mem0.ai/v1/memories/?user_id=alex&page=1&page_size=50"
"categories":None
}
... (remaining 48 memories)
]
}
]
}
```
@@ -1029,48 +894,13 @@ curl -X GET "https://api.mem0.ai/v1/memories/?agent_id=ai-tutor&page=1&page_size
-H "Authorization: Token your-api-key"
```
```json Output (v1.0)
{
"count": 78,
"next": "https://api.mem0.ai/v1/memories/?agent_id=ai-tutor&page=2&page_size=50",
"previous": null,
"results": [
{
"id":"f38b689d-6b24-45b7-bced-17fbb4d8bac7",
"memory":"是素食主义者,对坚果过敏。",
"agent_id":"ai-tutor",
"hash":"62bc074f56d1f909f1b4c2b639f56f6a",
"metadata":None,
"immutable": false,
"expiration_date": null,
"created_at":"2024-07-25T23:57:00.108347-07:00",
"updated_at":"2024-07-25T23:57:00.108367-07:00",
"categories":None
},
{
"id":"0a14d8f0-e364-4f5c-b305-10da1f0d0878",
"memory":"My name is Alice.",
"agent_id":"ai-tutor",
"hash":"35a305373d639b0bffc6c2a3e2eb4244",
"metadata":None,
"immutable": false,
"expiration_date": null,
"created_at":"2024-07-26T00:31:03.543759-07:00",
"updated_at":"2024-07-26T00:31:03.543778-07:00",
"categories":None
}
... (remaining 48 memories)
]
}
```
```json Output (v1.1)
{
"count": 78,
"next": "https://api.mem0.ai/v1/memories/?agent_id=ai-tutor&output_format=v1.1&page=2&page_size=50",
"previous": null,
"results": {
"results": [
"results":
[
{
"id": "f38b689d-6b24-45b7-bced-17fbb4d8bac7",
"memory": "是素食主义者,对坚果过敏。",
@@ -1094,8 +924,7 @@ curl -X GET "https://api.mem0.ai/v1/memories/?agent_id=ai-tutor&page=1&page_size
"updated_at": "2024-07-26T00:31:03.543778-07:00"
}
... (remaining 48 memories)
]
}
]
}
```
</CodeGroup>
@@ -1119,60 +948,13 @@ curl -X GET "https://api.mem0.ai/v1/memories/?user_id=alex&run_id=trip-planning-
-H "Authorization: Token your-api-key"
```
```json Output (v1.0)
```json Output
{
"count": 18,
"next": null,
"previous": null,
"results": [
{
"id":"06d8df63-7bd2-4fad-9acb-60871bcecee0",
"memory":"Planning a trip to Japan next month. Interested in vegetarian restaurants in Tokyo.",
"user_id":"alex",
"hash":"d2088c936e259f2f5d2d75543d31401c",
"metadata":None,
"immutable": false,
"expiration_date": null,
"created_at":"2024-07-26T00:25:16.566471-07:00",
"updated_at":"2024-07-26T00:25:16.566492-07:00",
"categories":None
},
{
"id":"b4229775-d860-4ccb-983f-0f628ca112f5",
"memory":"Planning a trip to Japan next month. Interested in vegetarian restaurants in Tokyo.",
"user_id":"alex",
"hash":"d2088c936e259f2f5d2d75543d31401c",
"metadata":None,
"immutable": false,
"expiration_date": null,
"created_at":"2024-07-26T00:33:20.350542-07:00",
"updated_at":"2024-07-26T00:33:20.350560-07:00",
"categories":None
},
{
"id":"df1aca24-76cf-4b92-9f58-d03857efcb64",
"memory":"Planning a trip to Japan next month. Interested in vegetarian restaurants in Tokyo.",
"user_id":"alex",
"hash":"d2088c936e259f2f5d2d75543d31401c",
"metadata":None,
"immutable": false,
"expiration_date": null,
"created_at":"2024-07-26T00:51:09.642275-07:00",
"updated_at":"2024-07-26T00:51:09.642295-07:00",
"categories":None
}
... (remaining 15 memories)
]
}
```
```json Output (v1.1)
{
"count": 18,
"next": null,
"previous": null,
"results": {
"results": [
"results":
[
{
"id": "06d8df63-7bd2-4fad-9acb-60871bcecee0",
"memory": "Planning a trip to Japan next month. Interested in vegetarian restaurants in Tokyo.",
@@ -1211,7 +993,6 @@ curl -X GET "https://api.mem0.ai/v1/memories/?user_id=alex&run_id=trip-planning-
}
... (remaining 15 memories)
]
}
}
```
</CodeGroup>
View File
+1 -1
View File
@@ -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 });
const memories = await getMemories(messages, { user_id: userId, rerank: true, threshold: 0.1 });
const mem0Instructions = retrieveMemories(memories);
const result = streamText({
+7 -2
View File
@@ -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": "^0.0.14",
"@mem0/vercel-ai-provider": "^1.0.0",
"@radix-ui/react-alert-dialog": "^1.1.6",
"@radix-ui/react-avatar": "^1.1.3",
"@radix-ui/react-popover": "^1.1.6",
@@ -50,5 +50,10 @@
"tailwindcss": "^3.4.1",
"typescript": "^5"
},
"packageManager": "pnpm@10.5.2"
"packageManager": "pnpm@10.5.2",
"pnpm": {
"onlyBuiltDependencies": [
"sqlite3"
]
}
}
+2 -13
View File
@@ -1,6 +1,6 @@
{
"name": "mem0ai",
"version": "2.1.11",
"version": "2.1.13",
"description": "The Memory Layer For Your AI Apps",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
@@ -92,7 +92,6 @@
},
"dependencies": {
"axios": "1.7.7",
"neo4j-driver": "^5.28.1",
"openai": "4.28.0",
"uuid": "9.0.1",
"zod": "3.22.4"
@@ -105,22 +104,12 @@
"@types/pg": "8.11.0",
"@types/sqlite3": "3.1.11",
"groq-sdk": "0.3.0",
"neo4j-driver": "^5.28.1",
"ollama": "^0.5.14",
"pg": "8.11.3",
"redis": "4.7.0",
"sqlite3": "5.1.7"
},
"peerDependenciesMeta": {
"posthog-node": {
"optional": true
},
"posthog-js": {
"optional": true
}
},
"optionalDependencies": {
"posthog-js": "^1.116.6"
},
"engines": {
"node": ">=18"
},
-79
View File
@@ -92,10 +92,6 @@ importers:
typescript:
specifier: 5.5.4
version: 5.5.4
optionalDependencies:
posthog-js:
specifier: ^1.116.6
version: 1.224.1(@rrweb/types@2.0.0-alpha.17)
packages:
"@ampproject/remapping@2.3.0":
@@ -1052,12 +1048,6 @@ packages:
cpu: [x64]
os: [win32]
"@rrweb/types@2.0.0-alpha.17":
resolution:
{
integrity: sha512-AfDTVUuCyCaIG0lTSqYtrZqJX39ZEYzs4fYKnexhQ+id+kbZIpIJtaut5cto6dWZbB3SEe4fW0o90Po3LvTmfg==,
}
"@sevinf/maybe@0.5.0":
resolution:
{
@@ -1788,12 +1778,6 @@ packages:
integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==,
}
core-js@3.40.0:
resolution:
{
integrity: sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==,
}
create-jest@29.7.0:
resolution:
{
@@ -2125,12 +2109,6 @@ packages:
picomatch:
optional: true
fflate@0.4.8:
resolution:
{
integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==,
}
file-uri-to-path@1.0.0:
resolution:
{
@@ -3663,20 +3641,6 @@ packages:
integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==,
}
posthog-js@1.224.1:
resolution:
{
integrity: sha512-C/0adjCiqvJ9JlGdlBT7HyxqBbMB8wFwb7/DKULyXfT4GJX/8ETaqXaJuSL3HLcuUJjxYPqDinBC6mt8QoVYnA==,
}
peerDependencies:
"@rrweb/types": 2.0.0-alpha.17
preact@10.26.3:
resolution:
{
integrity: sha512-OJCfNTdttkOTCbTN+gCnXn/woDqz1dIjvP+gdCoYGP2kKuX6w79FAP8qgY/r7jgAunvqHVVmEOKzKOFWzrXZdw==,
}
prebuild-install@7.1.3:
resolution:
{
@@ -3882,12 +3846,6 @@ packages:
engines: { node: ">=18.0.0", npm: ">=8.0.0" }
hasBin: true
rrweb-snapshot@2.0.0-alpha.18:
resolution:
{
integrity: sha512-hBHZL/NfgQX6wO1D9mpwqFu1NJPpim+moIcKhFEjVTZVRUfCln+LOugRc4teVTCISYHN8Cw5e2iNTWCSm+SkoA==,
}
run-parallel@1.2.0:
resolution:
{
@@ -4518,12 +4476,6 @@ packages:
}
engines: { node: ">= 14" }
web-vitals@4.2.4:
resolution:
{
integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==,
}
webidl-conversions@3.0.1:
resolution:
{
@@ -5286,11 +5238,6 @@ snapshots:
"@rollup/rollup-win32-x64-msvc@4.37.0":
optional: true
"@rrweb/types@2.0.0-alpha.17":
dependencies:
rrweb-snapshot: 2.0.0-alpha.18
optional: true
"@sevinf/maybe@0.5.0": {}
"@sinclair/typebox@0.27.8": {}
@@ -5750,9 +5697,6 @@ snapshots:
convert-source-map@2.0.0: {}
core-js@3.40.0:
optional: true
create-jest@29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)):
dependencies:
"@jest/types": 29.6.3
@@ -5950,9 +5894,6 @@ snapshots:
optionalDependencies:
picomatch: 4.0.2
fflate@0.4.8:
optional: true
file-uri-to-path@1.0.0: {}
filelist@1.0.4:
@@ -7028,18 +6969,6 @@ snapshots:
postgres-range@1.1.4: {}
posthog-js@1.224.1(@rrweb/types@2.0.0-alpha.17):
dependencies:
"@rrweb/types": 2.0.0-alpha.17
core-js: 3.40.0
fflate: 0.4.8
preact: 10.26.3
web-vitals: 4.2.4
optional: true
preact@10.26.3:
optional: true
prebuild-install@7.1.3:
dependencies:
detect-libc: 2.0.3
@@ -7185,11 +7114,6 @@ snapshots:
"@rollup/rollup-win32-x64-msvc": 4.37.0
fsevents: 2.3.3
rrweb-snapshot@2.0.0-alpha.18:
dependencies:
postcss: 8.5.3
optional: true
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
@@ -7566,9 +7490,6 @@ snapshots:
web-streams-polyfill@4.0.0-beta.3: {}
web-vitals@4.2.4:
optional: true
webidl-conversions@3.0.1: {}
webidl-conversions@4.0.2: {}
-12
View File
@@ -1,10 +1,4 @@
import { MemoryClient } from "./mem0";
import type { TelemetryClient, TelemetryInstance } from "./telemetry.types";
import {
telemetry,
captureClientEvent,
generateHash,
} from "./telemetry.browser";
import type * as MemoryTypes from "./mem0.types";
// Re-export all types from mem0.types
@@ -27,12 +21,6 @@ export type {
Feedback,
} from "./mem0.types";
// Export telemetry types
export type { TelemetryClient, TelemetryInstance };
// Export telemetry implementation
export { telemetry, captureClientEvent, generateHash };
// Export the main client
export { MemoryClient };
export default MemoryClient;
+109 -49
View File
@@ -62,7 +62,7 @@ export default class MemoryClient {
(this.organizationName !== null && this.projectName === null)
) {
console.warn(
"Warning: Both organizationName and projectName must be provided together when using either. This will be removedfrom the version 1.0.40. Note that organizationName/projectName are being deprecated in favor of organizationId/projectId.",
"Warning: Both organizationName and projectName must be provided together when using either. This will be removed from version 1.0.40. Note that organizationName/projectName are being deprecated in favor of organizationId/projectId.",
);
}
@@ -72,7 +72,7 @@ export default class MemoryClient {
(this.organizationId !== null && this.projectId === null)
) {
console.warn(
"Warning: Both organizationId and projectId must be provided together when using either. This will be removedfrom the version 1.0.40.",
"Warning: Both organizationId and projectId must be provided together when using either. This will be removed from version 1.0.40.",
);
}
}
@@ -97,7 +97,6 @@ export default class MemoryClient {
});
this._validateApiKey();
this._validateOrgProject();
// Initialize with a temporary ID that will be updated
this.telemetryId = "";
@@ -108,59 +107,50 @@ export default class MemoryClient {
private async _initializeClient() {
try {
// do this only in browser
if (typeof window !== "undefined") {
this.telemetryId = await generateHash(this.apiKey);
await captureClientEvent("init", this);
// Generate telemetry ID
await this.ping();
if (!this.telemetryId) {
this.telemetryId = generateHash(this.apiKey);
}
// Wrap methods after initialization
this.add = this.wrapMethod("add", this.add);
this.get = this.wrapMethod("get", this.get);
this.getAll = this.wrapMethod("get_all", this.getAll);
this.search = this.wrapMethod("search", this.search);
this.delete = this.wrapMethod("delete", this.delete);
this.deleteAll = this.wrapMethod("delete_all", this.deleteAll);
this.history = this.wrapMethod("history", this.history);
this.users = this.wrapMethod("users", this.users);
this.deleteUser = this.wrapMethod("delete_user", this.deleteUser);
this.deleteUsers = this.wrapMethod("delete_users", this.deleteUsers);
this.batchUpdate = this.wrapMethod("batch_update", this.batchUpdate);
this.batchDelete = this.wrapMethod("batch_delete", this.batchDelete);
this.getProject = this.wrapMethod("get_project", this.getProject);
this.updateProject = this.wrapMethod(
"update_project",
this.updateProject,
);
this.getWebhooks = this.wrapMethod("get_webhook", this.getWebhooks);
this.createWebhook = this.wrapMethod(
"create_webhook",
this.createWebhook,
);
this.updateWebhook = this.wrapMethod(
"update_webhook",
this.updateWebhook,
);
this.deleteWebhook = this.wrapMethod(
"delete_webhook",
this.deleteWebhook,
);
} catch (error) {
this._validateOrgProject();
// Capture initialization event
captureClientEvent("init", this, {
api_version: "v1",
client_type: "MemoryClient",
}).catch((error: any) => {
console.error("Failed to capture event:", error);
});
} catch (error: any) {
console.error("Failed to initialize client:", error);
await captureClientEvent("init_error", this, {
error: error?.message || "Unknown error",
stack: error?.stack || "No stack trace",
});
}
}
wrapMethod(methodName: any, method: any) {
return async function (...args: any) {
// @ts-ignore
await captureClientEvent(methodName, this);
// @ts-ignore
return method.apply(this, args);
}.bind(this);
private _captureEvent(methodName: string, args: any[]) {
captureClientEvent(methodName, this, {
success: true,
args_count: args.length,
keys: args.length > 0 ? args[0] : [],
}).catch((error: any) => {
console.error("Failed to capture event:", error);
});
}
async _fetchWithErrorHandling(url: string, options: any): Promise<any> {
const response = await fetch(url, options);
const response = await fetch(url, {
...options,
headers: {
...options.headers,
Authorization: `Token ${this.apiKey}`,
"Mem0-User-ID": this.telemetryId,
},
});
if (!response.ok) {
const errorData = await response.text();
throw new APIError(`API request failed: ${errorData}`);
@@ -188,10 +178,31 @@ export default class MemoryClient {
);
}
async ping(): Promise<void> {
const response = await fetch(`${this.host}/v1/ping/`, {
headers: {
Authorization: `Token ${this.apiKey}`,
},
});
const data = await response.json();
if (data.status !== "ok") {
throw new Error("API Key is invalid");
}
const { org_id, project_id, user_email } = data;
this.organizationId = org_id || null;
this.projectId = project_id || null;
this.telemetryId = user_email || "";
}
async add(
messages: string | Array<Message>,
options: MemoryOptions = {},
): Promise<Array<Memory>> {
if (this.telemetryId === "") await this.ping();
this._validateOrgProject();
if (this.organizationName != null && this.projectName != null) {
options.org_name = this.organizationName;
@@ -211,6 +222,11 @@ export default class MemoryClient {
}
const payload = this._preparePayload(messages, options);
// get payload keys whose value is not null or undefined
const payloadKeys = Object.keys(payload);
this._captureEvent("add", [payloadKeys]);
const response = await this._fetchWithErrorHandling(
`${this.host}/v1/memories/`,
{
@@ -223,10 +239,15 @@ export default class MemoryClient {
}
async update(memoryId: string, message: string): Promise<Array<Memory>> {
if (this.telemetryId === "") await this.ping();
this._validateOrgProject();
const payload = {
text: message,
};
const payloadKeys = Object.keys(payload);
this._captureEvent("update", [payloadKeys]);
const response = await this._fetchWithErrorHandling(
`${this.host}/v1/memories/${memoryId}/`,
{
@@ -239,6 +260,8 @@ export default class MemoryClient {
}
async get(memoryId: string): Promise<Memory> {
if (this.telemetryId === "") await this.ping();
this._captureEvent("get", []);
return this._fetchWithErrorHandling(
`${this.host}/v1/memories/${memoryId}/`,
{
@@ -248,7 +271,10 @@ export default class MemoryClient {
}
async getAll(options?: SearchOptions): Promise<Array<Memory>> {
if (this.telemetryId === "") await this.ping();
this._validateOrgProject();
const payloadKeys = Object.keys(options || {});
this._captureEvent("get_all", [payloadKeys]);
const { api_version, page, page_size, ...otherOptions } = options!;
if (this.organizationName != null && this.projectName != null) {
otherOptions.org_name = this.organizationName;
@@ -293,7 +319,10 @@ export default class MemoryClient {
}
async search(query: string, options?: SearchOptions): Promise<Array<Memory>> {
if (this.telemetryId === "") await this.ping();
this._validateOrgProject();
const payloadKeys = Object.keys(options || {});
this._captureEvent("search", [payloadKeys]);
const { api_version, ...otherOptions } = options!;
const payload = { query, ...otherOptions };
if (this.organizationName != null && this.projectName != null) {
@@ -322,6 +351,8 @@ export default class MemoryClient {
}
async delete(memoryId: string): Promise<{ message: string }> {
if (this.telemetryId === "") await this.ping();
this._captureEvent("delete", []);
return this._fetchWithErrorHandling(
`${this.host}/v1/memories/${memoryId}/`,
{
@@ -332,7 +363,10 @@ export default class MemoryClient {
}
async deleteAll(options: MemoryOptions = {}): Promise<{ message: string }> {
if (this.telemetryId === "") await this.ping();
this._validateOrgProject();
const payloadKeys = Object.keys(options || {});
this._captureEvent("delete_all", [payloadKeys]);
if (this.organizationName != null && this.projectName != null) {
options.org_name = this.organizationName;
options.project_name = this.projectName;
@@ -358,6 +392,8 @@ export default class MemoryClient {
}
async history(memoryId: string): Promise<Array<MemoryHistory>> {
if (this.telemetryId === "") await this.ping();
this._captureEvent("history", []);
const response = await this._fetchWithErrorHandling(
`${this.host}/v1/memories/${memoryId}/history/`,
{
@@ -368,7 +404,9 @@ export default class MemoryClient {
}
async users(): Promise<AllUsers> {
if (this.telemetryId === "") await this.ping();
this._validateOrgProject();
this._captureEvent("users", []);
const options: MemoryOptions = {};
if (this.organizationName != null && this.projectName != null) {
options.org_name = this.organizationName;
@@ -397,6 +435,8 @@ export default class MemoryClient {
entityId: string,
entity: { type: string } = { type: "user" },
): Promise<{ message: string }> {
if (this.telemetryId === "") await this.ping();
this._captureEvent("delete_user", []);
const response = await this._fetchWithErrorHandling(
`${this.host}/v1/entities/${entity.type}/${entityId}/`,
{
@@ -408,7 +448,9 @@ export default class MemoryClient {
}
async deleteUsers(): 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) {
@@ -433,6 +475,8 @@ export default class MemoryClient {
}
async batchUpdate(memories: Array<MemoryUpdateBody>): Promise<string> {
if (this.telemetryId === "") await this.ping();
this._captureEvent("batch_update", []);
const memoriesBody = memories.map((memory) => ({
memory_id: memory.memoryId,
text: memory.text,
@@ -449,6 +493,8 @@ export default class MemoryClient {
}
async batchDelete(memories: Array<string>): Promise<string> {
if (this.telemetryId === "") await this.ping();
this._captureEvent("batch_delete", []);
const memoriesBody = memories.map((memory) => ({
memory_id: memory,
}));
@@ -464,8 +510,10 @@ export default class MemoryClient {
}
async getProject(options: ProjectOptions): Promise<ProjectResponse> {
if (this.telemetryId === "") await this.ping();
this._validateOrgProject();
const payloadKeys = Object.keys(options || {});
this._captureEvent("get_project", [payloadKeys]);
const { fields } = options;
if (!(this.organizationId && this.projectId)) {
@@ -489,8 +537,9 @@ export default class MemoryClient {
async updateProject(
prompts: PromptUpdatePayload,
): Promise<Record<string, any>> {
if (this.telemetryId === "") await this.ping();
this._validateOrgProject();
this._captureEvent("update_project", []);
if (!(this.organizationId && this.projectId)) {
throw new Error(
"organizationId and projectId must be set to update instructions or categories",
@@ -510,6 +559,8 @@ export default class MemoryClient {
// WebHooks
async getWebhooks(data?: { projectId?: string }): Promise<Array<Webhook>> {
if (this.telemetryId === "") await this.ping();
this._captureEvent("get_webhooks", []);
const project_id = data?.projectId || this.projectId;
const response = await this._fetchWithErrorHandling(
`${this.host}/api/v1/webhooks/projects/${project_id}/`,
@@ -521,6 +572,8 @@ export default class MemoryClient {
}
async createWebhook(webhook: WebhookPayload): Promise<Webhook> {
if (this.telemetryId === "") await this.ping();
this._captureEvent("create_webhook", []);
const response = await this._fetchWithErrorHandling(
`${this.host}/api/v1/webhooks/projects/${this.projectId}/`,
{
@@ -533,6 +586,8 @@ export default class MemoryClient {
}
async updateWebhook(webhook: WebhookPayload): Promise<{ message: string }> {
if (this.telemetryId === "") await this.ping();
this._captureEvent("update_webhook", []);
const project_id = webhook.projectId || this.projectId;
const response = await this._fetchWithErrorHandling(
`${this.host}/api/v1/webhooks/${webhook.webhookId}/`,
@@ -551,6 +606,8 @@ export default class MemoryClient {
async deleteWebhook(data: {
webhookId: string;
}): Promise<{ message: string }> {
if (this.telemetryId === "") await this.ping();
this._captureEvent("delete_webhook", []);
const webhook_id = data.webhookId || data;
const response = await this._fetchWithErrorHandling(
`${this.host}/api/v1/webhooks/${webhook_id}/`,
@@ -563,6 +620,9 @@ export default class MemoryClient {
}
async feedback(data: FeedbackPayload): Promise<{ message: string }> {
if (this.telemetryId === "") await this.ping();
const payloadKeys = Object.keys(data || {});
this._captureEvent("feedback", [payloadKeys]);
const response = await this._fetchWithErrorHandling(
`${this.host}/v1/feedback/`,
{
-85
View File
@@ -1,85 +0,0 @@
// @ts-nocheck
import type { PostHog } from "posthog-js";
import type { TelemetryClient } from "./telemetry.types";
let version = "1.0.20";
const MEM0_TELEMETRY = "false";
const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX";
const POSTHOG_HOST = "https://us.i.posthog.com";
// Browser-specific hash function using Web Crypto API
async function generateHash(input: string): Promise<string> {
const msgBuffer = new TextEncoder().encode(input);
const hashBuffer = await window.crypto.subtle.digest("SHA-256", msgBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
}
class BrowserTelemetry implements TelemetryClient {
client: PostHog | null = null;
constructor(projectApiKey: string, host: string) {
if (MEM0_TELEMETRY) {
this.initializeClient(projectApiKey, host);
}
}
private async initializeClient(projectApiKey: string, host: string) {
try {
const posthog = await import("posthog-js").catch(() => null);
if (posthog) {
posthog.init(projectApiKey, { api_host: host });
this.client = posthog;
}
} catch (error) {
// Silently fail if posthog-js is not available
this.client = null;
}
}
async captureEvent(distinctId: string, eventName: string, properties = {}) {
if (!this.client || !MEM0_TELEMETRY) return;
const eventProperties = {
client_source: "browser",
client_version: getVersion(),
browser: window.navigator.userAgent,
...properties,
};
try {
this.client.capture(eventName, eventProperties);
} catch (error) {
// Silently fail if telemetry fails
}
}
async shutdown() {
// No shutdown needed for browser client
}
}
function getVersion() {
return version;
}
const telemetry = new BrowserTelemetry(POSTHOG_API_KEY, POSTHOG_HOST);
async function captureClientEvent(
eventName: string,
instance: any,
additionalData = {},
) {
const eventData = {
function: `${instance.constructor.name}`,
...additionalData,
};
await telemetry.captureEvent(
instance.telemetryId,
`client.${eventName}`,
eventData,
);
}
export { telemetry, captureClientEvent, generateHash };
-107
View File
@@ -1,107 +0,0 @@
// @ts-nocheck
import type { TelemetryClient } from "./telemetry.types";
let version = "1.0.20";
const MEM0_TELEMETRY = process.env.MEM0_TELEMETRY !== "false";
const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX";
const POSTHOG_HOST = "https://us.i.posthog.com";
// Node-specific hash function using crypto module
function generateHash(input: string): string {
const crypto = require("crypto");
return crypto.createHash("sha256").update(input).digest("hex");
}
class NodeTelemetry implements TelemetryClient {
client: any = null;
constructor(projectApiKey: string, host: string) {
if (MEM0_TELEMETRY) {
this.initializeClient(projectApiKey, host);
}
}
private async initializeClient(projectApiKey: string, host: string) {
try {
const { PostHog } = await import("posthog-node").catch(() => ({
PostHog: null,
}));
if (PostHog) {
this.client = new PostHog(projectApiKey, { host, flushAt: 1 });
}
} catch (error) {
// Silently fail if posthog-node is not available
this.client = null;
}
}
async captureEvent(distinctId: string, eventName: string, properties = {}) {
if (!this.client || !MEM0_TELEMETRY) return;
const eventProperties = {
client_source: "nodejs",
client_version: getVersion(),
...this.getEnvironmentInfo(),
...properties,
};
try {
this.client.capture({
distinctId,
event: eventName,
properties: eventProperties,
});
} catch (error) {
// Silently fail if telemetry fails
}
}
private getEnvironmentInfo() {
try {
const os = require("os");
return {
node_version: process.version,
os: process.platform,
os_version: os.release(),
os_arch: os.arch(),
};
} catch (error) {
return {};
}
}
async shutdown() {
if (this.client) {
try {
return this.client.shutdown();
} catch (error) {
// Silently fail shutdown
}
}
}
}
function getVersion() {
return version;
}
const telemetry = new NodeTelemetry(POSTHOG_API_KEY, POSTHOG_HOST);
async function captureClientEvent(
eventName: string,
instance: any,
additionalData = {},
) {
const eventData = {
function: `${instance.constructor.name}`,
...additionalData,
};
await telemetry.captureEvent(
instance.telemetryId,
`client.${eventName}`,
eventData,
);
}
export { telemetry, captureClientEvent, generateHash };
+96 -2
View File
@@ -1,3 +1,97 @@
// @ts-nocheck
// Re-export browser telemetry by default
export * from "./telemetry.browser";
import type { TelemetryClient, TelemetryOptions } from "./telemetry.types";
let version = "2.1.12";
// Safely check for process.env in different environments
const MEM0_TELEMETRY = process?.env?.MEM0_TELEMETRY === "false" ? false : true;
const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX";
const POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/";
// Simple hash function using random strings
function generateHash(input: string): string {
const randomStr =
Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15);
return randomStr;
}
class UnifiedTelemetry implements TelemetryClient {
private apiKey: string;
private host: string;
constructor(projectApiKey: string, host: string) {
this.apiKey = projectApiKey;
this.host = host;
}
async captureEvent(distinctId: string, eventName: string, properties = {}) {
if (!MEM0_TELEMETRY) return;
const eventProperties = {
client_version: version,
timestamp: new Date().toISOString(),
...properties,
$process_person_profile: false,
$lib: "posthog-node",
};
const payload = {
api_key: this.apiKey,
distinct_id: distinctId,
event: eventName,
properties: eventProperties,
};
try {
const response = await fetch(this.host, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!response.ok) {
console.error("Telemetry event capture failed:", await response.text());
}
} catch (error) {
console.error("Telemetry event capture failed:", error);
}
}
async shutdown() {
// No shutdown needed for direct API calls
}
}
const telemetry = new UnifiedTelemetry(POSTHOG_API_KEY, POSTHOG_HOST);
async function captureClientEvent(
eventName: string,
instance: any,
additionalData = {},
) {
if (!instance.telemetryId) {
console.warn("No telemetry ID found for instance");
return;
}
const eventData = {
function: `${instance.constructor.name}`,
method: eventName,
api_host: instance.host,
timestamp: new Date().toISOString(),
client_version: version,
keys: additionalData?.keys || [],
...additionalData,
};
await telemetry.captureEvent(
instance.telemetryId,
`client.${eventName}`,
eventData,
);
}
export { telemetry, captureClientEvent, generateHash };
+19
View File
@@ -12,4 +12,23 @@ export interface TelemetryInstance {
constructor: {
name: string;
};
host?: string;
apiKey?: string;
}
export interface TelemetryEventData {
function: string;
method: string;
api_host?: string;
timestamp?: string;
client_source: "browser" | "nodejs";
client_version: string;
[key: string]: any;
}
export interface TelemetryOptions {
enabled?: boolean;
apiKey?: string;
host?: string;
version?: string;
}
+1
View File
@@ -14,6 +14,7 @@
},
"dependencies": {
"@anthropic-ai/sdk": "^0.18.0",
"@google/genai": "^0.7.0",
"@qdrant/js-client-rest": "^1.13.0",
"@types/node": "^20.11.19",
"@types/pg": "^8.11.0",
+31
View File
@@ -0,0 +1,31 @@
import { GoogleGenAI } from "@google/genai";
import { Embedder } from "./base";
import { EmbeddingConfig } from "../types";
export class GoogleEmbedder implements Embedder {
private google: GoogleGenAI;
private model: string;
constructor(config: EmbeddingConfig) {
this.google = new GoogleGenAI({ apiKey: config.apiKey });
this.model = config.model || "text-embedding-004";
}
async embed(text: string): Promise<number[]> {
const response = await this.google.models.embedContent({
model: this.model,
contents: text,
config: { outputDimensionality: 768 },
});
return response.embeddings![0].values!;
}
async embedBatch(texts: string[]): Promise<number[][]> {
const response = await this.google.models.embedContent({
model: this.model,
contents: texts,
config: { outputDimensionality: 768 },
});
return response.embeddings!.map((item) => item.values!);
}
}
+2
View File
@@ -4,8 +4,10 @@ export * from "./types";
export * from "./embeddings/base";
export * from "./embeddings/openai";
export * from "./embeddings/ollama";
export * from "./embeddings/google";
export * from "./llms/base";
export * from "./llms/openai";
export * from "./llms/google";
export * from "./llms/openai_structured";
export * from "./llms/anthropic";
export * from "./llms/groq";
+54
View File
@@ -0,0 +1,54 @@
import { GoogleGenAI } from "@google/genai";
import { LLM, LLMResponse } from "./base";
import { LLMConfig, Message } from "../types";
export class GoogleLLM implements LLM {
private google: GoogleGenAI;
private model: string;
constructor(config: LLMConfig) {
this.google = new GoogleGenAI({ apiKey: config.apiKey });
this.model = config.model || "gemini-2.0-flash";
}
async generateResponse(
messages: Message[],
responseFormat?: { type: string },
tools?: any[],
): Promise<string | LLMResponse> {
const completion = await this.google.models.generateContent({
contents: messages.map((msg) => ({
parts: [
{
text:
typeof msg.content === "string"
? msg.content
: JSON.stringify(msg.content),
},
],
role: msg.role === "system" ? "model" : "user",
})),
model: this.model,
// config: {
// responseSchema: {}, // Add response schema if needed
// },
});
const text = completion.text?.replace(/^```json\n/, "").replace(/\n```$/, "");
return text || "";
}
async generateChat(messages: Message[]): Promise<LLMResponse> {
const completion = await this.google.models.generateContent({
contents: messages,
model: this.model,
});
const response = completion.candidates![0].content;
return {
content: response!.parts![0].text || "",
role: response!.role!,
};
}
}
+22 -1
View File
@@ -112,7 +112,7 @@ export class Memory {
runId,
metadata = {},
filters = {},
prompt,
infer = true,
} = config;
if (userId) filters.userId = metadata.userId = userId;
@@ -136,6 +136,7 @@ export class Memory {
final_parsedMessages,
metadata,
filters,
infer,
);
// Add to graph store if available
@@ -161,7 +162,27 @@ export class Memory {
messages: Message[],
metadata: Record<string, any>,
filters: SearchFilters,
infer: boolean,
): Promise<MemoryItem[]> {
if (!infer) {
const returnedMemories: MemoryItem[] = [];
for (const message of messages) {
if (message.content === "system") {
continue;
}
const memoryId = await this.createMemory(
message.content as string,
{},
metadata,
);
returnedMemories.push({
id: memoryId,
memory: message.content as string,
metadata: { event: "ADD" },
});
}
return returnedMemories;
}
const parsedMessages = messages.map((m) => m.content).join("\n");
// Get prompts
+1 -1
View File
@@ -10,7 +10,7 @@ export interface Entity {
export interface AddMemoryOptions extends Entity {
metadata?: Record<string, any>;
filters?: SearchFilters;
prompt?: string;
infer?: boolean;
}
export interface SearchMemoryOptions extends Entity {
+6
View File
@@ -22,6 +22,8 @@ import { SQLiteManager } from "../storage/SQLiteManager";
import { MemoryHistoryManager } from "../storage/MemoryHistoryManager";
import { SupabaseHistoryManager } from "../storage/SupabaseHistoryManager";
import { HistoryManager } from "../storage/base";
import { GoogleEmbedder } from "../embeddings/google";
import { GoogleLLM } from "../llms/google";
export class EmbedderFactory {
static create(provider: string, config: EmbeddingConfig): Embedder {
@@ -30,6 +32,8 @@ export class EmbedderFactory {
return new OpenAIEmbedder(config);
case "ollama":
return new OllamaEmbedder(config);
case "google":
return new GoogleEmbedder(config);
default:
throw new Error(`Unsupported embedder provider: ${provider}`);
}
@@ -49,6 +53,8 @@ export class LLMFactory {
return new GroqLLM(config);
case "ollama":
return new OllamaLLM(config);
case "google":
return new GoogleLLM(config);
default:
throw new Error(`Unsupported LLM provider: ${provider}`);
}
+1 -1
View File
@@ -2,5 +2,5 @@ import importlib.metadata
__version__ = importlib.metadata.version("mem0ai")
from mem0.client.main import MemoryClient, AsyncMemoryClient # noqa
from mem0.client.main import AsyncMemoryClient, MemoryClient # noqa
from mem0.memory.main import Memory # noqa
+3 -2
View File
@@ -130,13 +130,14 @@ class MemoryClient:
"""
kwargs = self._prepare_params(kwargs)
if kwargs.get("output_format") != "v1.1":
kwargs["output_format"] = "v1.1"
warnings.warn(
"Using default output format 'v1.0' is deprecated and will be removed in version 0.1.70. "
"Please use output_format='v1.1' for enhanced memory details. "
"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 = self.client.post("/v1/memories/", json=payload)
response.raise_for_status()
+7
View File
@@ -0,0 +1,7 @@
from enum import Enum
class MemoryType(Enum):
SEMANTIC = "semantic_memory"
EPISODIC = "episodic_memory"
PROCEDURAL = "procedural_memory"
+1 -1
View File
@@ -13,7 +13,7 @@ class BaseLlmConfig(ABC):
def __init__(
self,
model: Optional[str] = None,
model: Optional[Union[str, Dict]] = None,
temperature: float = 0.1,
api_key: Optional[str] = None,
max_tokens: int = 2000,
+83 -4
View File
@@ -208,6 +208,85 @@ Please note to return the IDs in the output from the input IDs only and do not g
}
"""
PROCEDURAL_MEMORY_SYSTEM_PROMPT = """
You are a memory summarization system that records and preserves the complete interaction history between a human and an AI agent. You are provided with the agent’s execution history over the past N steps. Your task is to produce a comprehensive summary of the agent's output history that contains every detail necessary for the agent to continue the task without ambiguity. **Every output produced by the agent must be recorded verbatim as part of the summary.**
### Overall Structure:
- **Overview (Global Metadata):**
- **Task Objective**: The overall goal the agent is working to accomplish.
- **Progress Status**: The current completion percentage and summary of specific milestones or steps completed.
- **Sequential Agent Actions (Numbered Steps):**
Each numbered step must be a self-contained entry that includes all of the following elements:
1. **Agent Action**:
- Precisely describe what the agent did (e.g., "Clicked on the 'Blog' link", "Called API to fetch content", "Scraped page data").
- Include all parameters, target elements, or methods involved.
2. **Action Result (Mandatory, Unmodified)**:
- Immediately follow the agent action with its exact, unaltered output.
- Record all returned data, responses, HTML snippets, JSON content, or error messages exactly as received. This is critical for constructing the final output later.
3. **Embedded Metadata**:
For the same numbered step, include additional context such as:
- **Key Findings**: Any important information discovered (e.g., URLs, data points, search results).
- **Navigation History**: For browser agents, detail which pages were visited, including their URLs and relevance.
- **Errors & Challenges**: Document any error messages, exceptions, or challenges encountered along with any attempted recovery or troubleshooting.
- **Current Context**: Describe the state after the action (e.g., "Agent is on the blog detail page" or "JSON data stored for further processing") and what the agent plans to do next.
### Guidelines:
1. **Preserve Every Output**: The exact output of each agent action is essential. Do not paraphrase or summarize the output. It must be stored as is for later use.
2. **Chronological Order**: Number the agent actions sequentially in the order they occurred. Each numbered step is a complete record of that action.
3. **Detail and Precision**:
- Use exact data: Include URLs, element indexes, error messages, JSON responses, and any other concrete values.
- Preserve numeric counts and metrics (e.g., "3 out of 5 items processed").
- For any errors, include the full error message and, if applicable, the stack trace or cause.
4. **Output Only the Summary**: The final output must consist solely of the structured summary with no additional commentary or preamble.
### Example Template:
```
## Summary of the agent's execution history
**Task Objective**: Scrape blog post titles and full content from the OpenAI blog.
**Progress Status**: 10% complete — 5 out of 50 blog posts processed.
1. **Agent Action**: Opened URL "https://openai.com"
**Action Result**:
"HTML Content of the homepage including navigation bar with links: 'Blog', 'API', 'ChatGPT', etc."
**Key Findings**: Navigation bar loaded correctly.
**Navigation History**: Visited homepage: "https://openai.com"
**Current Context**: Homepage loaded; ready to click on the 'Blog' link.
2. **Agent Action**: Clicked on the "Blog" link in the navigation bar.
**Action Result**:
"Navigated to 'https://openai.com/blog/' with the blog listing fully rendered."
**Key Findings**: Blog listing shows 10 blog previews.
**Navigation History**: Transitioned from homepage to blog listing page.
**Current Context**: Blog listing page displayed.
3. **Agent Action**: Extracted the first 5 blog post links from the blog listing page.
**Action Result**:
"[ '/blog/chatgpt-updates', '/blog/ai-and-education', '/blog/openai-api-announcement', '/blog/gpt-4-release', '/blog/safety-and-alignment' ]"
**Key Findings**: Identified 5 valid blog post URLs.
**Current Context**: URLs stored in memory for further processing.
4. **Agent Action**: Visited URL "https://openai.com/blog/chatgpt-updates"
**Action Result**:
"HTML content loaded for the blog post including full article text."
**Key Findings**: Extracted blog title "ChatGPT Updates – March 2025" and article content excerpt.
**Current Context**: Blog post content extracted and stored.
5. **Agent Action**: Extracted blog title and full article content from "https://openai.com/blog/chatgpt-updates"
**Action Result**:
"{ 'title': 'ChatGPT Updates – March 2025', 'content': 'We\'re introducing new updates to ChatGPT, including improved browsing capabilities and memory recall... (full content)' }"
**Key Findings**: Full content captured for later summarization.
**Current Context**: Data stored; ready to proceed to next blog post.
... (Additional numbered steps for subsequent actions)
```
"""
def get_update_memory_messages(retrieved_old_memory_dict, response_content, custom_update_memory_prompt=None):
if custom_update_memory_prompt is None:
@@ -215,7 +294,7 @@ def get_update_memory_messages(retrieved_old_memory_dict, response_content, cust
custom_update_memory_prompt = DEFAULT_UPDATE_MEMORY_PROMPT
return f"""{custom_update_memory_prompt}
Below is the current content of my memory which I have collected till now. You have to update it in the following format only:
```
@@ -227,9 +306,9 @@ def get_update_memory_messages(retrieved_old_memory_dict, response_content, cust
```
{response_content}
```
You must return your response in the following JSON structure only:
{{
"memory" : [
{{
@@ -241,7 +320,7 @@ def get_update_memory_messages(retrieved_old_memory_dict, response_content, cust
...
]
}}
Follow the instruction mentioned below:
- Do not return anything from the custom few shot prompts provided above.
- If the current memory is empty, then you have to add the new retrieved facts to the memory.
@@ -1,4 +1,5 @@
from typing import Any, Dict, Optional
from pydantic import BaseModel, Field, model_validator
+39
View File
@@ -0,0 +1,39 @@
from typing import Any, Dict, Optional
from pydantic import BaseModel, Field, model_validator
class FAISSConfig(BaseModel):
collection_name: str = Field("mem0", description="Default name for the collection")
path: Optional[str] = Field(None, description="Path to store FAISS index and metadata")
distance_strategy: str = Field(
"euclidean", description="Distance strategy to use. Options: 'euclidean', 'inner_product', 'cosine'"
)
normalize_L2: bool = Field(
False, description="Whether to normalize L2 vectors (only applicable for euclidean distance)"
)
embedding_model_dims: int = Field(1536, description="Dimension of the embedding vector")
@model_validator(mode="before")
@classmethod
def validate_distance_strategy(cls, values: Dict[str, Any]) -> Dict[str, Any]:
distance_strategy = values.get("distance_strategy")
if distance_strategy and distance_strategy not in ["euclidean", "inner_product", "cosine"]:
raise ValueError("Invalid distance_strategy. Must be one of: 'euclidean', 'inner_product', 'cosine'")
return values
@model_validator(mode="before")
@classmethod
def validate_extra_fields(cls, values: Dict[str, Any]) -> Dict[str, Any]:
allowed_fields = set(cls.model_fields.keys())
input_fields = set(values.keys())
extra_fields = input_fields - allowed_fields
if extra_fields:
raise ValueError(
f"Extra fields not allowed: {', '.join(extra_fields)}. Please input only the following fields: {', '.join(allowed_fields)}"
)
return values
model_config = {
"arbitrary_types_allowed": True,
}
+1 -1
View File
@@ -1,5 +1,5 @@
from typing import Any, Dict, Optional
from enum import Enum
from typing import Any, Dict, Optional
from pydantic import BaseModel, Field, model_validator
+1
View File
@@ -1,4 +1,5 @@
from typing import Any, ClassVar, Dict, Optional
from pydantic import BaseModel, Field, model_validator
+1
View File
@@ -22,6 +22,7 @@ class EmbedderConfig(BaseModel):
"vertexai",
"together",
"lmstudio",
"langchain",
]:
return v
else:
+36
View File
@@ -0,0 +1,36 @@
import os
from typing import Literal, Optional
from mem0.configs.embeddings.base import BaseEmbedderConfig
from mem0.embeddings.base import EmbeddingBase
try:
from langchain.embeddings.base import Embeddings
except ImportError:
raise ImportError("langchain is not installed. Please install it using `pip install langchain`")
class LangchainEmbedding(EmbeddingBase):
def __init__(self, config: Optional[BaseEmbedderConfig] = None):
super().__init__(config)
if self.config.model is None:
raise ValueError("`model` parameter is required")
if not isinstance(self.config.model, Embeddings):
raise ValueError("`model` must be an instance of Embeddings")
self.langchain_model = self.config.model
def embed(self, text, memory_action: Optional[Literal["add", "search", "update"]] = None):
"""
Get the embedding for the given text using Langchain.
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.langchain_model.embed_query(text)
+1
View File
@@ -25,6 +25,7 @@ class LlmConfig(BaseModel):
"deepseek",
"xai",
"lmstudio",
"langchain",
):
return v
else:
+65
View File
@@ -0,0 +1,65 @@
from typing import Dict, List, Optional
from mem0.configs.llms.base import BaseLlmConfig
from mem0.llms.base import LLMBase
try:
from langchain.chat_models.base import BaseChatModel
except ImportError:
raise ImportError("langchain is not installed. Please install it using `pip install langchain`")
class LangchainLLM(LLMBase):
def __init__(self, config: Optional[BaseLlmConfig] = None):
super().__init__(config)
if self.config.model is None:
raise ValueError("`model` parameter is required")
if not isinstance(self.config.model, BaseChatModel):
raise ValueError("`model` must be an instance of BaseChatModel")
self.langchain_model = self.config.model
def generate_response(
self,
messages: List[Dict[str, str]],
response_format=None,
tools: Optional[List[Dict]] = None,
tool_choice: str = "auto",
):
"""
Generate a response based on the given messages using langchain_community.
Args:
messages (list): List of message dicts containing 'role' and 'content'.
response_format (str or object, optional): Format of the response. Not used in Langchain.
tools (list, optional): List of tools that the model can call. Not used in Langchain.
tool_choice (str, optional): Tool choice method. Not used in Langchain.
Returns:
str: The generated response.
"""
try:
# Convert the messages to LangChain's tuple format
langchain_messages = []
for message in messages:
role = message["role"]
content = message["content"]
if role == "system":
langchain_messages.append(("system", content))
elif role == "user":
langchain_messages.append(("human", content))
elif role == "assistant":
langchain_messages.append(("ai", content))
if not langchain_messages:
raise ValueError("No valid messages found in the messages list")
ai_message = self.langchain_model.invoke(langchain_messages)
return ai_message.content
except Exception as e:
raise Exception(f"Error generating response using langchain model: {str(e)}")
+11 -26
View File
@@ -167,7 +167,9 @@ class MemoryGraph:
for item in search_results["tool_calls"][0]["arguments"]["entities"]:
entity_type_map[item["entity"]] = item["entity_type"]
except Exception as e:
logger.error(f"Error in search tool: {e}")
logger.exception(
f"Error in search tool: {e}, llm_provider={self.llm_provider}, search_results={search_results}"
)
entity_type_map = {k.lower().replace(" ", "_"): v.lower().replace(" ", "_") for k, v in entity_type_map.items()}
logger.debug(f"Entity type map: {entity_type_map}")
@@ -203,14 +205,13 @@ class MemoryGraph:
tools=_tools,
)
entities = []
if extracted_entities["tool_calls"]:
extracted_entities = extracted_entities["tool_calls"][0]["arguments"]["entities"]
else:
extracted_entities = []
entities = extracted_entities["tool_calls"][0]["arguments"]["entities"]
extracted_entities = self._remove_spaces_from_entities(extracted_entities)
logger.debug(f"Extracted entities: {extracted_entities}")
return extracted_entities
entities = self._remove_spaces_from_entities(entities)
logger.debug(f"Extracted entities: {entities}")
return entities
def _search_graph_db(self, node_list, filters, limit=100):
"""Search similar nodes among and their respective incoming and outgoing relations."""
@@ -347,14 +348,9 @@ class MemoryGraph:
params = {
"source_id": source_node_search_result[0]["elementId(source_candidate)"],
"destination_name": destination,
"relationship": relationship,
"destination_type": destination_type,
"destination_embedding": dest_embedding,
"user_id": user_id,
}
resp = self.graph.query(cypher, params=params)
results.append(resp)
elif destination_node_search_result and not source_node_search_result:
cypher = f"""
MATCH (destination)
@@ -372,14 +368,9 @@ class MemoryGraph:
params = {
"destination_id": destination_node_search_result[0]["elementId(destination_candidate)"],
"source_name": source,
"relationship": relationship,
"source_type": source_type,
"source_embedding": source_embedding,
"user_id": user_id,
}
resp = self.graph.query(cypher, params=params)
results.append(resp)
elif source_node_search_result and destination_node_search_result:
cypher = f"""
MATCH (source)
@@ -396,12 +387,8 @@ class MemoryGraph:
"source_id": source_node_search_result[0]["elementId(source_candidate)"],
"destination_id": destination_node_search_result[0]["elementId(destination_candidate)"],
"user_id": user_id,
"relationship": relationship,
}
resp = self.graph.query(cypher, params=params)
results.append(resp)
elif not source_node_search_result and not destination_node_search_result:
else:
cypher = f"""
MERGE (n:{source_type} {{name: $source_name, user_id: $user_id}})
ON CREATE SET n.created = timestamp(), n.embedding = $source_embedding
@@ -415,15 +402,13 @@ class MemoryGraph:
"""
params = {
"source_name": source,
"source_type": source_type,
"dest_name": destination,
"destination_type": destination_type,
"source_embedding": source_embedding,
"dest_embedding": dest_embedding,
"user_id": user_id,
}
resp = self.graph.query(cypher, params=params)
results.append(resp)
result = self.graph.query(cypher, params=params)
results.append(result)
return results
def _remove_spaces_from_entities(self, entity_list):
+63 -11
View File
@@ -11,17 +11,13 @@ import pytz
from pydantic import ValidationError
from mem0.configs.base import MemoryConfig, MemoryItem
from mem0.configs.prompts import get_update_memory_messages
from mem0.configs.enums import MemoryType
from mem0.configs.prompts import PROCEDURAL_MEMORY_SYSTEM_PROMPT, get_update_memory_messages
from mem0.memory.base import MemoryBase
from mem0.memory.setup import setup_config
from mem0.memory.storage import SQLiteManager
from mem0.memory.telemetry import capture_event
from mem0.memory.utils import (
get_fact_retrieval_messages,
parse_messages,
parse_vision_messages,
remove_code_blocks,
)
from mem0.memory.utils import get_fact_retrieval_messages, parse_messages, parse_vision_messages, remove_code_blocks
from mem0.utils.factory import EmbedderFactory, LlmFactory, VectorStoreFactory
# Setup user config
@@ -89,6 +85,7 @@ class Memory(MemoryBase):
metadata=None,
filters=None,
infer=True,
memory_type=None,
prompt=None,
):
"""
@@ -102,8 +99,8 @@ class Memory(MemoryBase):
metadata (dict, optional): Metadata to store with the memory. Defaults to None.
filters (dict, optional): Filters to apply to the search. Defaults to None.
infer (bool, optional): Whether to infer the memories. Defaults to True.
prompt (str, optional): Prompt to use for memory deduction. Defaults to None.
memory_type (str, optional): Type of memory to create. Defaults to None. By default, it creates the short term memories and long term (semantic and episodic) memories. Pass "procedural_memory" to create procedural memories.
prompt (str, optional): Prompt to use for the memory creation. Defaults to None.
Returns:
dict: A dictionary containing the result of the memory addition operation.
result: dict of affected events with each dict has the following key:
@@ -131,9 +128,18 @@ class Memory(MemoryBase):
if not any(key in filters for key in ("user_id", "agent_id", "run_id")):
raise ValueError("One of the filters: user_id, agent_id or run_id is required!")
if memory_type is not None and memory_type != MemoryType.PROCEDURAL.value:
raise ValueError(
f"Invalid 'memory_type'. Please pass {MemoryType.PROCEDURAL.value} to create procedural memories."
)
if isinstance(messages, str):
messages = [{"role": "user", "content": messages}]
if agent_id is not None and memory_type == MemoryType.PROCEDURAL.value:
results = self._create_procedural_memory(messages, metadata=metadata, prompt=prompt)
return results
if self.config.llm.config.get("enable_vision"):
messages = parse_vision_messages(messages, self.llm, self.config.llm.config.get("vision_details"))
else:
@@ -595,11 +601,11 @@ class Memory(MemoryBase):
return self.db.get_history(memory_id)
def _create_memory(self, data, existing_embeddings, metadata=None):
logging.info(f"Creating memory with {data=}")
logging.debug(f"Creating memory with {data=}")
if data in existing_embeddings:
embeddings = existing_embeddings[data]
else:
embeddings = self.embedding_model.embed(data, "add")
embeddings = self.embedding_model.embed(data, memory_action="add")
memory_id = str(uuid.uuid4())
metadata = metadata or {}
metadata["data"] = data
@@ -615,6 +621,52 @@ class Memory(MemoryBase):
capture_event("mem0._create_memory", self, {"memory_id": memory_id})
return memory_id
def _create_procedural_memory(self, messages, metadata=None, prompt=None):
"""
Create a procedural memory
Args:
messages (list): List of messages to create a procedural memory from.
metadata (dict): Metadata to create a procedural memory from.
prompt (str, optional): Prompt to use for the procedural memory creation. Defaults to None.
"""
try:
from langchain_core.messages.utils import convert_to_messages # type: ignore
except Exception:
logger.error(
"Import error while loading langchain-core. Please install 'langchain-core' to use procedural memory."
)
raise
logger.info("Creating procedural memory")
parsed_messages = [
{"role": "system", "content": prompt or PROCEDURAL_MEMORY_SYSTEM_PROMPT},
*messages,
{"role": "user", "content": "Create procedural memory of the above conversation."},
]
try:
procedural_memory = self.llm.generate_response(messages=parsed_messages)
except Exception as e:
logger.error(f"Error generating procedural memory summary: {e}")
raise
if metadata is None:
raise ValueError("Metadata cannot be done for procedural memory.")
metadata["memory_type"] = MemoryType.PROCEDURAL.value
# Generate embeddings for the summary
embeddings = self.embedding_model.embed(procedural_memory, memory_action="add")
# Create the memory
memory_id = self._create_memory(procedural_memory, {procedural_memory: embeddings}, metadata=metadata)
capture_event("mem0._create_procedural_memory", self, {"memory_id": memory_id})
# Return results in the same format as add()
result = {"results": [{"id": memory_id, "memory": procedural_memory, "event": "ADD"}]}
return result
def _update_memory(self, memory_id, data, existing_embeddings, metadata=None):
logger.info(f"Updating memory with {data=}")
+1 -1
View File
@@ -1,6 +1,6 @@
import sqlite3
import uuid
import threading
import uuid
class SQLiteManager:
+3
View File
@@ -26,6 +26,7 @@ class LlmFactory:
"deepseek": "mem0.llms.deepseek.DeepSeekLLM",
"xai": "mem0.llms.xai.XAILLM",
"lmstudio": "mem0.llms.lmstudio.LMStudioLLM",
"langchain": "mem0.llms.langchain.LangchainLLM",
}
@classmethod
@@ -49,6 +50,7 @@ class EmbedderFactory:
"vertexai": "mem0.embeddings.vertexai.VertexAIEmbedding",
"together": "mem0.embeddings.together.TogetherEmbedding",
"lmstudio": "mem0.embeddings.lmstudio.LMStudioEmbedding",
"langchain": "mem0.embeddings.langchain.LangchainEmbedding",
}
@classmethod
@@ -76,6 +78,7 @@ class VectorStoreFactory:
"opensearch": "mem0.vector_stores.opensearch.OpenSearchDB",
"supabase": "mem0.vector_stores.supabase.Supabase",
"weaviate": "mem0.vector_stores.weaviate.Weaviate",
"faiss": "mem0.vector_stores.faiss.FAISS",
}
@classmethod
+1
View File
@@ -23,6 +23,7 @@ class VectorStoreConfig(BaseModel):
"opensearch": "OpenSearchConfig",
"supabase": "SupabaseConfig",
"weaviate": "WeaviateConfig",
"faiss": "FAISSConfig",
}
@model_validator(mode="after")
+464
View File
@@ -0,0 +1,464 @@
import logging
import os
import pickle
import uuid
from pathlib import Path
from typing import Dict, List, Optional
import numpy as np
from pydantic import BaseModel
try:
import faiss
except ImportError:
raise ImportError(
"Could not import faiss python package. "
"Please install it with `pip install faiss-gpu` (for CUDA supported GPU) "
"or `pip install faiss-cpu` (depending on Python version)."
)
from mem0.vector_stores.base import VectorStoreBase
logger = logging.getLogger(__name__)
class OutputData(BaseModel):
id: Optional[str] # memory id
score: Optional[float] # distance
payload: Optional[Dict] # metadata
class FAISS(VectorStoreBase):
def __init__(
self,
collection_name: str,
path: Optional[str] = None,
distance_strategy: str = "euclidean",
normalize_L2: bool = False,
embedding_model_dims: int = 1536,
):
"""
Initialize the FAISS vector store.
Args:
collection_name (str): Name of the collection.
path (str, optional): Path for local FAISS database. Defaults to None.
distance_strategy (str, optional): Distance strategy to use. Options: 'euclidean', 'inner_product', 'cosine'.
Defaults to "euclidean".
normalize_L2 (bool, optional): Whether to normalize L2 vectors. Only applicable for euclidean distance.
Defaults to False.
"""
self.collection_name = collection_name
self.path = path or f"/tmp/faiss/{collection_name}"
self.distance_strategy = distance_strategy
self.normalize_L2 = normalize_L2
self.embedding_model_dims = embedding_model_dims
# Initialize storage structures
self.index = None
self.docstore = {}
self.index_to_id = {}
# Create directory if it doesn't exist
if self.path:
os.makedirs(os.path.dirname(self.path), exist_ok=True)
# Try to load existing index if available
index_path = f"{self.path}/{collection_name}.faiss"
docstore_path = f"{self.path}/{collection_name}.pkl"
if os.path.exists(index_path) and os.path.exists(docstore_path):
self._load(index_path, docstore_path)
else:
self.create_col(collection_name)
def _load(self, index_path: str, docstore_path: str):
"""
Load FAISS index and docstore from disk.
Args:
index_path (str): Path to FAISS index file.
docstore_path (str): Path to docstore pickle file.
"""
try:
self.index = faiss.read_index(index_path)
with open(docstore_path, "rb") as f:
self.docstore, self.index_to_id = pickle.load(f)
logger.info(f"Loaded FAISS index from {index_path} with {self.index.ntotal} vectors")
except Exception as e:
logger.warning(f"Failed to load FAISS index: {e}")
self.docstore = {}
self.index_to_id = {}
def _save(self):
"""Save FAISS index and docstore to disk."""
if not self.path or not self.index:
return
try:
os.makedirs(self.path, exist_ok=True)
index_path = f"{self.path}/{self.collection_name}.faiss"
docstore_path = f"{self.path}/{self.collection_name}.pkl"
faiss.write_index(self.index, index_path)
with open(docstore_path, "wb") as f:
pickle.dump((self.docstore, self.index_to_id), f)
except Exception as e:
logger.warning(f"Failed to save FAISS index: {e}")
def _parse_output(self, scores, ids, limit=None) -> List[OutputData]:
"""
Parse the output data.
Args:
scores: Similarity scores from FAISS.
ids: Indices from FAISS.
limit: Maximum number of results to return.
Returns:
List[OutputData]: Parsed output data.
"""
if limit is None:
limit = len(ids)
results = []
for i in range(min(len(ids), limit)):
if ids[i] == -1: # FAISS returns -1 for empty results
continue
index_id = int(ids[i])
vector_id = self.index_to_id.get(index_id)
if vector_id is None:
continue
payload = self.docstore.get(vector_id)
if payload is None:
continue
payload_copy = payload.copy()
score = float(scores[i])
entry = OutputData(
id=vector_id,
score=score,
payload=payload_copy,
)
results.append(entry)
return results
def create_col(self, name: str, distance: str = None):
"""
Create a new collection.
Args:
name (str): Name of the collection.
distance (str, optional): Distance metric to use. Overrides the distance_strategy
passed during initialization. Defaults to None.
Returns:
self: The FAISS instance.
"""
distance_strategy = distance or self.distance_strategy
# Create index based on distance strategy
if distance_strategy.lower() == "inner_product" or distance_strategy.lower() == "cosine":
self.index = faiss.IndexFlatIP(self.embedding_model_dims)
else:
self.index = faiss.IndexFlatL2(self.embedding_model_dims)
self.collection_name = name
self._save()
return self
def insert(
self,
vectors: List[list],
payloads: Optional[List[Dict]] = None,
ids: Optional[List[str]] = None,
):
"""
Insert vectors into a collection.
Args:
vectors (List[list]): List of vectors to insert.
payloads (Optional[List[Dict]], optional): List of payloads corresponding to vectors. Defaults to None.
ids (Optional[List[str]], optional): List of IDs corresponding to vectors. Defaults to None.
"""
if self.index is None:
raise ValueError("Collection not initialized. Call create_col first.")
if ids is None:
ids = [str(uuid.uuid4()) for _ in range(len(vectors))]
if payloads is None:
payloads = [{} for _ in range(len(vectors))]
if len(vectors) != len(ids) or len(vectors) != len(payloads):
raise ValueError("Vectors, payloads, and IDs must have the same length")
vectors_np = np.array(vectors, dtype=np.float32)
if self.normalize_L2 and self.distance_strategy.lower() == "euclidean":
faiss.normalize_L2(vectors_np)
self.index.add(vectors_np)
starting_idx = len(self.index_to_id)
for i, (vector_id, payload) in enumerate(zip(ids, payloads)):
self.docstore[vector_id] = payload.copy()
self.index_to_id[starting_idx + i] = vector_id
self._save()
logger.info(f"Inserted {len(vectors)} vectors into collection {self.collection_name}")
def search(
self, query: str, vectors: List[list], limit: int = 5, filters: Optional[Dict] = None
) -> List[OutputData]:
"""
Search for similar vectors.
Args:
query (str): Query (not used, kept for API compatibility).
vectors (List[list]): List of vectors to search.
limit (int, optional): Number of results to return. Defaults to 5.
filters (Optional[Dict], optional): Filters to apply to the search. Defaults to None.
Returns:
List[OutputData]: Search results.
"""
if self.index is None:
raise ValueError("Collection not initialized. Call create_col first.")
query_vectors = np.array(vectors, dtype=np.float32)
if len(query_vectors.shape) == 1:
query_vectors = query_vectors.reshape(1, -1)
if self.normalize_L2 and self.distance_strategy.lower() == "euclidean":
faiss.normalize_L2(query_vectors)
fetch_k = limit * 2 if filters else limit
scores, indices = self.index.search(query_vectors, fetch_k)
results = self._parse_output(scores[0], indices[0], limit)
if filters:
filtered_results = []
for result in results:
if self._apply_filters(result.payload, filters):
filtered_results.append(result)
if len(filtered_results) >= limit:
break
results = filtered_results[:limit]
return results
def _apply_filters(self, payload: Dict, filters: Dict) -> bool:
"""
Apply filters to a payload.
Args:
payload (Dict): Payload to filter.
filters (Dict): Filters to apply.
Returns:
bool: True if payload passes filters, False otherwise.
"""
if not filters or not payload:
return True
for key, value in filters.items():
if key not in payload:
return False
if isinstance(value, list):
if payload[key] not in value:
return False
elif payload[key] != value:
return False
return True
def delete(self, vector_id: str):
"""
Delete a vector by ID.
Args:
vector_id (str): ID of the vector to delete.
"""
if self.index is None:
raise ValueError("Collection not initialized. Call create_col first.")
index_to_delete = None
for idx, vid in self.index_to_id.items():
if vid == vector_id:
index_to_delete = idx
break
if index_to_delete is not None:
self.docstore.pop(vector_id, None)
self.index_to_id.pop(index_to_delete, None)
self._save()
logger.info(f"Deleted vector {vector_id} from collection {self.collection_name}")
else:
logger.warning(f"Vector {vector_id} not found in collection {self.collection_name}")
def update(
self,
vector_id: str,
vector: Optional[List[float]] = None,
payload: Optional[Dict] = None,
):
"""
Update a vector and its payload.
Args:
vector_id (str): ID of the vector to update.
vector (Optional[List[float]], optional): Updated vector. Defaults to None.
payload (Optional[Dict], optional): Updated payload. Defaults to None.
"""
if self.index is None:
raise ValueError("Collection not initialized. Call create_col first.")
if vector_id not in self.docstore:
raise ValueError(f"Vector {vector_id} not found")
current_payload = self.docstore[vector_id].copy()
if payload is not None:
self.docstore[vector_id] = payload.copy()
current_payload = self.docstore[vector_id].copy()
if vector is not None:
self.delete(vector_id)
self.insert([vector], [current_payload], [vector_id])
else:
self._save()
logger.info(f"Updated vector {vector_id} in collection {self.collection_name}")
def get(self, vector_id: str) -> OutputData:
"""
Retrieve a vector by ID.
Args:
vector_id (str): ID of the vector to retrieve.
Returns:
OutputData: Retrieved vector.
"""
if self.index is None:
raise ValueError("Collection not initialized. Call create_col first.")
if vector_id not in self.docstore:
return None
payload = self.docstore[vector_id].copy()
return OutputData(
id=vector_id,
score=None,
payload=payload,
)
def list_cols(self) -> List[str]:
"""
List all collections.
Returns:
List[str]: List of collection names.
"""
if not self.path:
return [self.collection_name] if self.index else []
try:
collections = []
path = Path(self.path).parent
for file in path.glob("*.faiss"):
collections.append(file.stem)
return collections
except Exception as e:
logger.warning(f"Failed to list collections: {e}")
return [self.collection_name] if self.index else []
def delete_col(self):
"""
Delete a collection.
"""
if self.path:
try:
index_path = f"{self.path}/{self.collection_name}.faiss"
docstore_path = f"{self.path}/{self.collection_name}.pkl"
if os.path.exists(index_path):
os.remove(index_path)
if os.path.exists(docstore_path):
os.remove(docstore_path)
logger.info(f"Deleted collection {self.collection_name}")
except Exception as e:
logger.warning(f"Failed to delete collection: {e}")
self.index = None
self.docstore = {}
self.index_to_id = {}
def col_info(self) -> Dict:
"""
Get information about a collection.
Returns:
Dict: Collection information.
"""
if self.index is None:
return {"name": self.collection_name, "count": 0}
return {
"name": self.collection_name,
"count": self.index.ntotal,
"dimension": self.index.d,
"distance": self.distance_strategy,
}
def list(self, filters: Optional[Dict] = None, limit: int = 100) -> List[OutputData]:
"""
List all vectors in a collection.
Args:
filters (Optional[Dict], optional): Filters to apply to the list. Defaults to None.
limit (int, optional): Number of vectors to return. Defaults to 100.
Returns:
List[OutputData]: List of vectors.
"""
if self.index is None:
return []
results = []
count = 0
for vector_id, payload in self.docstore.items():
if filters and not self._apply_filters(payload, filters):
continue
payload_copy = payload.copy()
results.append(
OutputData(
id=vector_id,
score=None,
payload=payload_copy,
)
)
count += 1
if count >= limit:
break
return [results]
+1
View File
@@ -67,6 +67,7 @@ class PGVector(VectorStoreBase):
Args:
embedding_model_dims (int): Dimension of the embedding vector.
"""
self.cur.execute("CREATE EXTENSION IF NOT EXISTS vector")
self.cur.execute(
f"""
CREATE TABLE IF NOT EXISTS {self.collection_name} (
+1 -1
View File
@@ -9,8 +9,8 @@ try:
except ImportError:
raise ImportError("The 'vecs' library is required. Please install it using 'pip install vecs'.")
from mem0.configs.vector_stores.supabase import IndexMeasure, IndexMethod
from mem0.vector_stores.base import VectorStoreBase
from mem0.configs.vector_stores.supabase import IndexMethod, IndexMeasure
logger = logging.getLogger(__name__)
Generated
+2 -64
View File
@@ -48,56 +48,6 @@ files = [
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
]
[[package]]
name = "azure-common"
version = "1.1.28"
description = "Microsoft Azure Client Library for Python (Common)"
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "azure-common-1.1.28.zip", hash = "sha256:4ac0cd3214e36b6a1b6a442686722a5d8cc449603aa833f3f0f40bda836704a3"},
{file = "azure_common-1.1.28-py2.py3-none-any.whl", hash = "sha256:5c12d3dcf4ec20599ca6b0d3e09e86e146353d443e7fcc050c9a19c1f9df20ad"},
]
[[package]]
name = "azure-core"
version = "1.32.0"
description = "Microsoft Azure Core Library for Python"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "azure_core-1.32.0-py3-none-any.whl", hash = "sha256:eac191a0efb23bfa83fddf321b27b122b4ec847befa3091fa736a5c32c50d7b4"},
{file = "azure_core-1.32.0.tar.gz", hash = "sha256:22b3c35d6b2dae14990f6c1be2912bf23ffe50b220e708a28ab1bb92b1c730e5"},
]
[package.dependencies]
requests = ">=2.21.0"
six = ">=1.11.0"
typing-extensions = ">=4.6.0"
[package.extras]
aio = ["aiohttp (>=3.0)"]
[[package]]
name = "azure-search-documents"
version = "11.5.2"
description = "Microsoft Azure Cognitive Search Client Library for Python"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "azure_search_documents-11.5.2-py3-none-any.whl", hash = "sha256:c949d011008a4b0bcee3db91132741b4e4d50ddb3f7e2f48944d949d4b413b11"},
{file = "azure_search_documents-11.5.2.tar.gz", hash = "sha256:98977dd1fa4978d3b7d8891a0856b3becb6f02cc07ff2e1ea40b9c7254ada315"},
]
[package.dependencies]
azure-common = ">=1.1"
azure-core = ">=1.28.0"
isodate = ">=0.6.0"
typing-extensions = ">=4.6.0"
[[package]]
name = "backoff"
version = "2.2.1"
@@ -394,7 +344,7 @@ description = "Lightweight in-process concurrent programming"
optional = false
python-versions = ">=3.7"
groups = ["main"]
markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"
markers = "(platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") and python_version < \"3.14\""
files = [
{file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"},
{file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"},
@@ -732,18 +682,6 @@ files = [
{file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"},
]
[[package]]
name = "isodate"
version = "0.7.2"
description = "An ISO 8601 date/time/duration parser and formatter"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15"},
{file = "isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6"},
]
[[package]]
name = "isort"
version = "5.13.2"
@@ -2303,4 +2241,4 @@ graph = ["langchain-neo4j", "neo4j", "rank-bm25"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.9,<4.0"
content-hash = "f7bee9b294566e32c580fef1940cbc6fcb7fd6ca2e695f23c719d134af4c3204"
content-hash = "5848e23bdd7b453f938c9b5f6171866faa01bdcc2651bedb83ee9f4fe90e8bc8"
+6 -2
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "mem0ai"
version = "0.1.77"
version = "0.1.86"
description = "Long-term memory for AI Agents"
authors = ["Mem0 <founders@mem0.ai>"]
exclude = [
@@ -8,6 +8,11 @@ exclude = [
"configs",
"notebooks",
"embedchain",
"evaluation",
"mem0-ts",
"examples",
"vercel-ai-sdk",
"docs",
]
packages = [
{ include = "mem0" },
@@ -25,7 +30,6 @@ sqlalchemy = "^2.0.31"
langchain-neo4j = "^0.4.0"
neo4j = "^5.23.1"
rank-bm25 = "^0.2.2"
azure-search-documents = "^11.5.0"
psycopg2-binary = "^2.9.10"
[tool.poetry.extras]
+12
View File
@@ -0,0 +1,12 @@
OPENAI_API_KEY=
NEO4J_URI=
NEO4J_USERNAME=
NEO4J_PASSWORD=
POSTGRES_HOST=
POSTGRES_PORT=
POSTGRES_DB=
POSTGRES_USER=
POSTGRES_PASSWORD=
POSTGRES_COLLECTION_NAME=
+25
View File
@@ -0,0 +1,25 @@
FROM python:3.12
WORKDIR /app
# Install Poetry
RUN curl -sSL https://install.python-poetry.org | python3 -
ENV PATH="/root/.local/bin:$PATH"
# Copy requirements first for better caching
COPY server/requirements.txt .
RUN pip install -r requirements.txt
# Install mem0 in editable mode using Poetry
WORKDIR /app/packages
COPY pyproject.toml .
COPY poetry.lock .
COPY README.md .
COPY mem0 ./mem0
RUN pip install -e .[graph]
# Return to app directory and copy server code
WORKDIR /app
COPY server .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
+74
View File
@@ -0,0 +1,74 @@
name: mem0-dev
services:
mem0:
build:
context: .. # Set context to parent directory
dockerfile: server/dev.Dockerfile
ports:
- "8888:8000"
env_file:
- .env
networks:
- mem0_network
volumes:
- ./history:/app/history # History db location. By default, it creates a history.db file on the server folder
- .:/app # Server code. This allows to reload the app when the server code is updated
- ../mem0:/app/packages/mem0 # Mem0 library. This allows to reload the app when the library code is updated
depends_on:
postgres:
condition: service_healthy
neo4j:
condition: service_healthy
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload # Enable auto-reload
environment:
- PYTHONDONTWRITEBYTECODE=1 # Prevents Python from writing .pyc files
- PYTHONUNBUFFERED=1 # Ensures Python output is sent straight to terminal
postgres:
image: ankane/pgvector:v0.5.1
restart: on-failure
shm_size: "128mb" # Increase this if vacuuming fails with a "no space left on device" error
networks:
- mem0_network
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "postgres", "-U", "postgres"]
interval: 5s
timeout: 5s
retries: 5
volumes:
- postgres_db:/var/lib/postgresql/data
ports:
- "8432:5432"
neo4j:
image: neo4j:5.26.4
networks:
- mem0_network
healthcheck:
test: wget http://localhost:7687 || exit 1
interval: 1s
timeout: 10s
retries: 20
start_period: 3s
ports:
- "8474:7474" # HTTP
- "8687:7687" # Bolt
volumes:
- neo4j_data:/data
environment:
- NEO4J_AUTH=neo4j/mem0graph
- NEO4J_PLUGINS=["apoc"] # Add this line to install APOC
- NEO4J_apoc_export_file_enabled=true
- NEO4J_apoc_import_file_enabled=true
- NEO4J_apoc_import_file_use__neo4j__config=true
volumes:
neo4j_data:
postgres_db:
networks:
mem0_network:
driver: bridge
+70 -1
View File
@@ -6,10 +6,69 @@ from typing import Optional, List, Any, Dict
from mem0 import Memory
from dotenv import load_dotenv
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
# Load environment variables
load_dotenv()
MEMORY_INSTANCE = Memory()
POSTGRES_HOST = os.environ.get("POSTGRES_HOST", "postgres")
POSTGRES_PORT = os.environ.get("POSTGRES_PORT", "5432")
POSTGRES_DB = os.environ.get("POSTGRES_DB", "postgres")
POSTGRES_USER = os.environ.get("POSTGRES_USER", "postgres")
POSTGRES_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "postgres")
POSTGRES_COLLECTION_NAME = os.environ.get("POSTGRES_COLLECTION_NAME", "memories")
NEO4J_URI = os.environ.get("NEO4J_URI", "bolt://neo4j:7687")
NEO4J_USERNAME = os.environ.get("NEO4J_USERNAME", "neo4j")
NEO4J_PASSWORD = os.environ.get("NEO4J_PASSWORD", "mem0graph")
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
HISTORY_DB_PATH = os.environ.get("HISTORY_DB_PATH", "/app/history/history.db")
DEFAULT_CONFIG = {
"version": "v1.1",
"vector_store": {
"provider": "pgvector",
"config": {
"host": POSTGRES_HOST,
"port": int(POSTGRES_PORT),
"dbname": POSTGRES_DB,
"user": POSTGRES_USER,
"password": POSTGRES_PASSWORD,
"collection_name": POSTGRES_COLLECTION_NAME,
}
},
"graph_store": {
"provider": "neo4j",
"config": {
"url": NEO4J_URI,
"username": NEO4J_USERNAME,
"password": NEO4J_PASSWORD
}
},
"llm": {
"provider": "openai",
"config": {
"api_key": OPENAI_API_KEY,
"temperature": 0.2,
"model": "gpt-4o"
}
},
"embedder": {
"provider": "openai",
"config": {
"api_key": OPENAI_API_KEY,
"model": "text-embedding-3-small"
}
},
"history_db_path": HISTORY_DB_PATH,
}
MEMORY_INSTANCE = Memory.from_config(DEFAULT_CONFIG)
app = FastAPI(
title="Mem0 REST APIs",
@@ -36,6 +95,7 @@ class SearchRequest(BaseModel):
user_id: Optional[str] = None
run_id: Optional[str] = None
agent_id: Optional[str] = None
filters: Optional[Dict[str, Any]] = None
@app.post("/configure", summary="Configure Mem0")
@@ -59,6 +119,7 @@ def add_memory(memory_create: MemoryCreate):
response = MEMORY_INSTANCE.add(messages=[m.model_dump() for m in memory_create.messages], **params)
return JSONResponse(content=response)
except Exception as e:
logging.exception("Error in add_memory:") # This will log the full traceback
raise HTTPException(status_code=500, detail=str(e))
@@ -75,6 +136,7 @@ def get_all_memories(
params = {k: v for k, v in {"user_id": user_id, "run_id": run_id, "agent_id": agent_id}.items() if v is not None}
return MEMORY_INSTANCE.get_all(**params)
except Exception as e:
logging.exception("Error in get_all_memories:")
raise HTTPException(status_code=500, detail=str(e))
@@ -84,6 +146,7 @@ def get_memory(memory_id: str):
try:
return MEMORY_INSTANCE.get(memory_id)
except Exception as e:
logging.exception("Error in get_memory:")
raise HTTPException(status_code=500, detail=str(e))
@@ -94,6 +157,7 @@ def search_memories(search_req: SearchRequest):
params = {k: v for k, v in search_req.model_dump().items() if v is not None and k != "query"}
return MEMORY_INSTANCE.search(query=search_req.query, **params)
except Exception as e:
logging.exception("Error in search_memories:")
raise HTTPException(status_code=500, detail=str(e))
@@ -103,6 +167,7 @@ def update_memory(memory_id: str, updated_memory: Dict[str, Any]):
try:
return MEMORY_INSTANCE.update(memory_id=memory_id, data=updated_memory)
except Exception as e:
logging.exception("Error in update_memory:")
raise HTTPException(status_code=500, detail=str(e))
@@ -112,6 +177,7 @@ def memory_history(memory_id: str):
try:
return MEMORY_INSTANCE.history(memory_id=memory_id)
except Exception as e:
logging.exception("Error in memory_history:")
raise HTTPException(status_code=500, detail=str(e))
@@ -122,6 +188,7 @@ def delete_memory(memory_id: str):
MEMORY_INSTANCE.delete(memory_id=memory_id)
return {"message": "Memory deleted successfully"}
except Exception as e:
logging.exception("Error in delete_memory:")
raise HTTPException(status_code=500, detail=str(e))
@@ -139,6 +206,7 @@ def delete_all_memories(
MEMORY_INSTANCE.delete_all(**params)
return {"message": "All relevant memories deleted"}
except Exception as e:
logging.exception("Error in delete_all_memories:")
raise HTTPException(status_code=500, detail=str(e))
@@ -149,6 +217,7 @@ def reset_memory():
MEMORY_INSTANCE.reset()
return {"message": "All memories reset"}
except Exception as e:
logging.exception("Error in reset_memory:")
raise HTTPException(status_code=500, detail=str(e))
+8 -20
View File
@@ -6,10 +6,12 @@ from mem0.configs.embeddings.base import BaseEmbedderConfig
@pytest.fixture
def mock_lm_studio_client():
with patch("mem0.embeddings.lmstudio.Client") as mock_lm_studio:
with patch("mem0.embeddings.lmstudio.OpenAI") as mock_openai:
mock_client = Mock()
mock_client.list.return_value = {"models": [{"name": "nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf"}]}
mock_lm_studio.return_value = mock_client
mock_client.embeddings.create.return_value = Mock(
data=[Mock(embedding=[0.1, 0.2, 0.3, 0.4, 0.5])]
)
mock_openai.return_value = mock_client
yield mock_client
@@ -17,25 +19,11 @@ def test_embed_text(mock_lm_studio_client):
config = BaseEmbedderConfig(model="nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf", embedding_dims=512)
embedder = LMStudioEmbedding(config)
mock_response = {"embedding": [0.1, 0.2, 0.3, 0.4, 0.5]}
mock_lm_studio_client.embeddings.return_value = mock_response
text = "Sample text to embed."
embedding = embedder.embed(text)
mock_lm_studio_client.embeddings.assert_called_once_with(model="nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf", prompt=text)
mock_lm_studio_client.embeddings.create.assert_called_once_with(
input=["Sample text to embed."], model="nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf"
)
assert embedding == [0.1, 0.2, 0.3, 0.4, 0.5]
def test_ensure_model_exists(mock_lm_studio_client):
config = BaseEmbedderConfig(model="nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf", embedding_dims=512)
embedder = LMStudioEmbedding(config)
mock_lm_studio_client.pull.assert_not_called()
mock_lm_studio_client.list.return_value = {"models": []}
embedder._ensure_model_exists()
mock_lm_studio_client.pull.assert_called_once_with("nomic-embed-text")
+102
View File
@@ -0,0 +1,102 @@
from unittest.mock import Mock, patch
import pytest
from mem0.configs.llms.base import BaseLlmConfig
from mem0.llms.langchain import LangchainLLM
# Add the import for BaseChatModel
try:
from langchain.chat_models.base import BaseChatModel
except ImportError:
from unittest.mock import MagicMock
BaseChatModel = MagicMock
@pytest.fixture
def mock_langchain_model():
"""Mock a Langchain model for testing."""
mock_model = Mock(spec=BaseChatModel)
mock_model.invoke.return_value = Mock(content="This is a test response")
return mock_model
def test_langchain_initialization(mock_langchain_model):
"""Test that LangchainLLM initializes correctly with a valid model."""
# Create a config with the model instance directly
config = BaseLlmConfig(
model=mock_langchain_model,
temperature=0.7,
max_tokens=100,
api_key="test-api-key"
)
# Initialize the LangchainLLM
llm = LangchainLLM(config)
# Verify the model was correctly assigned
assert llm.langchain_model == mock_langchain_model
def test_generate_response(mock_langchain_model):
"""Test that generate_response correctly processes messages and returns a response."""
# Create a config with the model instance
config = BaseLlmConfig(
model=mock_langchain_model,
temperature=0.7,
max_tokens=100,
api_key="test-api-key"
)
# Initialize the LangchainLLM
llm = LangchainLLM(config)
# Create test messages
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello, how are you?"},
{"role": "assistant", "content": "I'm doing well! How can I help you?"},
{"role": "user", "content": "Tell me a joke."}
]
# Get response
response = llm.generate_response(messages)
# Verify the correct message format was passed to the model
expected_langchain_messages = [
("system", "You are a helpful assistant."),
("human", "Hello, how are you?"),
("ai", "I'm doing well! How can I help you?"),
("human", "Tell me a joke.")
]
mock_langchain_model.invoke.assert_called_once()
# Extract the first argument of the first call
actual_messages = mock_langchain_model.invoke.call_args[0][0]
assert actual_messages == expected_langchain_messages
assert response == "This is a test response"
def test_invalid_model():
"""Test that LangchainLLM raises an error with an invalid model."""
config = BaseLlmConfig(
model="not-a-valid-model-instance",
temperature=0.7,
max_tokens=100,
api_key="test-api-key"
)
with pytest.raises(ValueError, match="`model` must be an instance of BaseChatModel"):
LangchainLLM(config)
def test_missing_model():
"""Test that LangchainLLM raises an error when model is None."""
config = BaseLlmConfig(
model=None,
temperature=0.7,
max_tokens=100,
api_key="test-api-key"
)
with pytest.raises(ValueError, match="`model` parameter is required"):
LangchainLLM(config)
+21 -9
View File
@@ -8,27 +8,39 @@ from mem0.llms.lmstudio import LMStudioLLM
@pytest.fixture
def mock_lm_studio_client():
with patch("mem0.llms.lmstudio.Client") as mock_lm_studio:
with patch("mem0.llms.lmstudio.OpenAI") as mock_openai: # Corrected path
mock_client = Mock()
mock_client.list.return_value = {"models": [{"name": "lmstudio-community/Meta-Llama-3.1-8B-Instruct-GGUF/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf"}]}
mock_lm_studio.return_value = mock_client
mock_client.chat.completions.create.return_value = Mock(
choices=[
Mock(message=Mock(content="I'm doing well, thank you for asking!"))
]
)
mock_openai.return_value = mock_client
yield mock_client
def test_generate_response_without_tools(mock_lm_studio_client):
config = BaseLlmConfig(model="lmstudio-community/Meta-Llama-3.1-8B-Instruct-GGUF/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf", temperature=0.7, max_tokens=100, top_p=1.0)
config = BaseLlmConfig(
model="lmstudio-community/Meta-Llama-3.1-8B-Instruct-GGUF/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf",
temperature=0.7,
max_tokens=100,
top_p=1.0,
)
llm = LMStudioLLM(config)
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello, how are you?"},
]
mock_response = {"message": {"content": "I'm doing well, thank you for asking!"}}
mock_lm_studio_client.chat.return_value = mock_response
response = llm.generate_response(messages)
mock_lm_studio_client.chat.assert_called_once_with(
model="lmstudio-community/Meta-Llama-3.1-8B-Instruct-GGUF/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf", messages=messages, options={"temperature": 0.7, "num_predict": 100, "top_p": 1.0}
mock_lm_studio_client.chat.completions.create.assert_called_once_with(
model="lmstudio-community/Meta-Llama-3.1-8B-Instruct-GGUF/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf",
messages=messages,
temperature=0.7,
max_tokens=100,
top_p=1.0,
response_format={"type": "json_object"},
)
assert response == "I'm doing well, thank you for asking!"
+1 -1
View File
@@ -38,7 +38,7 @@ def mock_clients():
# Stub required methods on index_client.
mock_index_client.create_or_update_index = Mock()
mock_index_client.list_indexes = Mock()
mock_index_client.list_index_names = Mock(return_value=["test-index"])
mock_index_client.list_index_names = Mock(return_value=[])
mock_index_client.delete_index = Mock()
# For col_info() we assume get_index returns an object with name and fields attributes.
fake_index = Mock()
+314
View File
@@ -0,0 +1,314 @@
import os
import tempfile
from unittest.mock import Mock, patch
import faiss
import numpy as np
import pytest
from mem0.vector_stores.faiss import FAISS, OutputData
@pytest.fixture
def mock_faiss_index():
index = Mock(spec=faiss.IndexFlatL2)
index.d = 128 # Dimension of the vectors
index.ntotal = 0 # Number of vectors in the index
return index
@pytest.fixture
def faiss_instance(mock_faiss_index):
with tempfile.TemporaryDirectory() as temp_dir:
# Mock the faiss index creation
with patch('faiss.IndexFlatL2', return_value=mock_faiss_index):
# Mock the faiss.write_index function
with patch('faiss.write_index'):
# Create a FAISS instance with a temporary directory
faiss_store = FAISS(
collection_name="test_collection",
path=os.path.join(temp_dir, "test_faiss"),
distance_strategy="euclidean",
)
# Set up the mock index
faiss_store.index = mock_faiss_index
yield faiss_store
def test_create_col(faiss_instance, mock_faiss_index):
# Test creating a collection with euclidean distance
with patch('faiss.IndexFlatL2', return_value=mock_faiss_index) as mock_index_flat_l2:
with patch('faiss.write_index'):
faiss_instance.create_col(name="new_collection", vector_size=256)
mock_index_flat_l2.assert_called_once_with(256)
# Test creating a collection with inner product distance
with patch('faiss.IndexFlatIP', return_value=mock_faiss_index) as mock_index_flat_ip:
with patch('faiss.write_index'):
faiss_instance.create_col(name="new_collection", vector_size=256, distance="inner_product")
mock_index_flat_ip.assert_called_once_with(256)
def test_insert(faiss_instance, mock_faiss_index):
# Prepare test data
vectors = [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]
payloads = [{"name": "vector1"}, {"name": "vector2"}]
ids = ["id1", "id2"]
# Mock the numpy array conversion
with patch('numpy.array', return_value=np.array(vectors, dtype=np.float32)) as mock_np_array:
# Mock index.add
mock_faiss_index.add.return_value = None
# Call insert
faiss_instance.insert(vectors=vectors, payloads=payloads, ids=ids)
# Verify numpy.array was called
mock_np_array.assert_called_once_with(vectors, dtype=np.float32)
# Verify index.add was called
mock_faiss_index.add.assert_called_once()
# Verify docstore and index_to_id were updated
assert faiss_instance.docstore["id1"] == {"name": "vector1"}
assert faiss_instance.docstore["id2"] == {"name": "vector2"}
assert faiss_instance.index_to_id[0] == "id1"
assert faiss_instance.index_to_id[1] == "id2"
def test_search(faiss_instance, mock_faiss_index):
# Prepare test data
query_vector = [0.1, 0.2, 0.3]
# Setup the docstore and index_to_id mapping
faiss_instance.docstore = {
"id1": {"name": "vector1"},
"id2": {"name": "vector2"}
}
faiss_instance.index_to_id = {0: "id1", 1: "id2"}
# First, create the mock for the search return values
search_scores = np.array([[0.9, 0.8]])
search_indices = np.array([[0, 1]])
mock_faiss_index.search.return_value = (search_scores, search_indices)
# Then patch numpy.array only for the query vector conversion
with patch('numpy.array') as mock_np_array:
mock_np_array.return_value = np.array(query_vector, dtype=np.float32)
# Then patch _parse_output to return the expected results
expected_results = [
OutputData(id="id1", score=0.9, payload={"name": "vector1"}),
OutputData(id="id2", score=0.8, payload={"name": "vector2"})
]
with patch.object(faiss_instance, '_parse_output', return_value=expected_results):
# Call search
results = faiss_instance.search(query="test query", vectors=query_vector, limit=2)
# Verify numpy.array was called (but we don't check exact call arguments since it's complex)
assert mock_np_array.called
# Verify index.search was called
mock_faiss_index.search.assert_called_once()
# Verify results
assert len(results) == 2
assert results[0].id == "id1"
assert results[0].score == 0.9
assert results[0].payload == {"name": "vector1"}
assert results[1].id == "id2"
assert results[1].score == 0.8
assert results[1].payload == {"name": "vector2"}
def test_search_with_filters(faiss_instance, mock_faiss_index):
# Prepare test data
query_vector = [0.1, 0.2, 0.3]
# Setup the docstore and index_to_id mapping
faiss_instance.docstore = {
"id1": {"name": "vector1", "category": "A"},
"id2": {"name": "vector2", "category": "B"}
}
faiss_instance.index_to_id = {0: "id1", 1: "id2"}
# First set up the search return values
search_scores = np.array([[0.9, 0.8]])
search_indices = np.array([[0, 1]])
mock_faiss_index.search.return_value = (search_scores, search_indices)
# Patch numpy.array for query vector conversion
with patch('numpy.array') as mock_np_array:
mock_np_array.return_value = np.array(query_vector, dtype=np.float32)
# Directly mock the _parse_output method to return our expected values
# We're simulating that _parse_output filters to just the first result
all_results = [
OutputData(id="id1", score=0.9, payload={"name": "vector1", "category": "A"}),
OutputData(id="id2", score=0.8, payload={"name": "vector2", "category": "B"})
]
filtered_results = [all_results[0]] # Just the "category": "A" result
# Create a side_effect function that returns all results first (for _parse_output)
# then returns filtered results (for the filters)
parse_output_mock = Mock(side_effect=[all_results, filtered_results])
# Replace the _apply_filters method to handle our test case
with patch.object(faiss_instance, '_parse_output', return_value=all_results):
with patch.object(faiss_instance, '_apply_filters', side_effect=lambda p, f: p.get("category") == "A"):
# Call search with filters
results = faiss_instance.search(
query="test query",
vectors=query_vector,
limit=2,
filters={"category": "A"}
)
# Verify numpy.array was called
assert mock_np_array.called
# Verify index.search was called
mock_faiss_index.search.assert_called_once()
# Verify filtered results - since we've mocked everything,
# we should get just the result we want
assert len(results) == 1
assert results[0].id == "id1"
assert results[0].score == 0.9
assert results[0].payload == {"name": "vector1", "category": "A"}
def test_delete(faiss_instance):
# Setup the docstore and index_to_id mapping
faiss_instance.docstore = {
"id1": {"name": "vector1"},
"id2": {"name": "vector2"}
}
faiss_instance.index_to_id = {0: "id1", 1: "id2"}
# Call delete
faiss_instance.delete(vector_id="id1")
# Verify the vector was removed from docstore and index_to_id
assert "id1" not in faiss_instance.docstore
assert 0 not in faiss_instance.index_to_id
assert "id2" in faiss_instance.docstore
assert 1 in faiss_instance.index_to_id
def test_update(faiss_instance, mock_faiss_index):
# Setup the docstore and index_to_id mapping
faiss_instance.docstore = {
"id1": {"name": "vector1"},
"id2": {"name": "vector2"}
}
faiss_instance.index_to_id = {0: "id1", 1: "id2"}
# Test updating payload only
faiss_instance.update(vector_id="id1", payload={"name": "updated_vector1"})
assert faiss_instance.docstore["id1"] == {"name": "updated_vector1"}
# Test updating vector
# This requires mocking the delete and insert methods
with patch.object(faiss_instance, 'delete') as mock_delete:
with patch.object(faiss_instance, 'insert') as mock_insert:
new_vector = [0.7, 0.8, 0.9]
faiss_instance.update(vector_id="id2", vector=new_vector)
# Verify delete and insert were called
# Match the actual call signature (positional arg instead of keyword)
mock_delete.assert_called_once_with("id2")
mock_insert.assert_called_once()
def test_get(faiss_instance):
# Setup the docstore
faiss_instance.docstore = {
"id1": {"name": "vector1"},
"id2": {"name": "vector2"}
}
# Test getting an existing vector
result = faiss_instance.get(vector_id="id1")
assert result.id == "id1"
assert result.payload == {"name": "vector1"}
assert result.score is None
# Test getting a non-existent vector
result = faiss_instance.get(vector_id="id3")
assert result is None
def test_list(faiss_instance):
# Setup the docstore
faiss_instance.docstore = {
"id1": {"name": "vector1", "category": "A"},
"id2": {"name": "vector2", "category": "B"},
"id3": {"name": "vector3", "category": "A"}
}
# Test listing all vectors
results = faiss_instance.list()
# Fix the expected result - the list method returns a list of lists
assert len(results[0]) == 3
# Test listing with a limit
results = faiss_instance.list(limit=2)
assert len(results[0]) == 2
# Test listing with filters
results = faiss_instance.list(filters={"category": "A"})
assert len(results[0]) == 2
for result in results[0]:
assert result.payload["category"] == "A"
def test_col_info(faiss_instance, mock_faiss_index):
# Mock index attributes
mock_faiss_index.ntotal = 5
mock_faiss_index.d = 128
# Get collection info
info = faiss_instance.col_info()
# Verify the returned info
assert info["name"] == "test_collection"
assert info["count"] == 5
assert info["dimension"] == 128
assert info["distance"] == "euclidean"
def test_delete_col(faiss_instance):
# Mock the os.remove function
with patch('os.remove') as mock_remove:
with patch('os.path.exists', return_value=True):
# Call delete_col
faiss_instance.delete_col()
# Verify os.remove was called twice (for index and docstore files)
assert mock_remove.call_count == 2
# Verify the internal state was reset
assert faiss_instance.index is None
assert faiss_instance.docstore == {}
assert faiss_instance.index_to_id == {}
def test_normalize_L2(faiss_instance, mock_faiss_index):
# Setup a FAISS instance with normalize_L2=True
faiss_instance.normalize_L2 = True
# Prepare test data
vectors = [[0.1, 0.2, 0.3]]
# Mock numpy array conversion
with patch('numpy.array', return_value=np.array(vectors, dtype=np.float32)) as mock_np_array:
# Mock faiss.normalize_L2
with patch('faiss.normalize_L2') as mock_normalize:
# Call insert
faiss_instance.insert(vectors=vectors, ids=["id1"])
# Verify faiss.normalize_L2 was called
mock_normalize.assert_called_once()
+9 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@mem0/vercel-ai-provider",
"version": "0.0.14",
"version": "1.0.0",
"description": "Vercel AI Provider for providing memory to LLMs",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
@@ -34,7 +34,7 @@
"@ai-sdk/provider-utils": "2.1.10",
"ai": "4.1.46",
"dotenv": "^16.4.5",
"mem0ai": "^1.0.29",
"mem0ai": "^2.1.12",
"partial-json": "0.1.7",
"zod": "^3.0.0"
},
@@ -66,5 +66,12 @@
"directories": {
"example": "example",
"test": "tests"
},
"packageManager": "pnpm@10.5.2+sha512.da9dc28cd3ff40d0592188235ab25d3202add8a207afbedc682220e4a0029ffbff4562102b9e6e46b4e3f9e8bd53e6d05de48544b0c57d4b0179e22c76d1199b",
"pnpm": {
"onlyBuiltDependencies": [
"esbuild",
"sqlite3"
]
}
}
+5697
View File
File diff suppressed because it is too large Load Diff
@@ -31,9 +31,14 @@ export class Mem0GenericLanguageModel implements LanguageModelV1 {
provider: string;
private async processMemories(messagesPrompts: LanguageModelV1Message[], mem0Config: Mem0ConfigSettings) {
const memories = await getMemories(messagesPrompts, mem0Config);
// Add New Memories
await addMemories(messagesPrompts, mem0Config);
addMemories(messagesPrompts, mem0Config).then((res) => {
return res;
});
// Get Memories
const memories = await getMemories(messagesPrompts, mem0Config);
const mySystemPrompt = "These are the memories I have stored. Give more weightage to the question by users and try to answer that first. You have to modify your answer based on the memories I have provided. If the memories are irrelevant you can ignore them. Also don't reply to this section of the prompt, or the memories, they are only for your reference. The System prompt starts after text System Message: \n\n";
let memoriesText = "";
+2
View File
@@ -26,6 +26,8 @@ export interface Mem0ConfigSettings {
page_size?: number;
mem0ApiKey?: string;
top_k?: number;
threshold?: number;
rerank?: boolean;
}
export interface Mem0ChatConfig extends Mem0ConfigSettings, Mem0ProviderSettings {}
+1 -1
View File
@@ -71,7 +71,7 @@ const searchInternalMemories = async (query: string, config?: Mem0ConfigSettings
environmentVariableName: "MEM0_API_KEY",
description: "Mem0",
})}`, 'Content-Type': 'application/json'},
body: JSON.stringify({query, filters, top_k: config&&config.top_k || top_k, version: "v2", ...org_project_filters}),
body: JSON.stringify({query, filters, ...config, top_k: config&&config.top_k || top_k, version: "v2", ...org_project_filters}),
};
const response = await fetch('https://api.mem0.ai/v2/memories/search/', options);
const data = await response.json();