Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b480c71f0f | |||
| 309c8c18a6 | |||
| f4d8647264 | |||
| f95c4cbbe5 | |||
| 00c7cc432c | |||
| 91abc03880 | |||
| 9100e95175 | |||
| cdb8dcdb9e | |||
| 2a79add7a5 | |||
| 9dfa9b4412 | |||
| 5509066925 | |||
| 3712522b14 | |||
| 93f34e4116 | |||
| 39e5cbfacc | |||
| d30c78c5eb | |||
| 039756abbe | |||
| cb38c2dae7 | |||
| 4dc9f6fad6 | |||
| 1b1f02eb57 | |||
| 1db1105cad | |||
| 91a28c7f04 | |||
| a78e1894b1 | |||
| 83338295d8 | |||
| aff70807c6 | |||
| 648c86b53d | |||
| de0b2de9c2 | |||
| e3f460fba4 | |||
| fe7b336922 | |||
| 1033ab227c | |||
| 5ed15c3bcd | |||
| c1f5a655ba | |||
| 72bb631bb5 | |||
| 2bf9286071 | |||
| 884b597312 | |||
| f1471bcc55 | |||
| 9ae23f9c88 | |||
| cbecbb7b64 | |||
| f8ad0c2b2c | |||
| d0da9a1ae0 | |||
| bc4ab3db77 | |||
| 0eb53b9f27 | |||
| 3cef3ed95e | |||
| 45e5f2af93 | |||
| 32ba13b3ea | |||
| 69a91d5cbb | |||
| 2004427acd | |||
| 2517ccd489 | |||
| 9d0300f774 | |||
| 366d263e0b | |||
| 4321d24284 | |||
| 9cb2a13f3b | |||
| 5ec7889d9a | |||
| b54845bcc9 | |||
| 1ae2747ff8 | |||
| 953a5a4a2d | |||
| 2b49c9eedd | |||
| 9db5f62262 | |||
| a1bd4285db | |||
| e77a10a8da | |||
| e4307ae420 | |||
| 7c89d00079 | |||
| 563eaae5ee | |||
| 6733f78f81 | |||
| c11637bd2f | |||
| ff30cb8ddd | |||
| 2e853c3d22 | |||
| 3cc7013fde | |||
| 8e6a08aa83 | |||
| 8b9a8e5825 | |||
| afc630272d | |||
| e33008e3a4 | |||
| 7b516328a8 |
@@ -13,7 +13,8 @@ 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
|
||||
google-generativeai elasticsearch opensearch-py vecs pinecone pinecone-text faiss-cpu langchain-community \
|
||||
upstash-vector azure-search-documents
|
||||
|
||||
# Format code with ruff
|
||||
format:
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
title: 'Feedback'
|
||||
openapi: post /v1/feedback/
|
||||
---
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: 'Get Memory Export'
|
||||
openapi: get /v1/exports/
|
||||
openapi: post /v1/exports/get
|
||||
---
|
||||
|
||||
Retrieve the latest structured memory export after submitting an export job. You can filter the export by `user_id`, `run_id`, `session_id`, or `app_id` to get the most recent export matching your filters.
|
||||
@@ -0,0 +1,156 @@
|
||||
---
|
||||
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-09" description="v2.1.15">
|
||||
**Improvements:**
|
||||
- **Client:** Added support for Mem0 to work with Chrome Extensions
|
||||
</Update>
|
||||
|
||||
<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>
|
||||
|
||||
@@ -84,6 +84,7 @@ Here's a comprehensive list of all parameters that can be used across different
|
||||
| `memory_add_embedding_type` | The type of embedding to use for the add memory action | VertexAI |
|
||||
| `memory_update_embedding_type` | The type of embedding to use for the update memory action | VertexAI |
|
||||
| `memory_search_embedding_type` | The type of embedding to use for the search memory action | VertexAI |
|
||||
| `lmstudio_base_url` | Base URL for LM Studio API | LM Studio |
|
||||
</Tab>
|
||||
<Tab title="TypeScript">
|
||||
| Parameter | Description | Provider |
|
||||
|
||||
@@ -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).
|
||||
@@ -0,0 +1,38 @@
|
||||
You can use embedding models from LM Studio to run Mem0 locally.
|
||||
|
||||
### Usage
|
||||
|
||||
```python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "your_api_key" # For LLM
|
||||
|
||||
config = {
|
||||
"embedder": {
|
||||
"provider": "lmstudio",
|
||||
"config": {
|
||||
"model": "nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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="john")
|
||||
```
|
||||
|
||||
### Config
|
||||
|
||||
Here are the parameters available for configuring Ollama embedder:
|
||||
|
||||
| Parameter | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `model` | The name of the OpenAI model to use | `nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf` |
|
||||
| `embedding_dims` | Dimensions of the embedding model | `1536` |
|
||||
| `lmstudio_base_url` | Base URL for LM Studio connection | `http://localhost:1234/v1` |
|
||||
@@ -22,6 +22,8 @@ See the list of supported embedders below.
|
||||
<Card title="Gemini" href="/components/embedders/models/gemini"></Card>
|
||||
<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
|
||||
|
||||
@@ -29,7 +29,7 @@ iconType: "solid"
|
||||
Config values are applied in the following order of precedence (from highest to lowest):
|
||||
|
||||
1. Values explicitly set in the `config` object/dictionary
|
||||
2. Environment variables (e.g., `OPENAI_API_KEY`, `OPENAI_API_BASE`)
|
||||
2. Environment variables (e.g., `OPENAI_API_KEY`, `OPENAI_BASE_URL`)
|
||||
3. Default values defined in the LLM implementation
|
||||
|
||||
This means that values specified in the `config` will override corresponding environment variables, which in turn override default values.
|
||||
@@ -108,6 +108,7 @@ Here's a comprehensive list of all parameters that can be used across different
|
||||
| `azure_kwargs` | Azure LLM args for initialization | AzureOpenAI |
|
||||
| `deepseek_base_url` | Base URL for DeepSeek API | DeepSeek |
|
||||
| `xai_base_url` | Base URL for XAI API | XAI |
|
||||
| `lmstudio_base_url` | Base URL for LM Studio API | LM Studio |
|
||||
</Tab>
|
||||
<Tab title="TypeScript">
|
||||
| Parameter | Description | Provider |
|
||||
|
||||
@@ -4,6 +4,9 @@ title: Azure OpenAI
|
||||
|
||||
To use Azure OpenAI models, you have to set the `LLM_AZURE_OPENAI_API_KEY`, `LLM_AZURE_ENDPOINT`, `LLM_AZURE_DEPLOYMENT` and `LLM_AZURE_API_VERSION` environment variables. You can obtain the Azure API key from the [Azure](https://azure.microsoft.com/).
|
||||
|
||||
> **Note**: The following are currently unsupported with reasoning models `Parallel tool calling`,`temperature`, `top_p`, `presence_penalty`, `frequency_penalty`, `logprobs`, `top_logprobs`, `logit_bias`, `max_tokens`
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
```python
|
||||
|
||||
@@ -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).
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: LM Studio
|
||||
---
|
||||
|
||||
To use LM Studio with Mem0, you'll need to have LM Studio running locally with its server enabled. LM Studio provides a way to run local LLMs with an OpenAI-compatible API.
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "your-api-key" # used for embedding model
|
||||
|
||||
config = {
|
||||
"llm": {
|
||||
"provider": "lmstudio",
|
||||
"config": {
|
||||
"model": "lmstudio-community/Meta-Llama-3.1-70B-Instruct-GGUF/Meta-Llama-3.1-70B-Instruct-IQ2_M.gguf",
|
||||
"temperature": 0.2,
|
||||
"max_tokens": 2000,
|
||||
"lmstudio_base_url": "http://localhost:1234/v1", # default LM Studio API URL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
### Running Completely Locally
|
||||
|
||||
You can also use LM Studio for both LLM and embedding to run Mem0 entirely locally:
|
||||
|
||||
```python
|
||||
from mem0 import Memory
|
||||
|
||||
# No external API keys needed!
|
||||
config = {
|
||||
"llm": {
|
||||
"provider": "lmstudio"
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "lmstudio"
|
||||
}
|
||||
}
|
||||
|
||||
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="alice123", metadata={"category": "movies"})
|
||||
```
|
||||
|
||||
<Note>
|
||||
When using LM Studio for both LLM and embedding, make sure you have:
|
||||
1. An LLM model loaded for generating responses
|
||||
2. An embedding model loaded for vector embeddings
|
||||
3. The server enabled with the correct endpoints accessible
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
To use LM Studio, you need to:
|
||||
1. Download and install [LM Studio](https://lmstudio.ai/)
|
||||
2. Start a local server from the "Server" tab
|
||||
3. Set the appropriate `lmstudio_base_url` in your configuration (default is usually http://localhost:1234/v1)
|
||||
</Note>
|
||||
|
||||
## Config
|
||||
|
||||
All available parameters for the `lmstudio` config are present in [Master List of All Params in Config](../config).
|
||||
@@ -32,6 +32,8 @@ To view all supported llms, visit the [Supported LLMs](./models).
|
||||
<Card title="Gemini" href="/components/llms/models/gemini" />
|
||||
<Card title="DeepSeek" href="/components/llms/models/deepseek" />
|
||||
<Card title="xAI" href="/components/llms/models/xAI" />
|
||||
<Card title="LM Studio" href="/components/llms/models/lmstudio" />
|
||||
<Card title="Langchain" href="/components/llms/models/langchain" />
|
||||
</CardGroup>
|
||||
|
||||
## Structured vs Unstructured Outputs
|
||||
|
||||
@@ -8,7 +8,7 @@ iconType: "solid"
|
||||
|
||||
The `config` is defined as an object with two main keys:
|
||||
- `vector_store`: Specifies the vector database provider and its configuration
|
||||
- `provider`: The name of the vector database (e.g., "chroma", "pgvector", "qdrant", "milvus","azure_ai_search", "vertex_ai_vector_search")
|
||||
- `provider`: The name of the vector database (e.g., "chroma", "pgvector", "qdrant", "milvus", "upstash_vector", "azure_ai_search", "vertex_ai_vector_search")
|
||||
- `config`: A nested dictionary containing provider-specific settings
|
||||
|
||||
|
||||
|
||||
+27
-1
@@ -1,4 +1,6 @@
|
||||
# Azure AI Search
|
||||
---
|
||||
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.
|
||||
|
||||
@@ -50,6 +52,24 @@ config = {
|
||||
}
|
||||
```
|
||||
|
||||
## Using hybrid search
|
||||
|
||||
```python
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "azure_ai_search",
|
||||
"config": {
|
||||
"service_name": "ai-search-test",
|
||||
"api_key": "*****",
|
||||
"collection_name": "mem0",
|
||||
"embedding_model_dims": 1536,
|
||||
"hybrid_search": True,
|
||||
"vector_filter_mode": "postFilter"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Parameters
|
||||
|
||||
| Parameter | Description | Default Value | Options |
|
||||
@@ -60,6 +80,8 @@ config = {
|
||||
| `embedding_model_dims` | Dimensions of the embedding model | `1536` | Any integer value |
|
||||
| `compression_type` | Type of vector compression to use | `none` | `none`, `scalar`, `binary` |
|
||||
| `use_float16` | Store vectors in half precision (Edm.Half) | `False` | `True`, `False` |
|
||||
| `vector_filter_mode` | Vector filter mode to use | `preFilter` | `postFilter`, `preFilter` |
|
||||
| `hybrid_search` | Use hybrid search | `False` | `True`, `False` |
|
||||
|
||||
## Notes on Configuration Options
|
||||
|
||||
@@ -68,6 +90,10 @@ config = {
|
||||
- `scalar`: Scalar quantization with reasonable balance of speed and accuracy
|
||||
- `binary`: Binary quantization for maximum compression with some accuracy trade-off
|
||||
|
||||
- **vector_filter_mode**:
|
||||
- `preFilter`: Applies filters before vector search (faster)
|
||||
- `postFilter`: Applies filters after vector search (may provide better relevance)
|
||||
|
||||
- **use_float16**: Using half precision (float16) reduces storage requirements but may slightly impact accuracy. Useful for very large vector collections.
|
||||
|
||||
- **Filterable Fields**: The implementation automatically extracts `user_id`, `run_id`, and `agent_id` fields from payloads for filtering.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,92 @@
|
||||
[Pinecone](https://www.pinecone.io/) is a fully managed vector database designed for machine learning applications, offering high performance vector search with low latency at scale. It's particularly well-suited for semantic search, recommendation systems, and other AI-powered applications.
|
||||
|
||||
> **Note**: Before configuring Pinecone, you need to select an embedding model (e.g., OpenAI, Cohere, or custom models) and ensure the `embedding_model_dims` in your config matches your chosen model's dimensions. For example, OpenAI's text-embedding-3-small uses 1536 dimensions.
|
||||
|
||||
### Usage
|
||||
|
||||
```python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "sk-xx"
|
||||
os.environ["PINECONE_API_KEY"] = "your-api-key"
|
||||
|
||||
# Example using serverless configuration
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "pinecone",
|
||||
"config": {
|
||||
"collection_name": "testing",
|
||||
"embedding_model_dims": 1536, # Matches OpenAI's text-embedding-3-small
|
||||
"serverless_config": {
|
||||
"cloud": "aws", # Choose between 'aws' or 'gcp' or 'azure'
|
||||
"region": "us-east-1"
|
||||
},
|
||||
"metric": "cosine"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"})
|
||||
```
|
||||
|
||||
### Config
|
||||
|
||||
Here are the parameters available for configuring Pinecone:
|
||||
|
||||
| Parameter | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `collection_name` | Name of the index/collection | Required |
|
||||
| `embedding_model_dims` | Dimensions of the embedding model (must match your chosen embedding model) | Required |
|
||||
| `client` | Existing Pinecone client instance | `None` |
|
||||
| `api_key` | API key for Pinecone | Environment variable: `PINECONE_API_KEY` |
|
||||
| `environment` | Pinecone environment | `None` |
|
||||
| `serverless_config` | Configuration for serverless deployment (AWS or GCP or Azure) | `None` |
|
||||
| `pod_config` | Configuration for pod-based deployment | `None` |
|
||||
| `hybrid_search` | Whether to enable hybrid search | `False` |
|
||||
| `metric` | Distance metric for vector similarity | `"cosine"` |
|
||||
| `batch_size` | Batch size for operations | `100` |
|
||||
|
||||
> **Important**: You must choose either `serverless_config` or `pod_config` for your deployment, but not both.
|
||||
|
||||
#### Serverless Config Example
|
||||
```python
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "pinecone",
|
||||
"config": {
|
||||
"collection_name": "memory_index",
|
||||
"embedding_model_dims": 1536, # For OpenAI's text-embedding-3-small
|
||||
"serverless_config": {
|
||||
"cloud": "aws", # or "gcp" or "azure"
|
||||
"region": "us-east-1" # Choose appropriate region
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Pod Config Example
|
||||
```python
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "pinecone",
|
||||
"config": {
|
||||
"collection_name": "memory_index",
|
||||
"embedding_model_dims": 1536, # For OpenAI's text-embedding-ada-002
|
||||
"pod_config": {
|
||||
"environment": "gcp-starter",
|
||||
"replicas": 1,
|
||||
"pod_type": "starter"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -4,7 +4,8 @@ Create a [Supabase](https://supabase.com/dashboard/projects) account and project
|
||||
|
||||
### Usage
|
||||
|
||||
```python
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
@@ -32,10 +33,90 @@ messages = [
|
||||
m.add(messages, user_id="alice", metadata={"category": "movies"})
|
||||
```
|
||||
|
||||
```typescript Typescript
|
||||
import { Memory } from "mem0ai/oss";
|
||||
|
||||
const config = {
|
||||
vectorStore: {
|
||||
provider: "supabase",
|
||||
config: {
|
||||
collectionName: "memories",
|
||||
embeddingModelDims: 1536,
|
||||
supabaseUrl: process.env.SUPABASE_URL || "",
|
||||
supabaseKey: process.env.SUPABASE_KEY || "",
|
||||
tableName: "memories",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const memory = new Memory(config);
|
||||
|
||||
const 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."}
|
||||
]
|
||||
|
||||
await memory.add(messages, { userId: "alice", metadata: { category: "movies" } });
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### SQL Migrations for TypeScript Implementation
|
||||
|
||||
The following SQL migrations are required to enable the vector extension and create the memories table:
|
||||
|
||||
```sql
|
||||
-- Enable the vector extension
|
||||
create extension if not exists vector;
|
||||
|
||||
-- Create the memories table
|
||||
create table if not exists memories (
|
||||
id text primary key,
|
||||
embedding vector(1536),
|
||||
metadata jsonb,
|
||||
created_at timestamp with time zone default timezone('utc', now()),
|
||||
updated_at timestamp with time zone default timezone('utc', now())
|
||||
);
|
||||
|
||||
-- Create the vector similarity search function
|
||||
create or replace function match_vectors(
|
||||
query_embedding vector(1536),
|
||||
match_count int,
|
||||
filter jsonb default '{}'::jsonb
|
||||
)
|
||||
returns table (
|
||||
id text,
|
||||
similarity float,
|
||||
metadata jsonb
|
||||
)
|
||||
language plpgsql
|
||||
as $$
|
||||
begin
|
||||
return query
|
||||
select
|
||||
t.id::text,
|
||||
1 - (t.embedding <=> query_embedding) as similarity,
|
||||
t.metadata
|
||||
from memories t
|
||||
where case
|
||||
when filter::text = '{}'::text then true
|
||||
else t.metadata @> filter
|
||||
end
|
||||
order by t.embedding <=> query_embedding
|
||||
limit match_count;
|
||||
end;
|
||||
$$;
|
||||
```
|
||||
|
||||
Goto [Supabase](https://supabase.com/dashboard/projects) and run the above SQL migrations inside the SQL Editor.
|
||||
|
||||
### Config
|
||||
|
||||
Here are the parameters available for configuring Supabase:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Python">
|
||||
| Parameter | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `connection_string` | PostgreSQL connection string (required) | None |
|
||||
@@ -43,6 +124,17 @@ Here are the parameters available for configuring Supabase:
|
||||
| `embedding_model_dims` | Dimensions of the embedding model | `1536` |
|
||||
| `index_method` | Vector index method to use | `auto` |
|
||||
| `index_measure` | Distance measure for similarity search | `cosine_distance` |
|
||||
</Tab>
|
||||
<Tab title="TypeScript">
|
||||
| Parameter | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `collectionName` | Name for the vector collection | `mem0` |
|
||||
| `embeddingModelDims` | Dimensions of the embedding model | `1536` |
|
||||
| `supabaseUrl` | Supabase URL | None |
|
||||
| `supabaseKey` | Supabase key | None |
|
||||
| `tableName` | Name for the vector table | `memories` |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Index Methods
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
[Upstash Vector](https://upstash.com/docs/vector) is a serverless vector database with built-in embedding models.
|
||||
|
||||
### Usage with Upstash embeddings
|
||||
|
||||
You can enable the built-in embedding models by setting `enable_embeddings` to `True`. This allows you to use Upstash's embedding models for vectorization.
|
||||
|
||||
```python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
os.environ["UPSTASH_VECTOR_REST_URL"] = "..."
|
||||
os.environ["UPSTASH_VECTOR_REST_TOKEN"] = "..."
|
||||
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "upstash_vector",
|
||||
"enable_embeddings": True,
|
||||
}
|
||||
}
|
||||
|
||||
m = Memory.from_config(config)
|
||||
m.add("Likes to play cricket on weekends", user_id="alice", metadata={"category": "hobbies"})
|
||||
```
|
||||
|
||||
<Note>
|
||||
Setting `enable_embeddings` to `True` will bypass any external embedding provider you have configured.
|
||||
</Note>
|
||||
|
||||
### Usage with external embedding providers
|
||||
|
||||
```python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "..."
|
||||
os.environ["UPSTASH_VECTOR_REST_URL"] = "..."
|
||||
os.environ["UPSTASH_VECTOR_REST_TOKEN"] = "..."
|
||||
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "upstash_vector",
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "text-embedding-3-large"
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
m = Memory.from_config(config)
|
||||
m.add("Likes to play cricket on weekends", user_id="alice", metadata={"category": "hobbies"})
|
||||
```
|
||||
|
||||
### Config
|
||||
|
||||
Here are the parameters available for configuring Upstash Vector:
|
||||
|
||||
| Parameter | Description | Default Value |
|
||||
| ------------------- | ---------------------------------- | ------------- |
|
||||
| `url` | URL for the Upstash Vector index | `None` |
|
||||
| `token` | Token for the Upstash Vector index | `None` |
|
||||
| `client` | An `upstash_vector.Index` instance | `None` |
|
||||
| `collection_name` | The default namespace used | `""` |
|
||||
| `enable_embeddings` | Whether to use Upstash embeddings | `False` |
|
||||
|
||||
<Note>
|
||||
When `url` and `token` are not provided, the `UPSTASH_VECTOR_REST_URL` and
|
||||
`UPSTASH_VECTOR_REST_TOKEN` environment variables are used.
|
||||
</Note>
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
## Google Cloud Vertex AI Vector Search
|
||||
---
|
||||
title: Vertex AI Vector Search
|
||||
---
|
||||
|
||||
|
||||
### Usage
|
||||
@@ -18,14 +18,17 @@ See the list of supported vector databases below.
|
||||
<Card title="Qdrant" href="/components/vectordbs/dbs/qdrant"></Card>
|
||||
<Card title="Chroma" href="/components/vectordbs/dbs/chroma"></Card>
|
||||
<Card title="Pgvector" href="/components/vectordbs/dbs/pgvector"></Card>
|
||||
<Card title="Upstash Vector" href="/components/vectordbs/dbs/upstash-vector"></Card>
|
||||
<Card title="Milvus" href="/components/vectordbs/dbs/milvus"></Card>
|
||||
<Card title="Azure AI Search" href="/components/vectordbs/dbs/azure_ai_search"></Card>
|
||||
<Card title="Pinecone" href="/components/vectordbs/dbs/pinecone"></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
|
||||
|
||||
+48
-8
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -129,13 +131,15 @@
|
||||
"components/vectordbs/dbs/chroma",
|
||||
"components/vectordbs/dbs/pgvector",
|
||||
"components/vectordbs/dbs/milvus",
|
||||
"components/vectordbs/dbs/azure_ai_search",
|
||||
"components/vectordbs/dbs/pinecone",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -155,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"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -182,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",
|
||||
@@ -192,7 +200,9 @@
|
||||
"examples/multimodal-demo",
|
||||
"examples/personalized-deep-research",
|
||||
"examples/mem0-agentic-tool",
|
||||
"examples/openai-inbuilt-tools"
|
||||
"examples/openai-inbuilt-tools",
|
||||
"examples/mem0-openai-voice-demo",
|
||||
"examples/email_processing"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -206,6 +216,7 @@
|
||||
"pages": [
|
||||
"integrations/overview",
|
||||
"integrations/vercel-ai-sdk",
|
||||
"integrations/flowise",
|
||||
"integrations/crewai",
|
||||
"integrations/autogen",
|
||||
"integrations/langchain",
|
||||
@@ -213,7 +224,10 @@
|
||||
"integrations/llama-index",
|
||||
"integrations/langchain-tools",
|
||||
"integrations/dify",
|
||||
"integrations/mcp-server"
|
||||
"integrations/mcp-server",
|
||||
"integrations/livekit",
|
||||
"integrations/elevenlabs",
|
||||
"integrations/pipecat"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -244,7 +258,8 @@
|
||||
"api-reference/memory/batch-delete",
|
||||
"api-reference/memory/delete-memories",
|
||||
"api-reference/memory/create-memory-export",
|
||||
"api-reference/memory/get-memory-export"
|
||||
"api-reference/memory/get-memory-export",
|
||||
"api-reference/memory/feedback"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -267,6 +282,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",
|
||||
@@ -280,6 +307,19 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Changelog",
|
||||
"icon": "clock",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Product Updates",
|
||||
"icon": "rocket",
|
||||
"pages": [
|
||||
"changelog/overview"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,538 @@
|
||||
---
|
||||
title: 'Mem0 with OpenAI Agents SDK for Voice'
|
||||
description: 'Integrate memory capabilities into your voice agents using Mem0 and OpenAI Agents SDK'
|
||||
---
|
||||
|
||||
# Building Voice Agents with Memory using Mem0 and OpenAI Agents SDK
|
||||
|
||||
This guide demonstrates how to combine OpenAI's Agents SDK for voice applications with Mem0's memory capabilities to create a voice assistant that remembers user preferences and past interactions.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, make sure you have:
|
||||
|
||||
1. Installed OpenAI Agents SDK with voice dependencies:
|
||||
```bash
|
||||
pip install 'openai-agents[voice]'
|
||||
```
|
||||
|
||||
2. Installed Mem0 SDK:
|
||||
```bash
|
||||
pip install mem0ai
|
||||
```
|
||||
|
||||
3. Installed other required dependencies:
|
||||
```bash
|
||||
pip install numpy sounddevice pydantic
|
||||
```
|
||||
|
||||
4. Set up your API keys:
|
||||
- OpenAI API key for the Agents SDK
|
||||
- Mem0 API key from the Mem0 Platform
|
||||
|
||||
## Code Breakdown
|
||||
|
||||
Let's break down the key components of this implementation:
|
||||
|
||||
### 1. Setting Up Dependencies and Environment
|
||||
|
||||
```python
|
||||
# OpenAI Agents SDK imports
|
||||
from agents import (
|
||||
Agent,
|
||||
function_tool
|
||||
)
|
||||
from agents.voice import (
|
||||
AudioInput,
|
||||
SingleAgentVoiceWorkflow,
|
||||
VoicePipeline
|
||||
)
|
||||
from agents.extensions.handoff_prompt import prompt_with_handoff_instructions
|
||||
|
||||
# Mem0 imports
|
||||
from mem0 import AsyncMemoryClient
|
||||
|
||||
# Set up API keys (replace with your actual keys)
|
||||
os.environ["OPENAI_API_KEY"] = "your-openai-api-key"
|
||||
os.environ["MEM0_API_KEY"] = "your-mem0-api-key"
|
||||
|
||||
# Define a global user ID for simplicity
|
||||
USER_ID = "voice_user"
|
||||
|
||||
# Initialize Mem0 client
|
||||
mem0_client = AsyncMemoryClient()
|
||||
```
|
||||
|
||||
This section handles:
|
||||
- Importing required modules from OpenAI Agents SDK and Mem0
|
||||
- Setting up environment variables for API keys
|
||||
- Defining a simple user identification system (using a global variable)
|
||||
- Initializing the Mem0 client that will handle memory operations
|
||||
|
||||
### 2. Memory Tools with Function Decorators
|
||||
|
||||
The `@function_tool` decorator transforms Python functions into callable tools for the OpenAI agent. Here are the key memory tools:
|
||||
|
||||
#### Storing User Memories
|
||||
|
||||
```python
|
||||
import logging
|
||||
|
||||
# Set up logging at the top of your file
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
force=True
|
||||
)
|
||||
logger = logging.getLogger("memory_voice_agent")
|
||||
|
||||
# Then use logger in your function tools
|
||||
@function_tool
|
||||
async def save_memories(
|
||||
memory: str
|
||||
) -> str:
|
||||
"""Store a user memory in memory."""
|
||||
# This will be visible in your console
|
||||
logger.debug(f"Saving memory: {memory} for user {USER_ID}")
|
||||
|
||||
# Store the preference in Mem0
|
||||
memory_content = f"User memory - {memory}"
|
||||
await mem0_client.add(
|
||||
memory_content,
|
||||
user_id=USER_ID,
|
||||
)
|
||||
|
||||
return f"I've saved your memory: {memory}"
|
||||
```
|
||||
|
||||
This function:
|
||||
- Takes a memory string
|
||||
- Creates a formatted memory string
|
||||
- Stores it in Mem0 using the `add()` method
|
||||
- Includes metadata to categorize the memory for easier retrieval
|
||||
- Returns a confirmation message that the agent will speak
|
||||
|
||||
#### Finding Relevant Memories
|
||||
|
||||
```python
|
||||
@function_tool
|
||||
async def search_memories(
|
||||
query: str
|
||||
) -> str:
|
||||
"""
|
||||
Find memories relevant to the current conversation.
|
||||
Args:
|
||||
query: The search query to find relevant memories
|
||||
"""
|
||||
print(f"Finding memories related to: {query}")
|
||||
results = await mem0_client.search(
|
||||
query,
|
||||
user_id=USER_ID,
|
||||
limit=5,
|
||||
threshold=0.7, # Higher threshold for more relevant results
|
||||
output_format="v1.1"
|
||||
)
|
||||
|
||||
# Format and return the results
|
||||
if not results.get('results', []):
|
||||
return "I don't have any relevant memories about this topic."
|
||||
|
||||
memories = [f"• {result['memory']}" for result in results.get('results', [])]
|
||||
return "Here's what I remember that might be relevant:\n" + "\n".join(memories)
|
||||
```
|
||||
|
||||
This tool:
|
||||
- Takes a search query string
|
||||
- Passes it to Mem0's semantic search to find related memories
|
||||
- Sets a threshold for relevance to ensure quality results
|
||||
- Returns a formatted list of relevant memories or a default message
|
||||
|
||||
### 3. Creating the Voice Agent
|
||||
|
||||
```python
|
||||
def create_memory_voice_agent():
|
||||
# Create the agent with memory-enabled tools
|
||||
agent = Agent(
|
||||
name="Memory Assistant",
|
||||
instructions=prompt_with_handoff_instructions(
|
||||
"""You're speaking to a human, so be polite and concise.
|
||||
Always respond in clear, natural English.
|
||||
You have the ability to remember information about the user.
|
||||
Use the save_memories tool when the user shares an important information worth remembering.
|
||||
Use the search_memories tool when you need context from past conversations or user asks you to recall something.
|
||||
""",
|
||||
),
|
||||
model="gpt-4o",
|
||||
tools=[save_memories, search_memories],
|
||||
)
|
||||
|
||||
return agent
|
||||
```
|
||||
|
||||
This function:
|
||||
- Creates an OpenAI Agent with specific instructions
|
||||
- Configures it to use gpt-4o (you can use other models)
|
||||
- Registers the memory-related tools with the agent
|
||||
- Uses `prompt_with_handoff_instructions` to include standard voice agent behaviors
|
||||
|
||||
### 4. Microphone Recording Functionality
|
||||
|
||||
```python
|
||||
async def record_from_microphone(duration=5, samplerate=24000):
|
||||
"""Record audio from the microphone for a specified duration."""
|
||||
print(f"Recording for {duration} seconds...")
|
||||
|
||||
# Create a buffer to store the recorded audio
|
||||
frames = []
|
||||
|
||||
# Callback function to store audio data
|
||||
def callback(indata, frames_count, time_info, status):
|
||||
frames.append(indata.copy())
|
||||
|
||||
# Start recording
|
||||
with sd.InputStream(samplerate=samplerate, channels=1, callback=callback, dtype=np.int16):
|
||||
await asyncio.sleep(duration)
|
||||
|
||||
# Combine all frames into a single numpy array
|
||||
audio_data = np.concatenate(frames)
|
||||
return audio_data
|
||||
```
|
||||
|
||||
This function:
|
||||
- Creates a simple asynchronous microphone recording function
|
||||
- Uses the sounddevice library to capture audio input
|
||||
- Stores frames in a buffer during recording
|
||||
- Combines frames into a single numpy array when complete
|
||||
- Returns the audio data for processing
|
||||
|
||||
### 5. Main Loop and Voice Processing
|
||||
|
||||
```python
|
||||
async def main():
|
||||
# Create the agent
|
||||
agent = create_memory_voice_agent()
|
||||
|
||||
# Set up the voice pipeline
|
||||
pipeline = VoicePipeline(
|
||||
workflow=SingleAgentVoiceWorkflow(agent)
|
||||
)
|
||||
|
||||
# Configure TTS settings
|
||||
pipeline.config.tts_settings.voice = "alloy"
|
||||
pipeline.config.tts_settings.speed = 1.0
|
||||
|
||||
try:
|
||||
while True:
|
||||
# Get user input
|
||||
print("\nPress Enter to start recording (or 'q' to quit)...")
|
||||
user_input = input()
|
||||
if user_input.lower() == 'q':
|
||||
break
|
||||
|
||||
# Record and process audio
|
||||
audio_data = await record_from_microphone(duration=5)
|
||||
audio_input = AudioInput(buffer=audio_data)
|
||||
result = await pipeline.run(audio_input)
|
||||
|
||||
# Play response and handle events
|
||||
player = sd.OutputStream(samplerate=24000, channels=1, dtype=np.int16)
|
||||
player.start()
|
||||
|
||||
agent_response = ""
|
||||
print("\nAgent response:")
|
||||
|
||||
async for event in result.stream():
|
||||
if event.type == "voice_stream_event_audio":
|
||||
player.write(event.data)
|
||||
elif event.type == "voice_stream_event_content":
|
||||
content = event.data
|
||||
agent_response += content
|
||||
print(content, end="", flush=True)
|
||||
|
||||
# Save the agent's response to memory
|
||||
if agent_response:
|
||||
try:
|
||||
await mem0_client.add(
|
||||
f"Agent response: {agent_response}",
|
||||
user_id=USER_ID,
|
||||
metadata={"type": "agent_response"}
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Failed to store memory: {e}")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\nExiting...")
|
||||
```
|
||||
|
||||
This main function orchestrates the entire process:
|
||||
1. Creates the memory-enabled voice agent
|
||||
2. Sets up the voice pipeline with TTS settings
|
||||
3. Implements an interactive loop for recording and processing voice input
|
||||
4. Handles streaming of response events (both audio and text)
|
||||
5. Automatically saves the agent's responses to memory
|
||||
6. Includes proper error handling and exit mechanisms
|
||||
|
||||
## 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 os
|
||||
import logging
|
||||
from typing import Optional, List, Dict, Any
|
||||
import numpy as np
|
||||
import sounddevice as sd
|
||||
from pydantic import BaseModel
|
||||
|
||||
# OpenAI Agents SDK imports
|
||||
from agents import (
|
||||
Agent,
|
||||
function_tool
|
||||
)
|
||||
from agents.voice import (
|
||||
AudioInput,
|
||||
SingleAgentVoiceWorkflow,
|
||||
VoicePipeline
|
||||
)
|
||||
from agents.extensions.handoff_prompt import prompt_with_handoff_instructions
|
||||
|
||||
# Mem0 imports
|
||||
from mem0 import AsyncMemoryClient
|
||||
|
||||
# Set up API keys (replace with your actual keys)
|
||||
os.environ["OPENAI_API_KEY"] = "your-openai-api-key"
|
||||
os.environ["MEM0_API_KEY"] = "your-mem0-api-key"
|
||||
|
||||
# Define a global user ID for simplicity
|
||||
USER_ID = "voice_user"
|
||||
|
||||
# Initialize Mem0 client
|
||||
mem0_client = AsyncMemoryClient()
|
||||
|
||||
# Create tools that utilize Mem0's memory
|
||||
@function_tool
|
||||
async def save_memories(
|
||||
memory: str
|
||||
) -> str:
|
||||
"""
|
||||
Store a user memory in memory.
|
||||
Args:
|
||||
memory: The memory to save
|
||||
"""
|
||||
print(f"Saving memory: {memory} for user {USER_ID}")
|
||||
|
||||
# Store the preference in Mem0
|
||||
memory_content = f"User memory - {memory}"
|
||||
await mem0_client.add(
|
||||
memory_content,
|
||||
user_id=USER_ID,
|
||||
)
|
||||
|
||||
return f"I've saved your memory: {memory}"
|
||||
|
||||
@function_tool
|
||||
async def search_memories(
|
||||
query: str
|
||||
) -> str:
|
||||
"""
|
||||
Find memories relevant to the current conversation.
|
||||
Args:
|
||||
query: The search query to find relevant memories
|
||||
"""
|
||||
print(f"Finding memories related to: {query}")
|
||||
results = await mem0_client.search(
|
||||
query,
|
||||
user_id=USER_ID,
|
||||
limit=5,
|
||||
threshold=0.7, # Higher threshold for more relevant results
|
||||
output_format="v1.1"
|
||||
)
|
||||
|
||||
# Format and return the results
|
||||
if not results.get('results', []):
|
||||
return "I don't have any relevant memories about this topic."
|
||||
|
||||
memories = [f"• {result['memory']}" for result in results.get('results', [])]
|
||||
return "Here's what I remember that might be relevant:\n" + "\n".join(memories)
|
||||
|
||||
# Create the agent with memory-enabled tools
|
||||
def create_memory_voice_agent():
|
||||
# Create the agent with memory-enabled tools
|
||||
agent = Agent(
|
||||
name="Memory Assistant",
|
||||
instructions=prompt_with_handoff_instructions(
|
||||
"""You're speaking to a human, so be polite and concise.
|
||||
Always respond in clear, natural English.
|
||||
You have the ability to remember information about the user.
|
||||
Use the save_memories tool when the user shares an important information worth remembering.
|
||||
Use the search_memories tool when you need context from past conversations or user asks you to recall something.
|
||||
""",
|
||||
),
|
||||
model="gpt-4o",
|
||||
tools=[save_memories, search_memories],
|
||||
)
|
||||
|
||||
return agent
|
||||
|
||||
async def record_from_microphone(duration=5, samplerate=24000):
|
||||
"""Record audio from the microphone for a specified duration."""
|
||||
print(f"Recording for {duration} seconds...")
|
||||
|
||||
# Create a buffer to store the recorded audio
|
||||
frames = []
|
||||
|
||||
# Callback function to store audio data
|
||||
def callback(indata, frames_count, time_info, status):
|
||||
frames.append(indata.copy())
|
||||
|
||||
# Start recording
|
||||
with sd.InputStream(samplerate=samplerate, channels=1, callback=callback, dtype=np.int16):
|
||||
await asyncio.sleep(duration)
|
||||
|
||||
# Combine all frames into a single numpy array
|
||||
audio_data = np.concatenate(frames)
|
||||
return audio_data
|
||||
|
||||
async def main():
|
||||
print("Starting Memory Voice Agent")
|
||||
|
||||
# Create the agent and context
|
||||
agent = create_memory_voice_agent()
|
||||
|
||||
# Set up the voice pipeline
|
||||
pipeline = VoicePipeline(
|
||||
workflow=SingleAgentVoiceWorkflow(agent)
|
||||
)
|
||||
|
||||
# Configure TTS settings
|
||||
pipeline.config.tts_settings.voice = "alloy"
|
||||
pipeline.config.tts_settings.speed = 1.0
|
||||
|
||||
try:
|
||||
while True:
|
||||
# Get user input
|
||||
print("\nPress Enter to start recording (or 'q' to quit)...")
|
||||
user_input = input()
|
||||
if user_input.lower() == 'q':
|
||||
break
|
||||
|
||||
# Record and process audio
|
||||
audio_data = await record_from_microphone(duration=5)
|
||||
audio_input = AudioInput(buffer=audio_data)
|
||||
|
||||
print("Processing your request...")
|
||||
|
||||
# Process the audio input
|
||||
result = await pipeline.run(audio_input)
|
||||
|
||||
# Create an audio player
|
||||
player = sd.OutputStream(samplerate=24000, channels=1, dtype=np.int16)
|
||||
player.start()
|
||||
|
||||
# Store the agent's response for adding to memory
|
||||
agent_response = ""
|
||||
|
||||
print("\nAgent response:")
|
||||
# Play the audio stream as it comes in
|
||||
async for event in result.stream():
|
||||
if event.type == "voice_stream_event_audio":
|
||||
player.write(event.data)
|
||||
elif event.type == "voice_stream_event_content":
|
||||
# Accumulate and print the text response
|
||||
content = event.data
|
||||
agent_response += content
|
||||
print(content, end="", flush=True)
|
||||
|
||||
print("\n")
|
||||
|
||||
# Example of saving the conversation to Mem0 after completion
|
||||
if agent_response:
|
||||
try:
|
||||
await mem0_client.add(
|
||||
f"Agent response: {agent_response}",
|
||||
user_id=USER_ID,
|
||||
metadata={"type": "agent_response"}
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Failed to store memory: {e}")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\nExiting...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Key Features of This Implementation
|
||||
|
||||
This implementation offers several key features:
|
||||
|
||||
1. **Simplified User Management**: Uses a global `USER_ID` variable for simplicity, but can be extended to manage multiple users.
|
||||
|
||||
2. **Real Microphone Input**: Includes a `record_from_microphone()` function that captures actual voice input from your microphone.
|
||||
|
||||
3. **Interactive Voice Loop**: Implements a continuous interaction loop, allowing for multiple back-and-forth exchanges.
|
||||
|
||||
4. **Memory Management Tools**:
|
||||
- `save_memories`: Stores user memories in Mem0
|
||||
- `search_memories`: Searches for relevant past information
|
||||
|
||||
5. **Voice Configuration**: Demonstrates how to configure TTS settings for the voice response.
|
||||
|
||||
## Running the Example
|
||||
|
||||
To run this example:
|
||||
|
||||
1. Replace the placeholder API keys with your actual keys
|
||||
2. Make sure your microphone is properly connected
|
||||
3. Run the script with Python 3.8 or newer
|
||||
4. Press Enter to start recording, then speak your request
|
||||
5. Press 'q' to quit the application
|
||||
|
||||
The agent will listen to your request, process it through the OpenAI model, utilize Mem0 for memory operations as needed, and respond both through text output and voice speech.
|
||||
|
||||
## Best Practices for Voice Agents with Memory
|
||||
|
||||
1. **Optimizing Memory for Voice**: Keep memories concise and relevant for voice responses.
|
||||
|
||||
2. **Forgetting Mechanism**: Implement a way to delete or expire memories that are no longer relevant.
|
||||
|
||||
3. **Context Preservation**: Store enough context with each memory to make retrieval effective.
|
||||
|
||||
4. **Error Handling**: Implement robust error handling for memory operations, as voice interactions should continue smoothly even if memory operations fail.
|
||||
|
||||
## Conclusion
|
||||
|
||||
By combining OpenAI's Agents SDK with Mem0's memory capabilities, you can create voice agents that maintain persistent memory of user preferences and past interactions. This significantly enhances the user experience by making conversations more natural and personalized.
|
||||
|
||||
As you build your voice application, experiment with different memory strategies and filtering approaches to find the optimal balance between comprehensive memory and efficient retrieval for your specific use case.
|
||||
|
||||
## Debugging Function Tools
|
||||
|
||||
When working with the OpenAI Agents SDK, you might notice that regular `print()` statements inside `@function_tool` decorated functions don't appear in your console output. This is because the Agents SDK captures and redirects standard output when executing these functions.
|
||||
|
||||
To effectively debug your function tools, use Python's `logging` module instead:
|
||||
|
||||
```python
|
||||
import logging
|
||||
|
||||
# Set up logging at the top of your file
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
force=True
|
||||
)
|
||||
logger = logging.getLogger("memory_voice_agent")
|
||||
|
||||
# Then use logger in your function tools
|
||||
@function_tool
|
||||
async def save_memories(
|
||||
memory: str
|
||||
) -> str:
|
||||
"""Store a user memory in memory."""
|
||||
# This will be visible in your console
|
||||
logger.debug(f"Saving memory: {memory} for user {USER_ID}")
|
||||
|
||||
# Rest of your function...
|
||||
```
|
||||
@@ -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>
|
||||
|
||||
@@ -66,4 +66,12 @@ Explore how **Mem0** can power real-world applications and bring personalized, i
|
||||
<Card title="OpenAI Inbuilt Tools" icon="robot" href="/examples/openai-inbuilt-tools">
|
||||
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="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 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ You can give feedback on a memory by calling the `feedback` method on the Mem0 c
|
||||
<CodeGroup>
|
||||
|
||||
```python Python
|
||||
from mem0 import Mem0
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = Mem0(api_key="your_api_key")
|
||||
client = MemoryClient(api_key="your_api_key")
|
||||
|
||||
client.feedback(memory_id="your-memory-id", feedback="NEGATIVE", feedback_reason="I don't like this memory because it is not relevant.")
|
||||
```
|
||||
|
||||
@@ -91,10 +91,17 @@ export_instructions = """
|
||||
5. Clearly distinguish between factual statements and inferences
|
||||
"""
|
||||
|
||||
# For create operation, using only user_id filter as requested
|
||||
filters = {
|
||||
"AND": [
|
||||
{"user_id": "alex"}
|
||||
]
|
||||
}
|
||||
|
||||
response = client.create_memory_export(
|
||||
schema=json_schema,
|
||||
user_id="alice",
|
||||
export_instructions=export_instructions
|
||||
filters=filters,
|
||||
export_instructions=export_instructions # Optional
|
||||
)
|
||||
|
||||
print(response)
|
||||
@@ -127,7 +134,15 @@ Once the export job is complete, you can retrieve the structured data:
|
||||
<CodeGroup>
|
||||
|
||||
```python Python
|
||||
response = client.get_memory_export(user_id="alice")
|
||||
# Corrected date range (assuming you meant July 10 to July 20)
|
||||
filters = {
|
||||
"AND": [
|
||||
{"created_at": {"gte": "2024-07-10", "lte": "2024-07-20"}},
|
||||
{"user_id": "alex"}
|
||||
]
|
||||
}
|
||||
|
||||
response = client.get_memory_export(filters=filters)
|
||||
print(response)
|
||||
```
|
||||
|
||||
@@ -157,6 +172,7 @@ You can apply various filters to customize which memories are included in the ex
|
||||
- `agent_id`: Filter memories by specific agent
|
||||
- `run_id`: Filter memories by specific run
|
||||
- `session_id`: Filter memories by specific session
|
||||
- `created_at`: Filter memories by date
|
||||
|
||||
<Note>
|
||||
The export process may take some time to complete, especially when dealing with a large number of memories or complex schemas.
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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" />
|
||||
@@ -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**.
|
||||
|
||||

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

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

|
||||
|
||||
### 2. Memory Retention
|
||||
|
||||
Validate memory persistence:
|
||||
|
||||
1. Clear the chat history in Flowise
|
||||
2. Ask a question about previously stored information
|
||||
3. Confirm that the AI remembers the context
|
||||
|
||||

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

|
||||
|
||||
Available settings include:
|
||||
|
||||
1. **Search Only Mode**: Enable memory retrieval without creating new memories
|
||||
2. **Mem0 Entities**: Configure identifiers:
|
||||
- `user_id`: Unique identifier for each user
|
||||
- `run_id`: Specific conversation session ID
|
||||
- `app_id`: Application identifier
|
||||
- `agent_id`: AI agent identifier
|
||||
3. **Project ID**: Assign memories to specific projects
|
||||
4. **Organization ID**: Organize memories by organization
|
||||
|
||||
### Platform Configuration
|
||||
|
||||
Additional settings available in [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
|
||||
|
||||

|
||||
|
||||
## 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" />
|
||||
@@ -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")
|
||||
```
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -301,6 +301,56 @@ await memory.deleteAll({ userId: "alice" });
|
||||
await memory.reset(); // Reset all memories
|
||||
```
|
||||
|
||||
### History Store
|
||||
|
||||
Mem0 TypeScript SDK support history stores to run on a serverless environment:
|
||||
|
||||
We recommend using `Supabase` as a history store for serverless environments or disable history store to run on a serverless environment.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript Supabase
|
||||
import { Memory } from 'mem0ai/oss';
|
||||
|
||||
const memory = new Memory({
|
||||
historyStore: {
|
||||
provider: 'supabase',
|
||||
config: {
|
||||
supabaseUrl: process.env.SUPABASE_URL || '',
|
||||
supabaseKey: process.env.SUPABASE_KEY || '',
|
||||
tableName: 'memory_history',
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```typescript Disable History
|
||||
import { Memory } from 'mem0ai/oss';
|
||||
|
||||
const memory = new Memory({
|
||||
disableHistory: true,
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Mem0 uses SQLite as a default history store.
|
||||
|
||||
#### Create Memory History Table in Supabase
|
||||
|
||||
You may need to create a memory history table in Supabase to store the history of memories. Use the following SQL command in `SQL Editor` on the Supabase project dashboard to create a memory history table:
|
||||
|
||||
```sql
|
||||
create table memory_history (
|
||||
id text primary key,
|
||||
memory_id text not null,
|
||||
previous_value text,
|
||||
new_value text,
|
||||
action text not null,
|
||||
created_at timestamp with time zone default timezone('utc', now()),
|
||||
updated_at timestamp with time zone,
|
||||
is_deleted integer default 0
|
||||
);
|
||||
```
|
||||
|
||||
## Configuration Parameters
|
||||
|
||||
Mem0 offers extensive configuration options to customize its behavior according to your needs. These configurations span across different components like vector stores, language models, embedders, and graph stores.
|
||||
@@ -352,6 +402,14 @@ Mem0 offers extensive configuration options to customize its behavior according
|
||||
| `customPrompt` | Custom prompt for memory processing | None |
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="History Table Configuration">
|
||||
| Parameter | Description | Default |
|
||||
|------------------|--------------------------------------|----------------------------|
|
||||
| `provider` | History store provider | "sqlite" |
|
||||
| `config` | History store configuration | None (Defaults to SQLite) |
|
||||
| `disableHistory` | Disable history store | false |
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Complete Configuration Example">
|
||||
```typescript
|
||||
const config = {
|
||||
@@ -377,7 +435,15 @@ const config = {
|
||||
model: 'gpt-4-turbo-preview',
|
||||
},
|
||||
},
|
||||
historyDbPath: 'memory.db',
|
||||
historyStore: {
|
||||
provider: 'supabase',
|
||||
config: {
|
||||
supabaseUrl: process.env.SUPABASE_URL || '',
|
||||
supabaseKey: process.env.SUPABASE_KEY || '',
|
||||
tableName: 'memories',
|
||||
},
|
||||
},
|
||||
disableHistory: false, // This is false by default
|
||||
customPrompt: "I'm a virtual assistant. I'm here to help you with your queries.",
|
||||
}
|
||||
```
|
||||
|
||||
+273
-163
@@ -379,7 +379,118 @@
|
||||
}
|
||||
},
|
||||
"/v1/exports/": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"exports"
|
||||
],
|
||||
"summary": "Create an export job with schema",
|
||||
"description": "Create a structured export of memories based on a provided schema.",
|
||||
"operationId": "exports_create",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["schema"],
|
||||
"properties": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"description": "Schema definition for the export"
|
||||
},
|
||||
"filters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user_id": {"type": "string"},
|
||||
"agent_id": {"type": "string"},
|
||||
"app_id": {"type": "string"},
|
||||
"run_id": {"type": "string"}
|
||||
},
|
||||
"description": "Filters to apply while exporting memories. Available fields are: user_id, agent_id, app_id, run_id."
|
||||
},
|
||||
"org_id": {
|
||||
"type": "string",
|
||||
"description": "Filter exports by organization ID"
|
||||
},
|
||||
"project_id": {
|
||||
"type": "string",
|
||||
"description": "Filter exports by project ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Export created successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"example": "Memory export request received. The export will be ready in a few seconds."
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"example": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
},
|
||||
"required": ["message", "id"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"example": "Schema is required and must be a valid object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\njson_schema = {pydantic_json_schema}\nfilters = {\n \"AND\": [\n {\"user_id\": \"alex\"}\n ]\n}\n\nresponse = client.create_memory_export(\n schema=json_schema,\n filters=filters\n)\nprint(response)"
|
||||
},
|
||||
{
|
||||
"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 jsonSchema = {pydantic_json_schema};\nconst filters = {\n AND: [\n {user_id: 'alex'}\n ]\n};\n\nclient.createMemoryExport({\n schema: jsonSchema,\n filters: filters\n})\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl --request POST \\\n --url 'https://api.mem0.ai/v1/exports/' \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"schema\": {pydantic_json_schema},\n \"filters\": {\n \"AND\": [\n {\"user_id\": \"alex\"}\n ]\n }\n }'"
|
||||
},
|
||||
{
|
||||
"lang": "Go",
|
||||
"source": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\turl := \"https://api.mem0.ai/v1/exports/\"\n\n\tfilters := map[string]interface{}{\n\t\t\"AND\": []map[string]interface{}{\n\t\t\t{\"user_id\": \"alex\"},\n\t\t},\n\t}\n\n\tdata := map[string]interface{}{\n\t\t\"schema\": map[string]interface{}{}, // Your schema here\n\t\t\"filters\": filters,\n\t}\n\n\tjsonData, _ := json.Marshal(data)\n\n\treq, _ := http.NewRequest(\"POST\", url, bytes.NewBuffer(jsonData))\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(string(body))\n}"
|
||||
},
|
||||
{
|
||||
"lang": "PHP",
|
||||
"source": "<?php\n\n$curl = curl_init();\n\n$filters = [\n 'AND' => [\n ['user_id' => 'alex']\n ]\n];\n\n$data = array(\n \"schema\" => array(), // Your schema here\n \"filters\" => $filters\n);\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/v1/exports/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"POST\",\n CURLOPT_POSTFIELDS => json_encode($data),\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
},
|
||||
{
|
||||
"lang": "Java",
|
||||
"source": "import com.mashape.unirest.http.HttpResponse;\nimport com.mashape.unirest.http.JsonNode;\nimport com.mashape.unirest.http.Unirest;\nimport org.json.JSONObject;\nimport org.json.JSONArray;\n\nJSONObject filters = new JSONObject()\n .put(\"AND\", new JSONArray()\n .put(new JSONObject().put(\"user_id\", \"alex\")));\n\nJSONObject data = new JSONObject()\n .put(\"schema\", new JSONObject()) // Your schema here\n .put(\"filters\", filters);\n\nHttpResponse<JsonNode> response = Unirest.post(\"https://api.mem0.ai/v1/exports/\")\n .header(\"Authorization\", \"Token <api-key>\")\n .header(\"Content-Type\", \"application/json\")\n .body(data.toString())\n .asJson();"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/exports/get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"exports"
|
||||
],
|
||||
@@ -388,36 +499,20 @@
|
||||
"operationId": "exports_list",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Filter exports by user ID"
|
||||
},
|
||||
{
|
||||
"name": "run_id",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Filter exports by run ID"
|
||||
},
|
||||
{
|
||||
"name": "session_id",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Filter exports by session ID"
|
||||
},
|
||||
{
|
||||
"name": "app_id",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Filter exports by app ID"
|
||||
"name": "filters",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user_id": {"type": "string"},
|
||||
"agent_id": {"type": "string"},
|
||||
"app_id": {"type": "string"},
|
||||
"run_id": {"type": "string"},
|
||||
"created_at": {"type": "string"},
|
||||
"updated_at": {"type": "string"}
|
||||
},
|
||||
"description": "Filters to apply while exporting memories. Available fields are: user_id, agent_id, app_id, run_id, created_at, updated_at."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "org_id",
|
||||
@@ -484,145 +579,30 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"project_id\")\n\nresponse = client.get_memory_export(user_id=\"your_user_id\")\nprint(response)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"project_id\")\n\nfilters = {\n \"AND\": [\n {\"created_at\": {\"gte\": \"2024-07-10\", \"lte\": \"2024-07-20\"}},\n {\"user_id\": \"alex\"}\n ]\n}\n\nresponse = client.get_memory_export(filters=filters)\nprint(response)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\n// Get memory export\nclient.getMemoryExport({ user_id: \"your_user_id\" })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
"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 filters = {\n AND: [\n {created_at: {gte: \"2024-07-10\", lte: \"2024-07-20\"}},\n {user_id: \"alex\"}\n ]\n};\n\n// Get memory export\nclient.getMemoryExport({ filters })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl --request GET \\\n --url 'https://api.mem0.ai/v1/exports/?user_id=your_user_id' \\\n --header 'Authorization: Token <api-key>'"
|
||||
"source": "curl --request GET \\\n --url 'https://api.mem0.ai/v1/exports/?filters={\"AND\":[{\"created_at\":{\"gte\":\"2024-07-10\",\"lte\":\"2024-07-20\"}},{\"user_id\":\"alex\"}]}' \\\n --header 'Authorization: Token <api-key>'"
|
||||
},
|
||||
{
|
||||
"lang": "Go",
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\turl := \"https://api.mem0.ai/v1/exports/?user_id=your_user_id\"\n\n\treq, _ := http.NewRequest(\"GET\", url, nil)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(string(body))\n}"
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\tfilters := `{\"AND\":[{\"created_at\":{\"gte\":\"2024-07-10\",\"lte\":\"2024-07-20\"}},{\"user_id\":\"alex\"}]}`\n\turl := fmt.Sprintf(\"https://api.mem0.ai/v1/exports/?filters=%s\", filters)\n\n\treq, _ := http.NewRequest(\"GET\", url, nil)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(string(body))\n}"
|
||||
},
|
||||
{
|
||||
"lang": "PHP",
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/v1/exports/?user_id=your_user_id\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"GET\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
"source": "<?php\n\n$curl = curl_init();\n\n$filters = urlencode('{\"AND\":[{\"created_at\":{\"gte\":\"2024-07-10\",\"lte\":\"2024-07-20\"}},{\"user_id\":\"alex\"}]}');\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/v1/exports/?filters=\" . $filters,\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"GET\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
},
|
||||
{
|
||||
"lang": "Java",
|
||||
"source": "HttpResponse<String> response = Unirest.get(\"https://api.mem0.ai/v1/exports/?user_id=your_user_id\")\n .header(\"Authorization\", \"Token <api-key>\")\n .asString();"
|
||||
"source": "String filters = \"{\\\"AND\\\":[{\\\"created_at\\\":{\\\"gte\\\":\\\"2024-07-10\\\",\\\"lte\\\":\\\"2024-07-20\\\"}},{\\\"user_id\\\":\\\"alex\\\"}]}\";\n\nHttpResponse<String> response = Unirest.get(\"https://api.mem0.ai/v1/exports/?filters=\" + filters)\n .header(\"Authorization\", \"Token <api-key>\")\n .asString();"
|
||||
}
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"exports"
|
||||
],
|
||||
"summary": "Create an export job with schema",
|
||||
"description": "Create a structured export of memories based on a provided schema.",
|
||||
"operationId": "exports_create",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["schema"],
|
||||
"properties": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"description": "Schema definition for the export"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "string",
|
||||
"description": "Filter exports by user ID"
|
||||
},
|
||||
"run_id": {
|
||||
"type": "string",
|
||||
"description": "Filter exports by run ID"
|
||||
},
|
||||
"session_id": {
|
||||
"type": "string",
|
||||
"description": "Filter exports by session ID"
|
||||
},
|
||||
"app_id": {
|
||||
"type": "string",
|
||||
"description": "Filter exports by app ID"
|
||||
},
|
||||
"org_id": {
|
||||
"type": "string",
|
||||
"description": "Filter exports by organization ID"
|
||||
},
|
||||
"project_id": {
|
||||
"type": "string",
|
||||
"description": "Filter exports by project ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Export created successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"example": "Memory export request received. The export will be ready in a few seconds."
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"example": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
},
|
||||
"required": ["message", "id"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"example": "Schema is required and must be a valid object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\n\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\njson_schema = {pydantic_json_schema}\n\nresponse = client.create_memory_export(\n schema=json_schema,\n user_id=\"your_user_id\"\n)\nprint(response)"
|
||||
},
|
||||
{
|
||||
"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 jsonSchema = {pydantic_json_schema};\n\nclient.createMemoryExport({\n schema: jsonSchema,\n user_id: \"your_user_id\"\n})\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl --request POST \\\n --url 'https://api.mem0.ai/v1/exports/' \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"schema\": {pydantic_json_schema},\n \"user_id\": \"your_user_id\"\n }'"
|
||||
},
|
||||
{
|
||||
"lang": "Go",
|
||||
"source": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\turl := \"https://api.mem0.ai/v1/exports/\"\n\n\tdata := map[string]interface{}{\n\t\t\"schema\": map[string]interface{}{}, // Your schema here\n\t\t\"user_id\": \"user123\",\n\t}\n\n\tjsonData, _ := json.Marshal(data)\n\n\treq, _ := http.NewRequest(\"POST\", url, bytes.NewBuffer(jsonData))\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(string(body))\n}"
|
||||
},
|
||||
{
|
||||
"lang": "PHP",
|
||||
"source": "<?php\n\n$curl = curl_init();\n\n$data = array(\n \"schema\" => array(), // Your schema here\n \"user_id\" => \"your_user_id\"\n);\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/v1/exports/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"POST\",\n CURLOPT_POSTFIELDS => json_encode($data),\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
},
|
||||
{
|
||||
"lang": "Java",
|
||||
"source": "import com.mashape.unirest.http.HttpResponse;\nimport com.mashape.unirest.http.JsonNode;\nimport com.mashape.unirest.http.Unirest;\nimport org.json.JSONObject;\n\nJSONObject data = new JSONObject()\n .put(\"schema\", new JSONObject()) // Your schema here\n .put(\"user_id\", \"your_user_id\");\n\nHttpResponse<JsonNode> response = Unirest.post(\"https://api.mem0.ai/v1/exports/\")\n .header(\"Authorization\", \"Token <api-key>\")\n .header(\"Content-Type\", \"application/json\")\n .body(data.toString())\n .asJson();"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/memories/": {
|
||||
"get": {
|
||||
@@ -783,6 +763,14 @@
|
||||
"title": "Immutable",
|
||||
"default": false
|
||||
},
|
||||
"expiration_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD",
|
||||
"title": "Expiration date",
|
||||
"nullable": true,
|
||||
"default": null
|
||||
},
|
||||
"organization": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1183,6 +1171,14 @@
|
||||
"title": "Immutable",
|
||||
"default": false
|
||||
},
|
||||
"expiration_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD",
|
||||
"title": "Expiration date",
|
||||
"nullable": true,
|
||||
"default": null
|
||||
},
|
||||
"organization": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1324,6 +1320,14 @@
|
||||
"title": "Immutable",
|
||||
"default": false
|
||||
},
|
||||
"expiration_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD",
|
||||
"title": "Expiration date",
|
||||
"nullable": true,
|
||||
"default": null
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
@@ -1367,11 +1371,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",
|
||||
@@ -1451,6 +1455,14 @@
|
||||
"title": "Immutable",
|
||||
"default": false
|
||||
},
|
||||
"expiration_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD",
|
||||
"title": "Expiration date",
|
||||
"nullable": true,
|
||||
"default": null
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
@@ -1761,7 +1773,7 @@
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "const options = {\n method: 'PUT',\n headers: {\n 'Authorization': 'Token <api-key>',\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n text: 'Your updated memory text here'\n })\n};\n\nfetch('https://api.mem0.ai/v1/memories/{memory_id}/', options)\n .then(response => response.json())\n .then(response => console.log(response))\n .catch(err => console.error(err));"
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\n// Update a specific memory\nconst memory_id=<memory_id>\nconst message=\"Your updated memory message here\"\nclient.update(memory_id, message)\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
@@ -2074,6 +2086,95 @@
|
||||
"x-codegen-request-body-name": "data"
|
||||
}
|
||||
},
|
||||
"/v1/feedback/": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"feedback"
|
||||
],
|
||||
"description": "Submit feedback for a memory.",
|
||||
"operationId": "submit_feedback",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"required": [
|
||||
"memory_id"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"memory_id": {
|
||||
"type": "string",
|
||||
"description": "ID of the memory to provide feedback for"
|
||||
},
|
||||
"feedback": {
|
||||
"type": "string",
|
||||
"enum": ["POSITIVE", "NEGATIVE", "VERY_NEGATIVE"],
|
||||
"nullable": true,
|
||||
"description": "Type of feedback"
|
||||
},
|
||||
"feedback_reason": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Reason for the feedback"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful operation",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Feedback ID"
|
||||
},
|
||||
"feedback": {
|
||||
"type": "string",
|
||||
"enum": ["POSITIVE", "NEGATIVE", "VERY_NEGATIVE"],
|
||||
"nullable": true,
|
||||
"description": "Type of feedback"
|
||||
},
|
||||
"feedback_reason": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Reason for the feedback"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request"
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
},
|
||||
"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\")\n\n# Submit feedback for a memory\nfeedback = client.feedback(memory_id=\"memory_id\", feedback=\"POSITIVE\")\nprint(feedback)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm install mem0ai\n\nimport MemoryClient from 'mem0ai';\n\nconst client = new MemoryClient({ apiKey: 'your-api-key'});\n\nclient.feedback({\n memory_id: \"your-memory-id\", \n feedback: \"NEGATIVE\", \n feedback_reason: \"I don't like this memory because it is not relevant.\"\n})"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl --request POST \\\n --url https://api.mem0.ai/v1/feedback/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\"memory_id\": \"memory_id\", \"feedback\": \"POSITIVE\"}'"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/orgs/organizations/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -3112,7 +3213,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"type": "string",
|
||||
"example": "Unauthorized to create projects in this organization."
|
||||
}
|
||||
}
|
||||
@@ -3478,7 +3579,7 @@
|
||||
"description": "Unauthorized to modify this project",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -4069,7 +4170,7 @@
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl -X PUT \"https://api.mem0.ai/v1/memories/batch/\" \\\n -H \"Authorization: Token your-api-key\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"memories\": [\n {\n \"memory_id\": \"285ed74b-6e05-4043-b16b-3abd5b533496\",\n \"text\": \"Watches football\"\n },\n {\n \"memory_id\": \"2c9bd859-d1b7-4d33-a6b8-94e0147c4f07\",\n \"text\": \"Likes to travel\"\n }\n ]\n }'"
|
||||
"source": "curl -X PUT \"https://api.mem0.ai/v1/batch/\" \\\n -H \"Authorization: Token your-api-key\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"memories\": [\n {\n \"memory_id\": \"285ed74b-6e05-4043-b16b-3abd5b533496\",\n \"text\": \"Watches football\"\n },\n {\n \"memory_id\": \"2c9bd859-d1b7-4d33-a6b8-94e0147c4f07\",\n \"text\": \"Likes to travel\"\n }\n ]\n }'"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -4146,7 +4247,7 @@
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl -X DELETE \"https://api.mem0.ai/v1/memories/batch/\" \\\n -H \"Authorization: Token your-api-key\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"delete_memories\": [\n {\n \"memory_id\": \"285ed74b-6e05-4043-b16b-3abd5b533496\"\n },\n {\n \"memory_id\": \"2c9bd859-d1b7-4d33-a6b8-94e0147c4f07\"\n }\n ]\n }'"
|
||||
"source": "curl -X DELETE \"https://api.mem0.ai/v1/batch/\" \\\n -H \"Authorization: Token your-api-key\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"memories\": [\n {\n \"memory_id\": \"285ed74b-6e05-4043-b16b-3abd5b533496\"\n },\n {\n \"memory_id\": \"2c9bd859-d1b7-4d33-a6b8-94e0147c4f07\"\n }\n ]\n }'"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -4772,10 +4873,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.",
|
||||
@@ -4798,6 +4900,13 @@
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"expiration_date": {
|
||||
"description": "The date and time when the memory will expire. Format: YYYY-MM-DD",
|
||||
"title": "Expiration date",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"default": null
|
||||
},
|
||||
"org_id": {
|
||||
"description": "The unique identifier of the organization associated with this memory.",
|
||||
"title": "Organization id",
|
||||
@@ -4893,7 +5002,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",
|
||||
|
||||
+212
-344
@@ -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,27 +304,114 @@ 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>
|
||||
|
||||
#### Setting Memory Expiration Date
|
||||
|
||||
You can set an expiration date for memories, after which they will no longer be retrieved in searches. This is useful for creating temporary memories or memories that are only relevant for a specific time period.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Python
|
||||
import datetime
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I'll be in San Francisco until August 31st."
|
||||
}
|
||||
]
|
||||
|
||||
# Set an expiration date for this memory
|
||||
client.add(messages=messages, user_id="alex", expiration_date=str(datetime.datetime.now().date() + datetime.timedelta(days=30)))
|
||||
|
||||
# You can also use an explicit date string
|
||||
client.add(messages=messages, user_id="alex", expiration_date="2023-08-31")
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
const messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I'll be in San Francisco until August 31st."
|
||||
}
|
||||
];
|
||||
|
||||
// Set an expiration date 30 days from now
|
||||
const expirationDate = new Date();
|
||||
expirationDate.setDate(expirationDate.getDate() + 30);
|
||||
client.add(messages, {
|
||||
user_id: "alex",
|
||||
expiration_date: expirationDate.toISOString().split('T')[0]
|
||||
})
|
||||
.then(response => console.log(response))
|
||||
.catch(error => console.error(error));
|
||||
|
||||
// You can also use an explicit date string
|
||||
client.add(messages, {
|
||||
user_id: "alex",
|
||||
expiration_date: "2023-08-31"
|
||||
})
|
||||
.then(response => console.log(response))
|
||||
.catch(error => console.error(error));
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X POST "https://api.mem0.ai/v1/memories/" \
|
||||
-H "Authorization: Token your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I'll be in San Francisco until August 31st."
|
||||
}
|
||||
],
|
||||
"user_id": "alex",
|
||||
"expiration_date": "2023-08-31"
|
||||
}'
|
||||
```
|
||||
|
||||
```json Output
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"id": "a1b2c3d4-e5f6-4g7h-8i9j-k0l1m2n3o4p5",
|
||||
"data": {
|
||||
"memory": "In San Francisco until August 31st"
|
||||
},
|
||||
"event": "ADD"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<Note>
|
||||
Once a memory reaches its expiration date, it won't be included in search or get results.
|
||||
</Note>
|
||||
|
||||
#### Monitor Memories
|
||||
|
||||
@@ -436,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
|
||||
@@ -461,22 +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,
|
||||
"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": [
|
||||
{
|
||||
@@ -486,6 +463,7 @@ curl -X POST "https://api.mem0.ai/v1/memories/search/" \
|
||||
"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"
|
||||
}
|
||||
@@ -523,18 +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,
|
||||
"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>
|
||||
|
||||
@@ -613,18 +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,
|
||||
"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>
|
||||
|
||||
@@ -678,18 +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,
|
||||
"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>
|
||||
|
||||
@@ -752,18 +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,
|
||||
"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>
|
||||
@@ -847,46 +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,
|
||||
"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,
|
||||
"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":"是素食主义者,对坚果过敏。",
|
||||
@@ -894,6 +851,7 @@ curl -X GET "https://api.mem0.ai/v1/memories/?user_id=alex&page=1&page_size=50"
|
||||
"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
|
||||
@@ -905,13 +863,13 @@ curl -X GET "https://api.mem0.ai/v1/memories/?user_id=alex&page=1&page_size=50"
|
||||
"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)
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -936,46 +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,
|
||||
"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,
|
||||
"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": "是素食主义者,对坚果过敏。",
|
||||
@@ -983,6 +908,7 @@ curl -X GET "https://api.mem0.ai/v1/memories/?agent_id=ai-tutor&page=1&page_size
|
||||
"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"
|
||||
},
|
||||
@@ -993,12 +919,12 @@ curl -X GET "https://api.mem0.ai/v1/memories/?agent_id=ai-tutor&page=1&page_size
|
||||
"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"
|
||||
}
|
||||
... (remaining 48 memories)
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -1022,57 +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,
|
||||
"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,
|
||||
"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,
|
||||
"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.",
|
||||
@@ -1080,6 +962,7 @@ curl -X GET "https://api.mem0.ai/v1/memories/?user_id=alex&run_id=trip-planning-
|
||||
"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": ["food_preferences"]
|
||||
@@ -1091,6 +974,7 @@ curl -X GET "https://api.mem0.ai/v1/memories/?user_id=alex&run_id=trip-planning-
|
||||
"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": ["food_preferences"]
|
||||
@@ -1102,13 +986,13 @@ curl -X GET "https://api.mem0.ai/v1/memories/?user_id=alex&run_id=trip-planning-
|
||||
"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)
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -1141,6 +1025,7 @@ curl -X GET "https://api.mem0.ai/v1/memories/582bbe6d-506b-48c6-a4c6-5df3b1e6342
|
||||
"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": ["travel"]
|
||||
@@ -1221,6 +1106,7 @@ curl -X GET "https://api.mem0.ai/v1/memories/?user_id=alex&keywords=to play&page
|
||||
"hash": "d2088c936e259f2f5d2d75543d31401c",
|
||||
"metadata": null,
|
||||
"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": ["likes", "food_preferences"]
|
||||
@@ -1232,6 +1118,7 @@ curl -X GET "https://api.mem0.ai/v1/memories/?user_id=alex&keywords=to play&page
|
||||
"hash": "d2088c936e259f2f5d2d75543d31401c",
|
||||
"metadata": null,
|
||||
"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": ["likes"]
|
||||
@@ -1359,6 +1246,7 @@ curl -X GET "https://api.mem0.ai/v1/memories/?version=v2&page=1&page_size=50" \
|
||||
"hash":"62bc074f56d1f909f1b4c2b639f56f6a",
|
||||
"metadata":null,
|
||||
"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": ["food_preferences"]
|
||||
@@ -1379,6 +1267,7 @@ curl -X GET "https://api.mem0.ai/v1/memories/?version=v2&page=1&page_size=50" \
|
||||
"hash":"62bc074f56d1f909f1b4c2b639f56f6a",
|
||||
"metadata":null,
|
||||
"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": ["food_preferences"]
|
||||
@@ -1468,45 +1357,25 @@ curl -X GET "https://api.mem0.ai/v1/memories/?version=v2&page=1&page_size=50" \
|
||||
}'
|
||||
```
|
||||
|
||||
```json Output (Default)
|
||||
```json Output
|
||||
[
|
||||
{
|
||||
"id":"f38b689d-6b24-45b7-bced-17fbb4d8bac7",
|
||||
"memory":"Name: Alex. Vegetarian. Allergic to nuts.",
|
||||
"user_id":"alex",
|
||||
"hash":"62bc074f56d1f909f1b4c2b639f56f6a",
|
||||
"metadata":{"food":"vegan"},
|
||||
"immutable": false,
|
||||
"created_at":"2024-07-25T23:57:00.108347-07:00",
|
||||
"updated_at":"2024-07-25T23:57:00.108367-07:00",
|
||||
"categories": ["food_preferences"]
|
||||
}
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
```json Output (Paginated)
|
||||
{
|
||||
"count": 1,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id":"f38b689d-6b24-45b7-bced-17fbb4d8bac7",
|
||||
"memory":"Name: Alex. Vegetarian. Allergic to nuts.",
|
||||
"user_id":"alex",
|
||||
"hash":"62bc074f56d1f909f1b4c2b639f56f6a",
|
||||
"metadata":{"food":"vegan"},
|
||||
"immutable": false,
|
||||
"created_at":"2024-07-25T23:57:00.108347-07:00",
|
||||
"updated_at":"2024-07-25T23:57:00.108367-07:00",
|
||||
"categories": ["food_preferences"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
### 4.5 Memory History
|
||||
|
||||
Get history of how a memory has changed over time.
|
||||
@@ -1819,8 +1688,7 @@ response = client.batch_update(update_memories)
|
||||
print(response)
|
||||
```
|
||||
```javascript JavaScript
|
||||
const updateMemories = [
|
||||
{"memory_id": "285ed74b-6e05-4043-b16b-3abd5b533496",
|
||||
const updateMemories = [{"memory_id": "285ed74b-6e05-4043-b16b-3abd5b533496",
|
||||
text: "Watches football"
|
||||
},
|
||||
{"memory_id": "2c9bd859-d1b7-4d33-a6b8-94e0147c4f07",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
import warnings
|
||||
from typing import Optional
|
||||
|
||||
from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction
|
||||
@@ -16,7 +17,18 @@ class OpenAIEmbedder(BaseEmbedder):
|
||||
self.config.model = "text-embedding-ada-002"
|
||||
|
||||
api_key = self.config.api_key or os.environ["OPENAI_API_KEY"]
|
||||
api_base = self.config.api_base or os.environ.get("OPENAI_API_BASE")
|
||||
api_base = (
|
||||
self.config.api_base
|
||||
or os.environ.get("OPENAI_API_BASE")
|
||||
or os.getenv("OPENAI_BASE_URL")
|
||||
or "https://api.openai.com/v1"
|
||||
)
|
||||
if os.environ.get("OPENAI_API_BASE"):
|
||||
warnings.warn(
|
||||
"The environment variable 'OPENAI_API_BASE' is deprecated and will be removed in the 0.1.140. "
|
||||
"Please use 'OPENAI_BASE_URL' instead.",
|
||||
DeprecationWarning
|
||||
)
|
||||
|
||||
if api_key is None and os.getenv("OPENAI_ORGANIZATION") is None:
|
||||
raise ValueError("OPENAI_API_KEY or OPENAI_ORGANIZATION environment variables not provided") # noqa:E501
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
import os
|
||||
import warnings
|
||||
from typing import Any, Callable, Dict, Optional, Type, Union
|
||||
|
||||
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
|
||||
@@ -58,7 +59,19 @@ class OpenAILlm(BaseLlm):
|
||||
"model_kwargs": config.model_kwargs or {},
|
||||
}
|
||||
api_key = config.api_key or os.environ["OPENAI_API_KEY"]
|
||||
base_url = config.base_url or os.environ.get("OPENAI_API_BASE", None)
|
||||
base_url = (
|
||||
config.base_url
|
||||
or os.getenv("OPENAI_API_BASE")
|
||||
or os.getenv("OPENAI_BASE_URL")
|
||||
or "https://api.openai.com/v1"
|
||||
)
|
||||
if os.environ.get("OPENAI_API_BASE"):
|
||||
warnings.warn(
|
||||
"The environment variable 'OPENAI_API_BASE' is deprecated and will be removed in the 0.1.140. "
|
||||
"Please use 'OPENAI_BASE_URL' instead.",
|
||||
DeprecationWarning
|
||||
)
|
||||
|
||||
if config.top_p:
|
||||
kwargs["top_p"] = config.top_p
|
||||
if config.default_headers:
|
||||
|
||||
Generated
+564
-87
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "embedchain"
|
||||
version = "0.1.127"
|
||||
version = "0.1.128"
|
||||
description = "Simplest open source retrieval (RAG) framework"
|
||||
authors = [
|
||||
"Taranjeet Singh <taranjeet@embedchain.ai>",
|
||||
@@ -91,7 +91,7 @@ exclude = '''
|
||||
color = true
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<=3.13"
|
||||
python = ">=3.9,<=3.13.2"
|
||||
python-dotenv = "^1.0.0"
|
||||
langchain = "^0.3.1"
|
||||
requests = "^2.31.0"
|
||||
@@ -139,7 +139,7 @@ alembic = "^1.13.1"
|
||||
langchain-cohere = "^0.3.0"
|
||||
langchain-community = "^0.3.1"
|
||||
langchain-aws = {version = "^0.2.1", optional = true}
|
||||
langsmith = "^0.1.17"
|
||||
langsmith = "^0.3.18"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^23.3.0"
|
||||
|
||||
@@ -70,7 +70,7 @@ const retrieveMemories = (memories: any) => {
|
||||
export async function POST(req: Request) {
|
||||
const { messages, system, tools, userId } = await req.json();
|
||||
|
||||
const memories = await getMemories(messages, { user_id: userId });
|
||||
const memories = await getMemories(messages, { user_id: userId, rerank: true, threshold: 0.1 });
|
||||
const mem0Instructions = retrieveMemories(memories);
|
||||
|
||||
const result = streamText({
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "mem0-sdk-chat-bot",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mem0/vercel-ai-provider": "0.0.12",
|
||||
"@radix-ui/react-avatar": "^1.1.1",
|
||||
"@radix-ui/react-dialog": "^1.1.2",
|
||||
"@radix-ui/react-icons": "^1.3.1",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-scroll-area": "^1.2.0",
|
||||
"@radix-ui/react-select": "^2.1.2",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"ai": "4.1.42",
|
||||
"buffer": "^6.0.3",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^11.11.11",
|
||||
"lucide-react": "^0.454.0",
|
||||
"openai": "^4.86.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
"mem0ai": "2.1.2",
|
||||
"tailwind-merge": "^2.5.4",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.13.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@vitejs/plugin-react": "^4.3.3",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.13.0",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.14",
|
||||
"globals": "^15.11.0",
|
||||
"postcss": "^8.4.47",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"typescript": "~5.6.2",
|
||||
"typescript-eslint": "^8.11.0",
|
||||
"vite": "^6.2.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.5.2+sha512.da9dc28cd3ff40d0592188235ab25d3202add8a207afbedc682220e4a0029ffbff4562102b9e6e46b4e3f9e8bd53e6d05de48544b0c57d4b0179e22c76d1199b"
|
||||
}
|
||||
+16
-17
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mem0ai",
|
||||
"version": "2.1.8",
|
||||
"version": "2.1.15",
|
||||
"description": "The Memory Layer For Your AI Apps",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
@@ -34,7 +34,8 @@
|
||||
"clean": "rimraf dist",
|
||||
"build": "npm run clean && npx prettier --check . && npx tsup",
|
||||
"dev": "npx nodemon",
|
||||
"start": "npx ts-node src/oss/examples/basic.ts",
|
||||
"start": "pnpm run example memory",
|
||||
"example": "ts-node src/oss/examples/vector-stores/index.ts",
|
||||
"test": "jest",
|
||||
"test:ts": "jest --config jest.config.js",
|
||||
"test:watch": "jest --config jest.config.js --watch",
|
||||
@@ -56,7 +57,13 @@
|
||||
"sourcemap": true,
|
||||
"clean": true,
|
||||
"treeshake": true,
|
||||
"minify": false
|
||||
"minify": false,
|
||||
"external": [
|
||||
"@mem0/community"
|
||||
],
|
||||
"noExternal": [
|
||||
"!src/community/**"
|
||||
]
|
||||
},
|
||||
"keywords": [
|
||||
"mem0",
|
||||
@@ -85,33 +92,25 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "1.7.7",
|
||||
"neo4j-driver": "^5.28.1",
|
||||
"openai": "4.28.0",
|
||||
"redis": "^4.6.13",
|
||||
"uuid": "9.0.1",
|
||||
"zod": "3.22.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@anthropic-ai/sdk": "0.18.0",
|
||||
"@qdrant/js-client-rest": "1.13.0",
|
||||
"@google/genai": "^0.7.0",
|
||||
"@supabase/supabase-js": "^2.49.1",
|
||||
"@types/jest": "29.5.14",
|
||||
"@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",
|
||||
"ollama": "^0.5.14"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"posthog-node": {
|
||||
"optional": true
|
||||
},
|
||||
"posthog-js": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"posthog-js": "^1.116.6"
|
||||
"sqlite3": "5.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
Generated
+7751
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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/`,
|
||||
{
|
||||
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -1,3 +1,100 @@
|
||||
// @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
|
||||
let MEM0_TELEMETRY = true;
|
||||
try {
|
||||
MEM0_TELEMETRY = process?.env?.MEM0_TELEMETRY === "false" ? false : true;
|
||||
} catch (error) {}
|
||||
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 };
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Dependencies
|
||||
node_modules
|
||||
.pnp
|
||||
.pnp.js
|
||||
|
||||
# Build outputs
|
||||
dist
|
||||
build
|
||||
|
||||
# Lock files
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
|
||||
# Coverage
|
||||
coverage
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"name": "@mem0/community",
|
||||
"version": "0.0.1",
|
||||
"description": "Community features for Mem0",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"require": "./dist/index.js",
|
||||
"import": "./dist/index.mjs"
|
||||
},
|
||||
"./langchain": {
|
||||
"types": "./dist/integrations/langchain/index.d.ts",
|
||||
"require": "./dist/integrations/langchain/index.js",
|
||||
"import": "./dist/integrations/langchain/index.mjs"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"build": "npm run clean && npx prettier --check . && npx tsup",
|
||||
"dev": "npx nodemon",
|
||||
"test": "jest",
|
||||
"test:ts": "jest --config jest.config.js",
|
||||
"test:watch": "jest --config jest.config.js --watch",
|
||||
"format": "npm run clean && prettier --write .",
|
||||
"format:check": "npm run clean && prettier --check .",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"tsup": {
|
||||
"entry": {
|
||||
"index": "src/index.ts",
|
||||
"integrations/langchain/index": "src/integrations/langchain/index.ts"
|
||||
},
|
||||
"format": [
|
||||
"cjs",
|
||||
"esm"
|
||||
],
|
||||
"dts": {
|
||||
"resolve": true,
|
||||
"compilerOptions": {
|
||||
"rootDir": "src"
|
||||
}
|
||||
},
|
||||
"splitting": false,
|
||||
"sourcemap": true,
|
||||
"clean": true,
|
||||
"treeshake": true,
|
||||
"minify": false,
|
||||
"outDir": "dist",
|
||||
"tsconfig": "./tsconfig.json"
|
||||
},
|
||||
"keywords": [
|
||||
"mem0",
|
||||
"community",
|
||||
"ai",
|
||||
"memory"
|
||||
],
|
||||
"author": "Deshraj Yadav",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.7.6",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"dotenv": "^16.4.5",
|
||||
"jest": "^29.7.0",
|
||||
"nodemon": "^3.0.1",
|
||||
"prettier": "^3.5.2",
|
||||
"rimraf": "^5.0.5",
|
||||
"ts-jest": "^29.2.6",
|
||||
"tsup": "^8.3.0",
|
||||
"typescript": "5.5.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@langchain/community": "^0.3.36",
|
||||
"@langchain/core": "^0.3.42",
|
||||
"axios": "1.7.7",
|
||||
"mem0ai": "^2.1.8",
|
||||
"uuid": "9.0.1",
|
||||
"zod": "3.22.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./integrations/langchain";
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./mem0";
|
||||
@@ -0,0 +1,314 @@
|
||||
import { MemoryClient } from "mem0ai";
|
||||
import type { Memory, MemoryOptions, SearchOptions } from "mem0ai";
|
||||
|
||||
import {
|
||||
InputValues,
|
||||
OutputValues,
|
||||
MemoryVariables,
|
||||
getInputValue,
|
||||
getOutputValue,
|
||||
} from "@langchain/core/memory";
|
||||
import {
|
||||
AIMessage,
|
||||
BaseMessage,
|
||||
ChatMessage,
|
||||
getBufferString,
|
||||
HumanMessage,
|
||||
SystemMessage,
|
||||
} from "@langchain/core/messages";
|
||||
import {
|
||||
BaseChatMemory,
|
||||
BaseChatMemoryInput,
|
||||
} from "@langchain/community/memory/chat_memory";
|
||||
|
||||
/**
|
||||
* Extracts and formats memory content into a system prompt
|
||||
* @param memory Array of Memory objects from mem0ai
|
||||
* @returns Formatted system prompt string
|
||||
*/
|
||||
export const mem0MemoryContextToSystemPrompt = (memory: Memory[]): string => {
|
||||
if (!memory || !Array.isArray(memory)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return memory
|
||||
.filter((m) => m?.memory)
|
||||
.map((m) => m.memory)
|
||||
.join("\n");
|
||||
};
|
||||
|
||||
/**
|
||||
* Condenses memory content into a single HumanMessage with context
|
||||
* @param memory Array of Memory objects from mem0ai
|
||||
* @returns HumanMessage containing formatted memory context
|
||||
*/
|
||||
export const condenseMem0MemoryIntoHumanMessage = (
|
||||
memory: Memory[],
|
||||
): HumanMessage => {
|
||||
const basePrompt =
|
||||
"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 MEMORIES of the USER are: \n\n";
|
||||
const systemPrompt = mem0MemoryContextToSystemPrompt(memory);
|
||||
|
||||
return new HumanMessage(`${basePrompt}\n${systemPrompt}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts Mem0 memories to a list of BaseMessages
|
||||
* @param memories Array of Memory objects from mem0ai
|
||||
* @returns Array of BaseMessage objects
|
||||
*/
|
||||
export const mem0MemoryToMessages = (memories: Memory[]): BaseMessage[] => {
|
||||
if (!memories || !Array.isArray(memories)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const messages: BaseMessage[] = [];
|
||||
|
||||
// Add memories as system message if present
|
||||
const memoryContent = memories
|
||||
.filter((m) => m?.memory)
|
||||
.map((m) => m.memory)
|
||||
.join("\n");
|
||||
|
||||
if (memoryContent) {
|
||||
messages.push(new SystemMessage(memoryContent));
|
||||
}
|
||||
|
||||
// Add conversation messages
|
||||
memories.forEach((memory) => {
|
||||
if (memory.messages) {
|
||||
memory.messages.forEach((msg) => {
|
||||
const content =
|
||||
typeof msg.content === "string"
|
||||
? msg.content
|
||||
: JSON.stringify(msg.content);
|
||||
if (msg.role === "user") {
|
||||
messages.push(new HumanMessage(content));
|
||||
} else if (msg.role === "assistant") {
|
||||
messages.push(new AIMessage(content));
|
||||
} else if (content) {
|
||||
messages.push(new ChatMessage(content, msg.role));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return messages;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface defining the structure of the input data for the Mem0Client
|
||||
*/
|
||||
export interface ClientOptions {
|
||||
apiKey: string;
|
||||
host?: string;
|
||||
organizationName?: string;
|
||||
projectName?: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface defining the structure of the input data for the Mem0Memory
|
||||
* class. It includes properties like memoryKey, sessionId, and apiKey.
|
||||
*/
|
||||
export interface Mem0MemoryInput extends BaseChatMemoryInput {
|
||||
sessionId: string;
|
||||
apiKey: string;
|
||||
humanPrefix?: string;
|
||||
aiPrefix?: string;
|
||||
memoryOptions?: MemoryOptions | SearchOptions;
|
||||
mem0Options?: ClientOptions;
|
||||
separateMessages?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used to manage the memory of a chat session using the Mem0 service.
|
||||
* It handles loading and saving chat history, and provides methods to format
|
||||
* the memory content for use in chat models.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const memory = new Mem0Memory({
|
||||
* sessionId: "user123" // or use user_id inside of memoryOptions (recommended),
|
||||
* apiKey: "your-api-key",
|
||||
* memoryOptions: {
|
||||
* user_id: "user123",
|
||||
* run_id: "run123"
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* // Use with a chat model
|
||||
* const model = new ChatOpenAI({
|
||||
* modelName: "gpt-3.5-turbo",
|
||||
* temperature: 0,
|
||||
* });
|
||||
*
|
||||
* const chain = new ConversationChain({ llm: model, memory });
|
||||
* ```
|
||||
*/
|
||||
export class Mem0Memory extends BaseChatMemory implements Mem0MemoryInput {
|
||||
memoryKey = "history";
|
||||
|
||||
apiKey: string;
|
||||
|
||||
sessionId: string;
|
||||
|
||||
humanPrefix = "Human";
|
||||
|
||||
aiPrefix = "AI";
|
||||
|
||||
mem0Client: InstanceType<typeof MemoryClient>;
|
||||
|
||||
memoryOptions: MemoryOptions | SearchOptions;
|
||||
|
||||
mem0Options: ClientOptions;
|
||||
|
||||
// Whether to return separate messages for chat history with a SystemMessage containing (facts and summary) or return a single HumanMessage with the entire memory context.
|
||||
// Defaults to false (return a single HumanMessage) in order to allow more flexibility with different models.
|
||||
separateMessages?: boolean;
|
||||
|
||||
constructor(fields: Mem0MemoryInput) {
|
||||
if (!fields.apiKey) {
|
||||
throw new Error("apiKey is required for Mem0Memory");
|
||||
}
|
||||
if (!fields.sessionId) {
|
||||
throw new Error("sessionId is required for Mem0Memory");
|
||||
}
|
||||
|
||||
super({
|
||||
returnMessages: fields?.returnMessages ?? false,
|
||||
inputKey: fields?.inputKey,
|
||||
outputKey: fields?.outputKey,
|
||||
});
|
||||
|
||||
this.apiKey = fields.apiKey;
|
||||
this.sessionId = fields.sessionId;
|
||||
this.humanPrefix = fields.humanPrefix ?? this.humanPrefix;
|
||||
this.aiPrefix = fields.aiPrefix ?? this.aiPrefix;
|
||||
this.memoryOptions = fields.memoryOptions ?? {};
|
||||
this.mem0Options = fields.mem0Options ?? {
|
||||
apiKey: this.apiKey,
|
||||
};
|
||||
this.separateMessages = fields.separateMessages ?? false;
|
||||
try {
|
||||
this.mem0Client = new MemoryClient({
|
||||
...this.mem0Options,
|
||||
apiKey: this.apiKey,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize Mem0Client:", error);
|
||||
throw new Error(
|
||||
"Failed to initialize Mem0Client. Please check your configuration.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
get memoryKeys(): string[] {
|
||||
return [this.memoryKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves memories from the Mem0 service and formats them for use
|
||||
* @param values Input values containing optional search query
|
||||
* @returns Promise resolving to formatted memory variables
|
||||
*/
|
||||
async loadMemoryVariables(values: InputValues): Promise<MemoryVariables> {
|
||||
const searchType = values.input ? "search" : "get_all";
|
||||
let memories: Memory[] = [];
|
||||
|
||||
try {
|
||||
if (searchType === "get_all") {
|
||||
memories = await this.mem0Client.getAll({
|
||||
user_id: this.sessionId,
|
||||
...this.memoryOptions,
|
||||
});
|
||||
} else {
|
||||
memories = await this.mem0Client.search(values.input, {
|
||||
user_id: this.sessionId,
|
||||
...this.memoryOptions,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading memories:", error);
|
||||
return this.returnMessages
|
||||
? { [this.memoryKey]: [] }
|
||||
: { [this.memoryKey]: "" };
|
||||
}
|
||||
|
||||
if (this.returnMessages) {
|
||||
return {
|
||||
[this.memoryKey]: this.separateMessages
|
||||
? mem0MemoryToMessages(memories)
|
||||
: [condenseMem0MemoryIntoHumanMessage(memories)],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
[this.memoryKey]: this.separateMessages
|
||||
? getBufferString(
|
||||
mem0MemoryToMessages(memories),
|
||||
this.humanPrefix,
|
||||
this.aiPrefix,
|
||||
)
|
||||
: (condenseMem0MemoryIntoHumanMessage(memories).content ?? ""),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current conversation context to the Mem0 service
|
||||
* @param inputValues Input messages to be saved
|
||||
* @param outputValues Output messages to be saved
|
||||
* @returns Promise resolving when the context has been saved
|
||||
*/
|
||||
async saveContext(
|
||||
inputValues: InputValues,
|
||||
outputValues: OutputValues,
|
||||
): Promise<void> {
|
||||
const input = getInputValue(inputValues, this.inputKey);
|
||||
const output = getOutputValue(outputValues, this.outputKey);
|
||||
|
||||
if (!input || !output) {
|
||||
console.warn("Missing input or output values, skipping memory save");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const messages = [
|
||||
{
|
||||
role: "user",
|
||||
content: `${input}`,
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: `${output}`,
|
||||
},
|
||||
];
|
||||
|
||||
await this.mem0Client.add(messages, {
|
||||
user_id: this.sessionId,
|
||||
...this.memoryOptions,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error saving memory context:", error);
|
||||
// Continue execution even if memory save fails
|
||||
}
|
||||
|
||||
await super.saveContext(inputValues, outputValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all memories for the current session
|
||||
* @returns Promise resolving when memories have been cleared
|
||||
*/
|
||||
async clear(): Promise<void> {
|
||||
try {
|
||||
// Note: Implement clear functionality if Mem0Client provides it
|
||||
// await this.mem0Client.clear(this.sessionId);
|
||||
} catch (error) {
|
||||
console.error("Error clearing memories:", error);
|
||||
}
|
||||
|
||||
await super.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020"],
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"types": ["node"],
|
||||
"typeRoots": ["./node_modules/@types"]
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { Memory } from "../../src";
|
||||
|
||||
export async function runTests(memory: Memory) {
|
||||
try {
|
||||
// Reset all memories
|
||||
console.log("\nResetting all memories...");
|
||||
await memory.reset();
|
||||
console.log("All memories reset");
|
||||
|
||||
// Add a single memory
|
||||
console.log("\nAdding a single memory...");
|
||||
const result1 = await memory.add(
|
||||
"Hi, my name is John and I am a software engineer.",
|
||||
{
|
||||
userId: "john",
|
||||
},
|
||||
);
|
||||
console.log("Added memory:", result1);
|
||||
|
||||
// Add multiple messages
|
||||
console.log("\nAdding multiple messages...");
|
||||
const result2 = await memory.add(
|
||||
[
|
||||
{ role: "user", content: "What is your favorite city?" },
|
||||
{ role: "assistant", content: "I love Paris, it is my favorite city." },
|
||||
],
|
||||
{
|
||||
userId: "john",
|
||||
},
|
||||
);
|
||||
console.log("Added messages:", result2);
|
||||
|
||||
// Trying to update the memory
|
||||
const result3 = await memory.add(
|
||||
[
|
||||
{ role: "user", content: "What is your favorite city?" },
|
||||
{
|
||||
role: "assistant",
|
||||
content: "I love New York, it is my favorite city.",
|
||||
},
|
||||
],
|
||||
{
|
||||
userId: "john",
|
||||
},
|
||||
);
|
||||
console.log("Updated messages:", result3);
|
||||
|
||||
// Get a single memory
|
||||
console.log("\nGetting a single memory...");
|
||||
if (result1.results && result1.results.length > 0) {
|
||||
const singleMemory = await memory.get(result1.results[0].id);
|
||||
console.log("Single memory:", singleMemory);
|
||||
} else {
|
||||
console.log("No memory was added in the first step");
|
||||
}
|
||||
|
||||
// Updating this memory
|
||||
const result4 = await memory.update(
|
||||
result1.results[0].id,
|
||||
"I love India, it is my favorite country.",
|
||||
);
|
||||
console.log("Updated memory:", result4);
|
||||
|
||||
// Get all memories
|
||||
console.log("\nGetting all memories...");
|
||||
const allMemories = await memory.getAll({
|
||||
userId: "john",
|
||||
});
|
||||
console.log("All memories:", allMemories);
|
||||
|
||||
// Search for memories
|
||||
console.log("\nSearching memories...");
|
||||
const searchResult = await memory.search("What do you know about Paris?", {
|
||||
userId: "john",
|
||||
});
|
||||
console.log("Search results:", searchResult);
|
||||
|
||||
// Get memory history
|
||||
if (result1.results && result1.results.length > 0) {
|
||||
console.log("\nGetting memory history...");
|
||||
const history = await memory.history(result1.results[0].id);
|
||||
console.log("Memory history:", history);
|
||||
}
|
||||
|
||||
// Delete a memory
|
||||
if (result1.results && result1.results.length > 0) {
|
||||
console.log("\nDeleting a memory...");
|
||||
await memory.delete(result1.results[0].id);
|
||||
console.log("Memory deleted successfully");
|
||||
}
|
||||
|
||||
// Reset all memories
|
||||
console.log("\nResetting all memories...");
|
||||
await memory.reset();
|
||||
console.log("All memories reset");
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import dotenv from "dotenv";
|
||||
import { demoMemoryStore } from "./memory";
|
||||
import { demoSupabase } from "./supabase";
|
||||
// import { demoQdrant } from "./qdrant";
|
||||
// import { demoRedis } from "./redis";
|
||||
// import { demoPGVector } from "./pgvector";
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const selectedStore = args[0]?.toLowerCase();
|
||||
|
||||
const stores: Record<string, () => Promise<void>> = {
|
||||
// memory: demoMemoryStore,
|
||||
supabase: demoSupabase,
|
||||
// Uncomment these as they are implemented
|
||||
// qdrant: demoQdrant,
|
||||
// redis: demoRedis,
|
||||
// pgvector: demoPGVector,
|
||||
};
|
||||
|
||||
if (selectedStore) {
|
||||
const demo = stores[selectedStore];
|
||||
if (demo) {
|
||||
try {
|
||||
await demo();
|
||||
} catch (error) {
|
||||
console.error(`\nError running ${selectedStore} demo:`, error);
|
||||
if (selectedStore !== "memory") {
|
||||
console.log("\nFalling back to memory store...");
|
||||
await stores.memory();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(`\nUnknown vector store: ${selectedStore}`);
|
||||
console.log("Available stores:", Object.keys(stores).join(", "));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If no store specified, run all available demos
|
||||
for (const [name, demo] of Object.entries(stores)) {
|
||||
try {
|
||||
await demo();
|
||||
} catch (error) {
|
||||
console.error(`\nError running ${name} demo:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Memory } from "../../src";
|
||||
import { runTests } from "../utils/test-utils";
|
||||
|
||||
export async function demoMemoryStore() {
|
||||
console.log("\n=== Testing In-Memory Vector Store ===\n");
|
||||
|
||||
const memory = new Memory({
|
||||
version: "v1.1",
|
||||
embedder: {
|
||||
provider: "openai",
|
||||
config: {
|
||||
apiKey: process.env.OPENAI_API_KEY || "",
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
},
|
||||
vectorStore: {
|
||||
provider: "memory",
|
||||
config: {
|
||||
collectionName: "memories",
|
||||
dimension: 1536,
|
||||
},
|
||||
},
|
||||
llm: {
|
||||
provider: "openai",
|
||||
config: {
|
||||
apiKey: process.env.OPENAI_API_KEY || "",
|
||||
model: "gpt-4-turbo-preview",
|
||||
},
|
||||
},
|
||||
historyDbPath: "memory.db",
|
||||
});
|
||||
|
||||
await runTests(memory);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
demoMemoryStore();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Memory } from "../../src";
|
||||
import { runTests } from "../utils/test-utils";
|
||||
|
||||
export async function demoPGVector() {
|
||||
console.log("\n=== Testing PGVector Store ===\n");
|
||||
|
||||
const memory = new Memory({
|
||||
version: "v1.1",
|
||||
embedder: {
|
||||
provider: "openai",
|
||||
config: {
|
||||
apiKey: process.env.OPENAI_API_KEY || "",
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
},
|
||||
vectorStore: {
|
||||
provider: "pgvector",
|
||||
config: {
|
||||
collectionName: "memories",
|
||||
dimension: 1536,
|
||||
dbname: process.env.PGVECTOR_DB || "vectordb",
|
||||
user: process.env.PGVECTOR_USER || "postgres",
|
||||
password: process.env.PGVECTOR_PASSWORD || "postgres",
|
||||
host: process.env.PGVECTOR_HOST || "localhost",
|
||||
port: parseInt(process.env.PGVECTOR_PORT || "5432"),
|
||||
embeddingModelDims: 1536,
|
||||
hnsw: true,
|
||||
},
|
||||
},
|
||||
llm: {
|
||||
provider: "openai",
|
||||
config: {
|
||||
apiKey: process.env.OPENAI_API_KEY || "",
|
||||
model: "gpt-4-turbo-preview",
|
||||
},
|
||||
},
|
||||
historyDbPath: "memory.db",
|
||||
});
|
||||
|
||||
await runTests(memory);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
if (!process.env.PGVECTOR_DB) {
|
||||
console.log("\nSkipping PGVector test - environment variables not set");
|
||||
process.exit(0);
|
||||
}
|
||||
demoPGVector();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Memory } from "../../src";
|
||||
import { runTests } from "../utils/test-utils";
|
||||
|
||||
export async function demoQdrant() {
|
||||
console.log("\n=== Testing Qdrant Store ===\n");
|
||||
|
||||
const memory = new Memory({
|
||||
version: "v1.1",
|
||||
embedder: {
|
||||
provider: "openai",
|
||||
config: {
|
||||
apiKey: process.env.OPENAI_API_KEY || "",
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
},
|
||||
vectorStore: {
|
||||
provider: "qdrant",
|
||||
config: {
|
||||
collectionName: "memories",
|
||||
embeddingModelDims: 1536,
|
||||
url: process.env.QDRANT_URL,
|
||||
apiKey: process.env.QDRANT_API_KEY,
|
||||
path: process.env.QDRANT_PATH,
|
||||
host: process.env.QDRANT_HOST,
|
||||
port: process.env.QDRANT_PORT
|
||||
? parseInt(process.env.QDRANT_PORT)
|
||||
: undefined,
|
||||
onDisk: true,
|
||||
},
|
||||
},
|
||||
llm: {
|
||||
provider: "openai",
|
||||
config: {
|
||||
apiKey: process.env.OPENAI_API_KEY || "",
|
||||
model: "gpt-4-turbo-preview",
|
||||
},
|
||||
},
|
||||
historyDbPath: "memory.db",
|
||||
});
|
||||
|
||||
await runTests(memory);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
if (!process.env.QDRANT_URL && !process.env.QDRANT_HOST) {
|
||||
console.log("\nSkipping Qdrant test - environment variables not set");
|
||||
process.exit(0);
|
||||
}
|
||||
demoQdrant();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Memory } from "../../src";
|
||||
import { runTests } from "../utils/test-utils";
|
||||
|
||||
export async function demoRedis() {
|
||||
console.log("\n=== Testing Redis Store ===\n");
|
||||
|
||||
const memory = new Memory({
|
||||
version: "v1.1",
|
||||
embedder: {
|
||||
provider: "openai",
|
||||
config: {
|
||||
apiKey: process.env.OPENAI_API_KEY || "",
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
},
|
||||
vectorStore: {
|
||||
provider: "redis",
|
||||
config: {
|
||||
collectionName: "memories",
|
||||
embeddingModelDims: 1536,
|
||||
redisUrl: process.env.REDIS_URL || "redis://localhost:6379",
|
||||
username: process.env.REDIS_USERNAME,
|
||||
password: process.env.REDIS_PASSWORD,
|
||||
},
|
||||
},
|
||||
llm: {
|
||||
provider: "openai",
|
||||
config: {
|
||||
apiKey: process.env.OPENAI_API_KEY || "",
|
||||
model: "gpt-4-turbo-preview",
|
||||
},
|
||||
},
|
||||
historyDbPath: "memory.db",
|
||||
});
|
||||
|
||||
await runTests(memory);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
if (!process.env.REDIS_URL) {
|
||||
console.log("\nSkipping Redis test - environment variables not set");
|
||||
process.exit(0);
|
||||
}
|
||||
demoRedis();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Memory } from "../../src";
|
||||
import { runTests } from "../utils/test-utils";
|
||||
import dotenv from "dotenv";
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
export async function demoSupabase() {
|
||||
console.log("\n=== Testing Supabase Vector Store ===\n");
|
||||
|
||||
const memory = new Memory({
|
||||
version: "v1.1",
|
||||
embedder: {
|
||||
provider: "openai",
|
||||
config: {
|
||||
apiKey: process.env.OPENAI_API_KEY || "",
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
},
|
||||
vectorStore: {
|
||||
provider: "supabase",
|
||||
config: {
|
||||
collectionName: "memories",
|
||||
embeddingModelDims: 1536,
|
||||
supabaseUrl: process.env.SUPABASE_URL || "",
|
||||
supabaseKey: process.env.SUPABASE_KEY || "",
|
||||
tableName: "memories",
|
||||
},
|
||||
},
|
||||
llm: {
|
||||
provider: "openai",
|
||||
config: {
|
||||
apiKey: process.env.OPENAI_API_KEY || "",
|
||||
model: "gpt-4-turbo-preview",
|
||||
},
|
||||
},
|
||||
historyDbPath: "memory.db",
|
||||
});
|
||||
|
||||
await runTests(memory);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
if (!process.env.SUPABASE_URL || !process.env.SUPABASE_KEY) {
|
||||
console.log("\nSkipping Supabase test - environment variables not set");
|
||||
process.exit(0);
|
||||
}
|
||||
demoSupabase();
|
||||
}
|
||||
@@ -7,13 +7,14 @@
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "jest",
|
||||
"start": "ts-node examples/basic.ts",
|
||||
"example": "ts-node examples/basic.ts",
|
||||
"start": "pnpm run example memory",
|
||||
"example": "ts-node examples/vector-stores/index.ts",
|
||||
"clean": "rimraf dist",
|
||||
"prepare": "npm run build"
|
||||
},
|
||||
"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",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { MemoryConfig } from "../types";
|
||||
|
||||
export const DEFAULT_MEMORY_CONFIG: MemoryConfig = {
|
||||
disableHistory: false,
|
||||
version: "v1.1",
|
||||
embedder: {
|
||||
provider: "openai",
|
||||
@@ -38,5 +39,10 @@ export const DEFAULT_MEMORY_CONFIG: MemoryConfig = {
|
||||
},
|
||||
},
|
||||
},
|
||||
historyDbPath: "memory.db",
|
||||
historyStore: {
|
||||
provider: "sqlite",
|
||||
config: {
|
||||
historyDbPath: "memory.db",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -51,6 +51,12 @@ export class ConfigManager {
|
||||
...DEFAULT_MEMORY_CONFIG.graphStore,
|
||||
...userConfig.graphStore,
|
||||
},
|
||||
historyStore: {
|
||||
...DEFAULT_MEMORY_CONFIG.historyStore,
|
||||
...userConfig.historyStore,
|
||||
},
|
||||
disableHistory:
|
||||
userConfig.disableHistory || DEFAULT_MEMORY_CONFIG.disableHistory,
|
||||
enableGraph: userConfig.enableGraph || DEFAULT_MEMORY_CONFIG.enableGraph,
|
||||
};
|
||||
|
||||
|
||||
@@ -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!);
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,15 @@ export * from "./memory/memory.types";
|
||||
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";
|
||||
export * from "./llms/ollama";
|
||||
export * from "./vector_stores/base";
|
||||
export * from "./vector_stores/memory";
|
||||
export * from "./vector_stores/qdrant";
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
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!,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ export class OllamaLLM implements LLM {
|
||||
arguments: JSON.stringify(call.function.arguments),
|
||||
})),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return response.content || "";
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
EmbedderFactory,
|
||||
LLMFactory,
|
||||
VectorStoreFactory,
|
||||
HistoryManagerFactory,
|
||||
} from "../utils/factory";
|
||||
import {
|
||||
getFactRetrievalMessages,
|
||||
@@ -19,7 +20,7 @@ import {
|
||||
parseMessages,
|
||||
removeCodeBlocks,
|
||||
} from "../prompts";
|
||||
import { SQLiteManager } from "../storage";
|
||||
import { DummyHistoryManager } from "../storage/DummyHistoryManager";
|
||||
import { Embedder } from "../embeddings/base";
|
||||
import { LLM } from "../llms/base";
|
||||
import { VectorStore } from "../vector_stores/base";
|
||||
@@ -32,6 +33,8 @@ import {
|
||||
GetAllMemoryOptions,
|
||||
} from "./memory.types";
|
||||
import { parse_vision_messages } from "../utils/memory";
|
||||
import { HistoryManager } from "../storage/base";
|
||||
import { captureClientEvent } from "../utils/telemetry";
|
||||
|
||||
export class Memory {
|
||||
private config: MemoryConfig;
|
||||
@@ -39,11 +42,12 @@ export class Memory {
|
||||
private embedder: Embedder;
|
||||
private vectorStore: VectorStore;
|
||||
private llm: LLM;
|
||||
private db: SQLiteManager;
|
||||
private db: HistoryManager;
|
||||
private collectionName: string;
|
||||
private apiVersion: string;
|
||||
private graphMemory?: MemoryGraph;
|
||||
private enableGraph: boolean;
|
||||
telemetryId: string;
|
||||
|
||||
constructor(config: Partial<MemoryConfig> = {}) {
|
||||
// Merge and validate config
|
||||
@@ -62,15 +66,80 @@ export class Memory {
|
||||
this.config.llm.provider,
|
||||
this.config.llm.config,
|
||||
);
|
||||
this.db = new SQLiteManager(this.config.historyDbPath || ":memory:");
|
||||
if (this.config.disableHistory) {
|
||||
this.db = new DummyHistoryManager();
|
||||
} else {
|
||||
const defaultConfig = {
|
||||
provider: "sqlite",
|
||||
config: {
|
||||
historyDbPath: this.config.historyDbPath || ":memory:",
|
||||
},
|
||||
};
|
||||
|
||||
this.db =
|
||||
this.config.historyStore && !this.config.disableHistory
|
||||
? HistoryManagerFactory.create(
|
||||
this.config.historyStore.provider,
|
||||
this.config.historyStore,
|
||||
)
|
||||
: HistoryManagerFactory.create("sqlite", defaultConfig);
|
||||
}
|
||||
|
||||
this.collectionName = this.config.vectorStore.config.collectionName;
|
||||
this.apiVersion = this.config.version || "v1.0";
|
||||
this.enableGraph = this.config.enableGraph || false;
|
||||
this.telemetryId = "anonymous";
|
||||
|
||||
// Initialize graph memory if configured
|
||||
if (this.enableGraph && this.config.graphStore) {
|
||||
this.graphMemory = new MemoryGraph(this.config);
|
||||
}
|
||||
|
||||
// Initialize telemetry if vector store is initialized
|
||||
this._initializeTelemetry();
|
||||
}
|
||||
|
||||
private async _initializeTelemetry() {
|
||||
try {
|
||||
await this._getTelemetryId();
|
||||
|
||||
// Capture initialization event
|
||||
await captureClientEvent("init", this, {
|
||||
api_version: this.apiVersion,
|
||||
client_type: "Memory",
|
||||
collection_name: this.collectionName,
|
||||
enable_graph: this.enableGraph,
|
||||
});
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
private async _getTelemetryId() {
|
||||
try {
|
||||
if (
|
||||
!this.telemetryId ||
|
||||
this.telemetryId === "anonymous" ||
|
||||
this.telemetryId === "anonymous-supabase"
|
||||
) {
|
||||
this.telemetryId = await this.vectorStore.getUserId();
|
||||
}
|
||||
return this.telemetryId;
|
||||
} catch (error) {
|
||||
this.telemetryId = "anonymous";
|
||||
return this.telemetryId;
|
||||
}
|
||||
}
|
||||
|
||||
private async _captureEvent(methodName: string, additionalData = {}) {
|
||||
try {
|
||||
await this._getTelemetryId();
|
||||
await captureClientEvent(methodName, this, {
|
||||
...additionalData,
|
||||
api_version: this.apiVersion,
|
||||
collection_name: this.collectionName,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Failed to capture ${methodName} event:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
static fromConfig(configDict: Record<string, any>): Memory {
|
||||
@@ -87,13 +156,19 @@ export class Memory {
|
||||
messages: string | Message[],
|
||||
config: AddMemoryOptions,
|
||||
): Promise<SearchResult> {
|
||||
await this._captureEvent("add", {
|
||||
message_count: Array.isArray(messages) ? messages.length : 1,
|
||||
has_metadata: !!config.metadata,
|
||||
has_filters: !!config.filters,
|
||||
infer: config.infer,
|
||||
});
|
||||
const {
|
||||
userId,
|
||||
agentId,
|
||||
runId,
|
||||
metadata = {},
|
||||
filters = {},
|
||||
prompt,
|
||||
infer = true,
|
||||
} = config;
|
||||
|
||||
if (userId) filters.userId = metadata.userId = userId;
|
||||
@@ -117,6 +192,7 @@ export class Memory {
|
||||
final_parsedMessages,
|
||||
metadata,
|
||||
filters,
|
||||
infer,
|
||||
);
|
||||
|
||||
// Add to graph store if available
|
||||
@@ -142,7 +218,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
|
||||
@@ -301,6 +397,11 @@ export class Memory {
|
||||
query: string,
|
||||
config: SearchMemoryOptions,
|
||||
): Promise<SearchResult> {
|
||||
await this._captureEvent("search", {
|
||||
query_length: query.length,
|
||||
limit: config.limit,
|
||||
has_filters: !!config.filters,
|
||||
});
|
||||
const { userId, agentId, runId, limit = 100, filters = {} } = config;
|
||||
|
||||
if (userId) filters.userId = userId;
|
||||
@@ -362,12 +463,14 @@ export class Memory {
|
||||
}
|
||||
|
||||
async update(memoryId: string, data: string): Promise<{ message: string }> {
|
||||
await this._captureEvent("update", { memory_id: memoryId });
|
||||
const embedding = await this.embedder.embed(data);
|
||||
await this.updateMemory(memoryId, data, { [data]: embedding });
|
||||
return { message: "Memory updated successfully!" };
|
||||
}
|
||||
|
||||
async delete(memoryId: string): Promise<{ message: string }> {
|
||||
await this._captureEvent("delete", { memory_id: memoryId });
|
||||
await this.deleteMemory(memoryId);
|
||||
return { message: "Memory deleted successfully!" };
|
||||
}
|
||||
@@ -375,6 +478,11 @@ export class Memory {
|
||||
async deleteAll(
|
||||
config: DeleteAllMemoryOptions,
|
||||
): Promise<{ message: string }> {
|
||||
await this._captureEvent("delete_all", {
|
||||
has_user_id: !!config.userId,
|
||||
has_agent_id: !!config.agentId,
|
||||
has_run_id: !!config.runId,
|
||||
});
|
||||
const { userId, agentId, runId } = config;
|
||||
|
||||
const filters: SearchFilters = {};
|
||||
@@ -401,6 +509,7 @@ export class Memory {
|
||||
}
|
||||
|
||||
async reset(): Promise<void> {
|
||||
await this._captureEvent("reset");
|
||||
await this.db.reset();
|
||||
await this.vectorStore.deleteCol();
|
||||
if (this.graphMemory) {
|
||||
@@ -413,6 +522,12 @@ export class Memory {
|
||||
}
|
||||
|
||||
async getAll(config: GetAllMemoryOptions): Promise<SearchResult> {
|
||||
await this._captureEvent("get_all", {
|
||||
limit: config.limit,
|
||||
has_user_id: !!config.userId,
|
||||
has_agent_id: !!config.agentId,
|
||||
has_run_id: !!config.runId,
|
||||
});
|
||||
const { userId, agentId, runId, limit = 100 } = config;
|
||||
|
||||
const filters: SearchFilters = {};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
export class DummyHistoryManager {
|
||||
constructor() {}
|
||||
|
||||
async addHistory(
|
||||
memoryId: string,
|
||||
previousValue: string | null,
|
||||
newValue: string | null,
|
||||
action: string,
|
||||
createdAt?: string,
|
||||
updatedAt?: string,
|
||||
isDeleted: number = 0,
|
||||
): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
async getHistory(memoryId: string): Promise<any[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
async reset(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
close(): void {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { HistoryManager } from "./base";
|
||||
interface HistoryEntry {
|
||||
id: string;
|
||||
memory_id: string;
|
||||
previous_value: string | null;
|
||||
new_value: string | null;
|
||||
action: string;
|
||||
created_at: string;
|
||||
updated_at: string | null;
|
||||
is_deleted: number;
|
||||
}
|
||||
|
||||
export class MemoryHistoryManager implements HistoryManager {
|
||||
private memoryStore: Map<string, HistoryEntry> = new Map();
|
||||
|
||||
async addHistory(
|
||||
memoryId: string,
|
||||
previousValue: string | null,
|
||||
newValue: string | null,
|
||||
action: string,
|
||||
createdAt?: string,
|
||||
updatedAt?: string,
|
||||
isDeleted: number = 0,
|
||||
): Promise<void> {
|
||||
const historyEntry: HistoryEntry = {
|
||||
id: uuidv4(),
|
||||
memory_id: memoryId,
|
||||
previous_value: previousValue,
|
||||
new_value: newValue,
|
||||
action: action,
|
||||
created_at: createdAt || new Date().toISOString(),
|
||||
updated_at: updatedAt || null,
|
||||
is_deleted: isDeleted,
|
||||
};
|
||||
|
||||
this.memoryStore.set(historyEntry.id, historyEntry);
|
||||
}
|
||||
|
||||
async getHistory(memoryId: string): Promise<any[]> {
|
||||
return Array.from(this.memoryStore.values())
|
||||
.filter((entry) => entry.memory_id === memoryId)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
new Date(b.created_at).getTime() - new Date(a.created_at).getTime(),
|
||||
)
|
||||
.slice(0, 100);
|
||||
}
|
||||
|
||||
async reset(): Promise<void> {
|
||||
this.memoryStore.clear();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
// No need to close anything for in-memory storage
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import sqlite3 from "sqlite3";
|
||||
import { promisify } from "util";
|
||||
import { HistoryManager } from "./base";
|
||||
|
||||
export class SQLiteManager {
|
||||
export class SQLiteManager implements HistoryManager {
|
||||
private db: sqlite3.Database;
|
||||
|
||||
constructor(dbPath: string) {
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import { createClient, SupabaseClient } from "@supabase/supabase-js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { HistoryManager } from "./base";
|
||||
|
||||
interface HistoryEntry {
|
||||
id: string;
|
||||
memory_id: string;
|
||||
previous_value: string | null;
|
||||
new_value: string | null;
|
||||
action: string;
|
||||
created_at: string;
|
||||
updated_at: string | null;
|
||||
is_deleted: number;
|
||||
}
|
||||
|
||||
interface SupabaseHistoryConfig {
|
||||
supabaseUrl: string;
|
||||
supabaseKey: string;
|
||||
tableName?: string;
|
||||
}
|
||||
|
||||
export class SupabaseHistoryManager implements HistoryManager {
|
||||
private supabase: SupabaseClient;
|
||||
private readonly tableName: string;
|
||||
|
||||
constructor(config: SupabaseHistoryConfig) {
|
||||
this.tableName = config.tableName || "memory_history";
|
||||
this.supabase = createClient(config.supabaseUrl, config.supabaseKey);
|
||||
this.initializeSupabase().catch(console.error);
|
||||
}
|
||||
|
||||
private async initializeSupabase(): Promise<void> {
|
||||
// Check if table exists
|
||||
const { error } = await this.supabase
|
||||
.from(this.tableName)
|
||||
.select("id")
|
||||
.limit(1);
|
||||
|
||||
if (error) {
|
||||
console.error(
|
||||
"Error: Table does not exist. Please run this SQL in your Supabase SQL Editor:",
|
||||
);
|
||||
console.error(`
|
||||
create table ${this.tableName} (
|
||||
id text primary key,
|
||||
memory_id text not null,
|
||||
previous_value text,
|
||||
new_value text,
|
||||
action text not null,
|
||||
created_at timestamp with time zone default timezone('utc', now()),
|
||||
updated_at timestamp with time zone,
|
||||
is_deleted integer default 0
|
||||
);
|
||||
`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async addHistory(
|
||||
memoryId: string,
|
||||
previousValue: string | null,
|
||||
newValue: string | null,
|
||||
action: string,
|
||||
createdAt?: string,
|
||||
updatedAt?: string,
|
||||
isDeleted: number = 0,
|
||||
): Promise<void> {
|
||||
const historyEntry: HistoryEntry = {
|
||||
id: uuidv4(),
|
||||
memory_id: memoryId,
|
||||
previous_value: previousValue,
|
||||
new_value: newValue,
|
||||
action: action,
|
||||
created_at: createdAt || new Date().toISOString(),
|
||||
updated_at: updatedAt || null,
|
||||
is_deleted: isDeleted,
|
||||
};
|
||||
|
||||
const { error } = await this.supabase
|
||||
.from(this.tableName)
|
||||
.insert(historyEntry);
|
||||
|
||||
if (error) {
|
||||
console.error("Error adding history to Supabase:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getHistory(memoryId: string): Promise<any[]> {
|
||||
const { data, error } = await this.supabase
|
||||
.from(this.tableName)
|
||||
.select("*")
|
||||
.eq("memory_id", memoryId)
|
||||
.order("created_at", { ascending: false })
|
||||
.limit(100);
|
||||
|
||||
if (error) {
|
||||
console.error("Error getting history from Supabase:", error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return data || [];
|
||||
}
|
||||
|
||||
async reset(): Promise<void> {
|
||||
const { error } = await this.supabase
|
||||
.from(this.tableName)
|
||||
.delete()
|
||||
.neq("id", "");
|
||||
|
||||
if (error) {
|
||||
console.error("Error resetting Supabase history:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
close(): void {
|
||||
// No need to close anything as connections are handled by the client
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export interface HistoryManager {
|
||||
addHistory(
|
||||
memoryId: string,
|
||||
previousValue: string | null,
|
||||
newValue: string | null,
|
||||
action: string,
|
||||
createdAt?: string,
|
||||
updatedAt?: string,
|
||||
isDeleted?: number,
|
||||
): Promise<void>;
|
||||
getHistory(memoryId: string): Promise<any[]>;
|
||||
reset(): Promise<void>;
|
||||
close(): void;
|
||||
}
|
||||
@@ -1 +1,5 @@
|
||||
export * from "./SQLiteManager";
|
||||
export * from "./DummyHistoryManager";
|
||||
export * from "./SupabaseHistoryManager";
|
||||
export * from "./MemoryHistoryManager";
|
||||
export * from "./base";
|
||||
|
||||
@@ -24,6 +24,16 @@ export interface VectorStoreConfig {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface HistoryStoreConfig {
|
||||
provider: string;
|
||||
config: {
|
||||
historyDbPath?: string;
|
||||
supabaseUrl?: string;
|
||||
supabaseKey?: string;
|
||||
tableName?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LLMConfig {
|
||||
provider?: string;
|
||||
config?: Record<string, any>;
|
||||
@@ -58,6 +68,8 @@ export interface MemoryConfig {
|
||||
provider: string;
|
||||
config: LLMConfig;
|
||||
};
|
||||
historyStore?: HistoryStoreConfig;
|
||||
disableHistory?: boolean;
|
||||
historyDbPath?: string;
|
||||
customPrompt?: string;
|
||||
graphStore?: GraphStoreConfig;
|
||||
@@ -137,4 +149,11 @@ export const MemoryConfigSchema = z.object({
|
||||
customPrompt: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
historyStore: z
|
||||
.object({
|
||||
provider: z.string(),
|
||||
config: z.record(z.string(), z.any()),
|
||||
})
|
||||
.optional(),
|
||||
disableHistory: z.boolean().optional(),
|
||||
});
|
||||
|
||||
@@ -5,13 +5,26 @@ import { OpenAIStructuredLLM } from "../llms/openai_structured";
|
||||
import { AnthropicLLM } from "../llms/anthropic";
|
||||
import { GroqLLM } from "../llms/groq";
|
||||
import { MemoryVectorStore } from "../vector_stores/memory";
|
||||
import { EmbeddingConfig, LLMConfig, VectorStoreConfig } from "../types";
|
||||
import {
|
||||
EmbeddingConfig,
|
||||
HistoryStoreConfig,
|
||||
LLMConfig,
|
||||
VectorStoreConfig,
|
||||
} from "../types";
|
||||
import { Embedder } from "../embeddings/base";
|
||||
import { LLM } from "../llms/base";
|
||||
import { VectorStore } from "../vector_stores/base";
|
||||
import { Qdrant } from "../vector_stores/qdrant";
|
||||
import { RedisDB } from "../vector_stores/redis";
|
||||
import { OllamaLLM } from "../llms/ollama";
|
||||
import { SupabaseDB } from "../vector_stores/supabase";
|
||||
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 {
|
||||
switch (provider.toLowerCase()) {
|
||||
@@ -19,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}`);
|
||||
}
|
||||
@@ -38,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}`);
|
||||
}
|
||||
@@ -53,8 +70,29 @@ export class VectorStoreFactory {
|
||||
return new Qdrant(config as any); // Type assertion needed as config is extended
|
||||
case "redis":
|
||||
return new RedisDB(config as any); // Type assertion needed as config is extended
|
||||
case "supabase":
|
||||
return new SupabaseDB(config as any); // Type assertion needed as config is extended
|
||||
default:
|
||||
throw new Error(`Unsupported vector store provider: ${provider}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class HistoryManagerFactory {
|
||||
static create(provider: string, config: HistoryStoreConfig): HistoryManager {
|
||||
switch (provider.toLowerCase()) {
|
||||
case "sqlite":
|
||||
return new SQLiteManager(config.config.historyDbPath || ":memory:");
|
||||
case "supabase":
|
||||
return new SupabaseHistoryManager({
|
||||
supabaseUrl: config.config.supabaseUrl || "",
|
||||
supabaseKey: config.config.supabaseKey || "",
|
||||
tableName: config.config.tableName || "memory_history",
|
||||
});
|
||||
case "memory":
|
||||
return new MemoryHistoryManager();
|
||||
default:
|
||||
throw new Error(`Unsupported history store provider: ${provider}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import type {
|
||||
TelemetryClient,
|
||||
TelemetryInstance,
|
||||
TelemetryEventData,
|
||||
} from "./telemetry.types";
|
||||
|
||||
let version = "2.1.15";
|
||||
|
||||
// Safely check for process.env in different environments
|
||||
let MEM0_TELEMETRY = true;
|
||||
try {
|
||||
MEM0_TELEMETRY = process?.env?.MEM0_TELEMETRY === "false" ? false : true;
|
||||
} catch (error) {}
|
||||
const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX";
|
||||
const POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/";
|
||||
|
||||
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:
|
||||
distinctId === "anonymous" || distinctId === "anonymous-supabase"
|
||||
? false
|
||||
: true,
|
||||
$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: TelemetryInstance,
|
||||
additionalData: Record<string, any> = {},
|
||||
) {
|
||||
if (!instance.telemetryId) {
|
||||
console.warn("No telemetry ID found for instance");
|
||||
return;
|
||||
}
|
||||
|
||||
const eventData: TelemetryEventData = {
|
||||
function: `${instance.constructor.name}`,
|
||||
method: eventName,
|
||||
api_host: instance.host,
|
||||
timestamp: new Date().toISOString(),
|
||||
client_version: version,
|
||||
client_source: "nodejs",
|
||||
...additionalData,
|
||||
};
|
||||
|
||||
await telemetry.captureEvent(
|
||||
instance.telemetryId,
|
||||
`mem0.${eventName}`,
|
||||
eventData,
|
||||
);
|
||||
}
|
||||
|
||||
export { telemetry, captureClientEvent };
|
||||
@@ -0,0 +1,34 @@
|
||||
export interface TelemetryClient {
|
||||
captureEvent(
|
||||
distinctId: string,
|
||||
eventName: string,
|
||||
properties?: Record<string, any>,
|
||||
): Promise<void>;
|
||||
shutdown(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface TelemetryInstance {
|
||||
telemetryId: string;
|
||||
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;
|
||||
}
|
||||
@@ -23,4 +23,7 @@ export interface VectorStore {
|
||||
filters?: SearchFilters,
|
||||
limit?: number,
|
||||
): Promise<[VectorStoreResult[], number]>;
|
||||
getUserId(): Promise<string>;
|
||||
setUserId(userId: string): Promise<void>;
|
||||
initialize(): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,13 @@ export class MemoryVectorStore implements VectorStore {
|
||||
payload TEXT NOT NULL
|
||||
)
|
||||
`);
|
||||
|
||||
await this.run(`
|
||||
CREATE TABLE IF NOT EXISTS memory_migrations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id TEXT NOT NULL UNIQUE
|
||||
)
|
||||
`);
|
||||
}
|
||||
|
||||
private async run(sql: string, params: any[] = []): Promise<void> {
|
||||
@@ -201,4 +208,33 @@ export class MemoryVectorStore implements VectorStore {
|
||||
|
||||
return [results.slice(0, limit), results.length];
|
||||
}
|
||||
|
||||
async getUserId(): Promise<string> {
|
||||
const row = await this.getOne(
|
||||
`SELECT user_id FROM memory_migrations LIMIT 1`,
|
||||
);
|
||||
if (row) {
|
||||
return row.user_id;
|
||||
}
|
||||
|
||||
// Generate a random user_id if none exists
|
||||
const randomUserId =
|
||||
Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15);
|
||||
await this.run(`INSERT INTO memory_migrations (user_id) VALUES (?)`, [
|
||||
randomUserId,
|
||||
]);
|
||||
return randomUserId;
|
||||
}
|
||||
|
||||
async setUserId(userId: string): Promise<void> {
|
||||
await this.run(`DELETE FROM memory_migrations`);
|
||||
await this.run(`INSERT INTO memory_migrations (user_id) VALUES (?)`, [
|
||||
userId,
|
||||
]);
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await this.init();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,14 @@ export class PGVector implements VectorStore {
|
||||
private useDiskann: boolean;
|
||||
private useHnsw: boolean;
|
||||
private readonly dbName: string;
|
||||
private config: PGVectorConfig;
|
||||
|
||||
constructor(config: PGVectorConfig) {
|
||||
this.collectionName = config.collectionName;
|
||||
this.useDiskann = config.diskann || false;
|
||||
this.useHnsw = config.hnsw || false;
|
||||
this.dbName = config.dbname || "vector_store";
|
||||
this.config = config;
|
||||
|
||||
this.client = new Client({
|
||||
database: "postgres", // Initially connect to default postgres database
|
||||
@@ -33,14 +35,9 @@ export class PGVector implements VectorStore {
|
||||
host: config.host,
|
||||
port: config.port,
|
||||
});
|
||||
|
||||
this.initialize(config, config.embeddingModelDims);
|
||||
}
|
||||
|
||||
private async initialize(
|
||||
config: PGVectorConfig,
|
||||
embeddingModelDims: number,
|
||||
): Promise<void> {
|
||||
async initialize(): Promise<void> {
|
||||
try {
|
||||
await this.client.connect();
|
||||
|
||||
@@ -56,20 +53,28 @@ export class PGVector implements VectorStore {
|
||||
// Connect to the target database
|
||||
this.client = new Client({
|
||||
database: this.dbName,
|
||||
user: config.user,
|
||||
password: config.password,
|
||||
host: config.host,
|
||||
port: config.port,
|
||||
user: this.config.user,
|
||||
password: this.config.password,
|
||||
host: this.config.host,
|
||||
port: this.config.port,
|
||||
});
|
||||
await this.client.connect();
|
||||
|
||||
// Create vector extension
|
||||
await this.client.query("CREATE EXTENSION IF NOT EXISTS vector");
|
||||
|
||||
// Create memory_migrations table
|
||||
await this.client.query(`
|
||||
CREATE TABLE IF NOT EXISTS memory_migrations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id TEXT NOT NULL UNIQUE
|
||||
)
|
||||
`);
|
||||
|
||||
// Check if the collection exists
|
||||
const collections = await this.listCols();
|
||||
if (!collections.includes(this.collectionName)) {
|
||||
await this.createCol(embeddingModelDims);
|
||||
await this.createCol(this.config.embeddingModelDims);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error during initialization:", error);
|
||||
@@ -296,4 +301,32 @@ export class PGVector implements VectorStore {
|
||||
async close(): Promise<void> {
|
||||
await this.client.end();
|
||||
}
|
||||
|
||||
async getUserId(): Promise<string> {
|
||||
const result = await this.client.query(
|
||||
"SELECT user_id FROM memory_migrations LIMIT 1",
|
||||
);
|
||||
|
||||
if (result.rows.length > 0) {
|
||||
return result.rows[0].user_id;
|
||||
}
|
||||
|
||||
// Generate a random user_id if none exists
|
||||
const randomUserId =
|
||||
Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15);
|
||||
await this.client.query(
|
||||
"INSERT INTO memory_migrations (user_id) VALUES ($1)",
|
||||
[randomUserId],
|
||||
);
|
||||
return randomUserId;
|
||||
}
|
||||
|
||||
async setUserId(userId: string): Promise<void> {
|
||||
await this.client.query("DELETE FROM memory_migrations");
|
||||
await this.client.query(
|
||||
"INSERT INTO memory_migrations (user_id) VALUES ($1)",
|
||||
[userId],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,33 +13,7 @@ interface QdrantConfig extends VectorStoreConfig {
|
||||
onDisk?: boolean;
|
||||
collectionName: string;
|
||||
embeddingModelDims: number;
|
||||
}
|
||||
|
||||
type DistanceType = "Cosine" | "Euclid" | "Dot";
|
||||
|
||||
interface QdrantPoint {
|
||||
id: string | number;
|
||||
vector: { name: string; vector: number[] };
|
||||
payload?: Record<string, unknown> | { [key: string]: unknown } | null;
|
||||
shard_key?: string;
|
||||
version?: number;
|
||||
}
|
||||
|
||||
interface QdrantScoredPoint extends QdrantPoint {
|
||||
score: number;
|
||||
version: number;
|
||||
}
|
||||
|
||||
interface QdrantNamedVector {
|
||||
name: string;
|
||||
vector: number[];
|
||||
}
|
||||
|
||||
interface QdrantSearchRequest {
|
||||
vector: { name: string; vector: number[] };
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
filter?: QdrantFilter;
|
||||
dimension?: number;
|
||||
}
|
||||
|
||||
interface QdrantFilter {
|
||||
@@ -54,27 +28,10 @@ interface QdrantCondition {
|
||||
range?: { gte?: number; gt?: number; lte?: number; lt?: number };
|
||||
}
|
||||
|
||||
interface QdrantVectorParams {
|
||||
size: number;
|
||||
distance: "Cosine" | "Euclid" | "Dot" | "Manhattan";
|
||||
on_disk?: boolean;
|
||||
}
|
||||
|
||||
interface QdrantCollectionInfo {
|
||||
config?: {
|
||||
params?: {
|
||||
vectors?: {
|
||||
size: number;
|
||||
distance: "Cosine" | "Euclid" | "Dot" | "Manhattan";
|
||||
on_disk?: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export class Qdrant implements VectorStore {
|
||||
private client: QdrantClient;
|
||||
private readonly collectionName: string;
|
||||
private dimension: number;
|
||||
|
||||
constructor(config: QdrantConfig) {
|
||||
if (config.client) {
|
||||
@@ -107,61 +64,8 @@ export class Qdrant implements VectorStore {
|
||||
}
|
||||
|
||||
this.collectionName = config.collectionName;
|
||||
this.createCol(config.embeddingModelDims, config.onDisk || false);
|
||||
}
|
||||
|
||||
private async createCol(
|
||||
vectorSize: number,
|
||||
onDisk: boolean,
|
||||
distance: DistanceType = "Cosine",
|
||||
): Promise<void> {
|
||||
try {
|
||||
// Check if collection exists
|
||||
const collections = await this.client.getCollections();
|
||||
const exists = collections.collections.some(
|
||||
(col: { name: string }) => col.name === this.collectionName,
|
||||
);
|
||||
|
||||
if (!exists) {
|
||||
const vectorParams: QdrantVectorParams = {
|
||||
size: vectorSize,
|
||||
distance: distance as "Cosine" | "Euclid" | "Dot" | "Manhattan",
|
||||
on_disk: onDisk,
|
||||
};
|
||||
|
||||
try {
|
||||
await this.client.createCollection(this.collectionName, {
|
||||
vectors: vectorParams,
|
||||
});
|
||||
} catch (error: any) {
|
||||
// Handle case where collection was created between our check and create
|
||||
if (error?.status === 409) {
|
||||
// Collection already exists - verify it has the correct configuration
|
||||
const collectionInfo = (await this.client.getCollection(
|
||||
this.collectionName,
|
||||
)) as QdrantCollectionInfo;
|
||||
const vectorConfig = collectionInfo.config?.params?.vectors;
|
||||
|
||||
if (!vectorConfig || vectorConfig.size !== vectorSize) {
|
||||
throw new Error(
|
||||
`Collection ${this.collectionName} exists but has wrong configuration. ` +
|
||||
`Expected vector size: ${vectorSize}, got: ${vectorConfig?.size}`,
|
||||
);
|
||||
}
|
||||
// Collection exists with correct configuration - we can proceed
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error("Error creating/verifying collection:", error.message);
|
||||
} else {
|
||||
console.error("Error creating/verifying collection:", error);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
this.dimension = config.dimension || 1536; // Default OpenAI dimension
|
||||
this.initialize().catch(console.error);
|
||||
}
|
||||
|
||||
private createFilter(filters?: SearchFilters): QdrantFilter | undefined {
|
||||
@@ -293,4 +197,158 @@ export class Qdrant implements VectorStore {
|
||||
|
||||
return [results, response.points.length];
|
||||
}
|
||||
|
||||
private generateUUID(): string {
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
|
||||
/[xy]/g,
|
||||
function (c) {
|
||||
const r = (Math.random() * 16) | 0;
|
||||
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async getUserId(): Promise<string> {
|
||||
try {
|
||||
// First check if the collection exists
|
||||
const collections = await this.client.getCollections();
|
||||
const userCollectionExists = collections.collections.some(
|
||||
(col: { name: string }) => col.name === "memory_migrations",
|
||||
);
|
||||
|
||||
if (!userCollectionExists) {
|
||||
// Create the collection if it doesn't exist
|
||||
await this.client.createCollection("memory_migrations", {
|
||||
vectors: {
|
||||
size: 1,
|
||||
distance: "Cosine",
|
||||
on_disk: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Now try to get the user ID
|
||||
const result = await this.client.scroll("memory_migrations", {
|
||||
limit: 1,
|
||||
with_payload: true,
|
||||
});
|
||||
|
||||
if (result.points.length > 0) {
|
||||
return result.points[0].payload?.user_id as string;
|
||||
}
|
||||
|
||||
// Generate a random user_id if none exists
|
||||
const randomUserId =
|
||||
Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15);
|
||||
|
||||
await this.client.upsert("memory_migrations", {
|
||||
points: [
|
||||
{
|
||||
id: this.generateUUID(),
|
||||
vector: [0],
|
||||
payload: { user_id: randomUserId },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return randomUserId;
|
||||
} catch (error) {
|
||||
console.error("Error getting user ID:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async setUserId(userId: string): Promise<void> {
|
||||
try {
|
||||
// Get existing point ID
|
||||
const result = await this.client.scroll("memory_migrations", {
|
||||
limit: 1,
|
||||
with_payload: true,
|
||||
});
|
||||
|
||||
const pointId =
|
||||
result.points.length > 0 ? result.points[0].id : this.generateUUID();
|
||||
|
||||
await this.client.upsert("memory_migrations", {
|
||||
points: [
|
||||
{
|
||||
id: pointId,
|
||||
vector: [0],
|
||||
payload: { user_id: userId },
|
||||
},
|
||||
],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error setting user ID:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
try {
|
||||
// Create collection if it doesn't exist
|
||||
const collections = await this.client.getCollections();
|
||||
const exists = collections.collections.some(
|
||||
(c) => c.name === this.collectionName,
|
||||
);
|
||||
|
||||
if (!exists) {
|
||||
try {
|
||||
await this.client.createCollection(this.collectionName, {
|
||||
vectors: {
|
||||
size: this.dimension,
|
||||
distance: "Cosine",
|
||||
},
|
||||
});
|
||||
} catch (error: any) {
|
||||
// Handle case where collection was created between our check and create
|
||||
if (error?.status === 409) {
|
||||
// Collection already exists - verify it has the correct configuration
|
||||
const collectionInfo = await this.client.getCollection(
|
||||
this.collectionName,
|
||||
);
|
||||
const vectorConfig = collectionInfo.config?.params?.vectors;
|
||||
|
||||
if (!vectorConfig || vectorConfig.size !== this.dimension) {
|
||||
throw new Error(
|
||||
`Collection ${this.collectionName} exists but has wrong configuration. ` +
|
||||
`Expected vector size: ${this.dimension}, got: ${vectorConfig?.size}`,
|
||||
);
|
||||
}
|
||||
// Collection exists with correct configuration - we can proceed
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create memory_migrations collection if it doesn't exist
|
||||
const userExists = collections.collections.some(
|
||||
(c) => c.name === "memory_migrations",
|
||||
);
|
||||
|
||||
if (!userExists) {
|
||||
try {
|
||||
await this.client.createCollection("memory_migrations", {
|
||||
vectors: {
|
||||
size: 1, // Minimal size since we only store user_id
|
||||
distance: "Cosine",
|
||||
},
|
||||
});
|
||||
} catch (error: any) {
|
||||
// Handle case where collection was created between our check and create
|
||||
if (error?.status === 409) {
|
||||
// Collection already exists - we can proceed
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error initializing Qdrant:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,57 +187,6 @@ export class RedisDB implements VectorStore {
|
||||
});
|
||||
}
|
||||
|
||||
private async initialize(): Promise<void> {
|
||||
try {
|
||||
await this.client.connect();
|
||||
console.log("Connected to Redis");
|
||||
|
||||
// Check if Redis Stack modules are loaded
|
||||
const modulesResponse =
|
||||
(await this.client.moduleList()) as unknown as any[];
|
||||
|
||||
// Parse module list to find search module
|
||||
const hasSearch = modulesResponse.some((module: any[]) => {
|
||||
const moduleMap = new Map();
|
||||
for (let i = 0; i < module.length; i += 2) {
|
||||
moduleMap.set(module[i], module[i + 1]);
|
||||
}
|
||||
return moduleMap.get("name")?.toLowerCase() === "search";
|
||||
});
|
||||
|
||||
if (!hasSearch) {
|
||||
throw new Error(
|
||||
"RediSearch module is not loaded. Please ensure Redis Stack is properly installed and running.",
|
||||
);
|
||||
}
|
||||
|
||||
// Create index with retries
|
||||
let retries = 0;
|
||||
const maxRetries = 3;
|
||||
while (retries < maxRetries) {
|
||||
try {
|
||||
await this.createIndex();
|
||||
console.log("Redis index created successfully");
|
||||
break;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Error creating index (attempt ${retries + 1}/${maxRetries}):`,
|
||||
error,
|
||||
);
|
||||
retries++;
|
||||
if (retries === maxRetries) {
|
||||
throw error;
|
||||
}
|
||||
// Wait before retrying
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error during Redis initialization:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async createIndex(): Promise<void> {
|
||||
try {
|
||||
// Drop existing index if it exists
|
||||
@@ -290,6 +239,61 @@ export class RedisDB implements VectorStore {
|
||||
}
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
try {
|
||||
await this.client.connect();
|
||||
console.log("Connected to Redis");
|
||||
|
||||
// Check if Redis Stack modules are loaded
|
||||
const modulesResponse =
|
||||
(await this.client.moduleList()) as unknown as any[];
|
||||
|
||||
// Parse module list to find search module
|
||||
const hasSearch = modulesResponse.some((module: any[]) => {
|
||||
const moduleMap = new Map();
|
||||
for (let i = 0; i < module.length; i += 2) {
|
||||
moduleMap.set(module[i], module[i + 1]);
|
||||
}
|
||||
return moduleMap.get("name")?.toLowerCase() === "search";
|
||||
});
|
||||
|
||||
if (!hasSearch) {
|
||||
throw new Error(
|
||||
"RediSearch module is not loaded. Please ensure Redis Stack is properly installed and running.",
|
||||
);
|
||||
}
|
||||
|
||||
// Create index with retries
|
||||
let retries = 0;
|
||||
const maxRetries = 3;
|
||||
while (retries < maxRetries) {
|
||||
try {
|
||||
await this.createIndex();
|
||||
console.log("Redis index created successfully");
|
||||
break;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Error creating index (attempt ${retries + 1}/${maxRetries}):`,
|
||||
error,
|
||||
);
|
||||
retries++;
|
||||
if (retries === maxRetries) {
|
||||
throw error;
|
||||
}
|
||||
// Wait before retrying
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error("Error initializing Redis:", error.message);
|
||||
} else {
|
||||
console.error("Error initializing Redis:", error);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async insert(
|
||||
vectors: number[][],
|
||||
ids: string[],
|
||||
@@ -629,4 +633,35 @@ export class RedisDB implements VectorStore {
|
||||
async close(): Promise<void> {
|
||||
await this.client.quit();
|
||||
}
|
||||
|
||||
async getUserId(): Promise<string> {
|
||||
try {
|
||||
// Check if the user ID exists in Redis
|
||||
const userId = await this.client.get("memory_migrations:1");
|
||||
if (userId) {
|
||||
return userId;
|
||||
}
|
||||
|
||||
// Generate a random user_id if none exists
|
||||
const randomUserId =
|
||||
Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15);
|
||||
|
||||
// Store the user ID
|
||||
await this.client.set("memory_migrations:1", randomUserId);
|
||||
return randomUserId;
|
||||
} catch (error) {
|
||||
console.error("Error getting user ID:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async setUserId(userId: string): Promise<void> {
|
||||
try {
|
||||
await this.client.set("memory_migrations:1", userId);
|
||||
} catch (error) {
|
||||
console.error("Error setting user ID:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,421 @@
|
||||
import { createClient, SupabaseClient } from "@supabase/supabase-js";
|
||||
import { VectorStore } from "./base";
|
||||
import { SearchFilters, VectorStoreConfig, VectorStoreResult } from "../types";
|
||||
|
||||
interface VectorData {
|
||||
id: string;
|
||||
embedding: number[];
|
||||
metadata: Record<string, any>;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface VectorQueryParams {
|
||||
query_embedding: number[];
|
||||
match_count: number;
|
||||
filter?: SearchFilters;
|
||||
}
|
||||
|
||||
interface VectorSearchResult {
|
||||
id: string;
|
||||
similarity: number;
|
||||
metadata: Record<string, any>;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface SupabaseConfig extends VectorStoreConfig {
|
||||
supabaseUrl: string;
|
||||
supabaseKey: string;
|
||||
tableName: string;
|
||||
embeddingColumnName?: string;
|
||||
metadataColumnName?: string;
|
||||
}
|
||||
|
||||
/*
|
||||
SQL Migration to run in Supabase SQL Editor:
|
||||
|
||||
-- Enable the vector extension
|
||||
create extension if not exists vector;
|
||||
|
||||
-- Create the memories table
|
||||
create table if not exists memories (
|
||||
id text primary key,
|
||||
embedding vector(1536),
|
||||
metadata jsonb,
|
||||
created_at timestamp with time zone default timezone('utc', now()),
|
||||
updated_at timestamp with time zone default timezone('utc', now())
|
||||
);
|
||||
|
||||
-- Create the memory migrations table
|
||||
create table if not exists memory_migrations (
|
||||
user_id text primary key,
|
||||
created_at timestamp with time zone default timezone('utc', now())
|
||||
);
|
||||
|
||||
-- Create the vector similarity search function
|
||||
create or replace function match_vectors(
|
||||
query_embedding vector(1536),
|
||||
match_count int,
|
||||
filter jsonb default '{}'::jsonb
|
||||
)
|
||||
returns table (
|
||||
id text,
|
||||
similarity float,
|
||||
metadata jsonb
|
||||
)
|
||||
language plpgsql
|
||||
as $$
|
||||
begin
|
||||
return query
|
||||
select
|
||||
t.id::text,
|
||||
1 - (t.embedding <=> query_embedding) as similarity,
|
||||
t.metadata
|
||||
from memories t
|
||||
where case
|
||||
when filter::text = '{}'::text then true
|
||||
else t.metadata @> filter
|
||||
end
|
||||
order by t.embedding <=> query_embedding
|
||||
limit match_count;
|
||||
end;
|
||||
$$;
|
||||
*/
|
||||
|
||||
export class SupabaseDB implements VectorStore {
|
||||
private client: SupabaseClient;
|
||||
private readonly tableName: string;
|
||||
private readonly embeddingColumnName: string;
|
||||
private readonly metadataColumnName: string;
|
||||
|
||||
constructor(config: SupabaseConfig) {
|
||||
this.client = createClient(config.supabaseUrl, config.supabaseKey);
|
||||
this.tableName = config.tableName;
|
||||
this.embeddingColumnName = config.embeddingColumnName || "embedding";
|
||||
this.metadataColumnName = config.metadataColumnName || "metadata";
|
||||
|
||||
this.initialize().catch((err) => {
|
||||
console.error("Failed to initialize Supabase:", err);
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
try {
|
||||
// Verify table exists and vector operations work by attempting a test insert
|
||||
const testVector = Array(1536).fill(0);
|
||||
try {
|
||||
await this.client.from(this.tableName).delete().eq("id", "test_vector");
|
||||
} catch (error) {
|
||||
console.warn("No test vector to delete, safe to ignore.");
|
||||
}
|
||||
const { error: testError } = await this.client
|
||||
.from(this.tableName)
|
||||
.insert({
|
||||
id: "test_vector",
|
||||
[this.embeddingColumnName]: testVector,
|
||||
[this.metadataColumnName]: {},
|
||||
})
|
||||
.select();
|
||||
|
||||
if (testError) {
|
||||
console.error("Test insert error:", testError);
|
||||
throw new Error(
|
||||
`Vector operations failed. Please ensure:
|
||||
1. The vector extension is enabled
|
||||
2. The table "${this.tableName}" exists with correct schema
|
||||
3. The match_vectors function is created
|
||||
|
||||
RUN THE FOLLOWING SQL IN YOUR SUPABASE SQL EDITOR:
|
||||
|
||||
-- Enable the vector extension
|
||||
create extension if not exists vector;
|
||||
|
||||
-- Create the memories table
|
||||
create table if not exists memories (
|
||||
id text primary key,
|
||||
embedding vector(1536),
|
||||
metadata jsonb,
|
||||
created_at timestamp with time zone default timezone('utc', now()),
|
||||
updated_at timestamp with time zone default timezone('utc', now())
|
||||
);
|
||||
|
||||
-- Create the memory migrations table
|
||||
create table if not exists memory_migrations (
|
||||
user_id text primary key,
|
||||
created_at timestamp with time zone default timezone('utc', now())
|
||||
);
|
||||
|
||||
-- Create the vector similarity search function
|
||||
create or replace function match_vectors(
|
||||
query_embedding vector(1536),
|
||||
match_count int,
|
||||
filter jsonb default '{}'::jsonb
|
||||
)
|
||||
returns table (
|
||||
id text,
|
||||
similarity float,
|
||||
metadata jsonb
|
||||
)
|
||||
language plpgsql
|
||||
as $$
|
||||
begin
|
||||
return query
|
||||
select
|
||||
t.id::text,
|
||||
1 - (t.embedding <=> query_embedding) as similarity,
|
||||
t.metadata
|
||||
from memories t
|
||||
where case
|
||||
when filter::text = '{}'::text then true
|
||||
else t.metadata @> filter
|
||||
end
|
||||
order by t.embedding <=> query_embedding
|
||||
limit match_count;
|
||||
end;
|
||||
$$;
|
||||
|
||||
See the SQL migration instructions in the code comments.`,
|
||||
);
|
||||
}
|
||||
|
||||
// Clean up test vector
|
||||
await this.client.from(this.tableName).delete().eq("id", "test_vector");
|
||||
|
||||
console.log("Connected to Supabase successfully");
|
||||
} catch (error) {
|
||||
console.error("Error during Supabase initialization:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async insert(
|
||||
vectors: number[][],
|
||||
ids: string[],
|
||||
payloads: Record<string, any>[],
|
||||
): Promise<void> {
|
||||
try {
|
||||
const data = vectors.map((vector, idx) => ({
|
||||
id: ids[idx],
|
||||
[this.embeddingColumnName]: vector,
|
||||
[this.metadataColumnName]: {
|
||||
...payloads[idx],
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
}));
|
||||
|
||||
const { error } = await this.client.from(this.tableName).insert(data);
|
||||
|
||||
if (error) throw error;
|
||||
} catch (error) {
|
||||
console.error("Error during vector insert:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async search(
|
||||
query: number[],
|
||||
limit: number = 5,
|
||||
filters?: SearchFilters,
|
||||
): Promise<VectorStoreResult[]> {
|
||||
try {
|
||||
const rpcQuery: VectorQueryParams = {
|
||||
query_embedding: query,
|
||||
match_count: limit,
|
||||
};
|
||||
|
||||
if (filters) {
|
||||
rpcQuery.filter = filters;
|
||||
}
|
||||
|
||||
const { data, error } = await this.client.rpc("match_vectors", rpcQuery);
|
||||
|
||||
if (error) throw error;
|
||||
if (!data) return [];
|
||||
|
||||
const results = data as VectorSearchResult[];
|
||||
return results.map((result) => ({
|
||||
id: result.id,
|
||||
payload: result.metadata,
|
||||
score: result.similarity,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("Error during vector search:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async get(vectorId: string): Promise<VectorStoreResult | null> {
|
||||
try {
|
||||
const { data, error } = await this.client
|
||||
.from(this.tableName)
|
||||
.select("*")
|
||||
.eq("id", vectorId)
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
if (!data) return null;
|
||||
|
||||
return {
|
||||
id: data.id,
|
||||
payload: data[this.metadataColumnName],
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error getting vector:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async update(
|
||||
vectorId: string,
|
||||
vector: number[],
|
||||
payload: Record<string, any>,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const { error } = await this.client
|
||||
.from(this.tableName)
|
||||
.update({
|
||||
[this.embeddingColumnName]: vector,
|
||||
[this.metadataColumnName]: {
|
||||
...payload,
|
||||
updated_at: new Date().toISOString(),
|
||||
},
|
||||
})
|
||||
.eq("id", vectorId);
|
||||
|
||||
if (error) throw error;
|
||||
} catch (error) {
|
||||
console.error("Error during vector update:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async delete(vectorId: string): Promise<void> {
|
||||
try {
|
||||
const { error } = await this.client
|
||||
.from(this.tableName)
|
||||
.delete()
|
||||
.eq("id", vectorId);
|
||||
|
||||
if (error) throw error;
|
||||
} catch (error) {
|
||||
console.error("Error deleting vector:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteCol(): Promise<void> {
|
||||
try {
|
||||
const { error } = await this.client
|
||||
.from(this.tableName)
|
||||
.delete()
|
||||
.neq("id", ""); // Delete all rows
|
||||
|
||||
if (error) throw error;
|
||||
} catch (error) {
|
||||
console.error("Error deleting collection:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async list(
|
||||
filters?: SearchFilters,
|
||||
limit: number = 100,
|
||||
): Promise<[VectorStoreResult[], number]> {
|
||||
try {
|
||||
let query = this.client
|
||||
.from(this.tableName)
|
||||
.select("*", { count: "exact" })
|
||||
.limit(limit);
|
||||
|
||||
if (filters) {
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
query = query.eq(`${this.metadataColumnName}->>${key}`, value);
|
||||
});
|
||||
}
|
||||
|
||||
const { data, error, count } = await query;
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
const results = data.map((item: VectorData) => ({
|
||||
id: item.id,
|
||||
payload: item[this.metadataColumnName],
|
||||
}));
|
||||
|
||||
return [results, count || 0];
|
||||
} catch (error) {
|
||||
console.error("Error listing vectors:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getUserId(): Promise<string> {
|
||||
try {
|
||||
// First check if the table exists
|
||||
const { data: tableExists } = await this.client
|
||||
.from("memory_migrations")
|
||||
.select("user_id")
|
||||
.limit(1);
|
||||
|
||||
if (!tableExists || tableExists.length === 0) {
|
||||
// Generate a random user_id
|
||||
const randomUserId =
|
||||
Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15);
|
||||
|
||||
// Insert the new user_id
|
||||
const { error: insertError } = await this.client
|
||||
.from("memory_migrations")
|
||||
.insert({ user_id: randomUserId });
|
||||
|
||||
if (insertError) throw insertError;
|
||||
return randomUserId;
|
||||
}
|
||||
|
||||
// Get the first user_id
|
||||
const { data, error } = await this.client
|
||||
.from("memory_migrations")
|
||||
.select("user_id")
|
||||
.limit(1);
|
||||
|
||||
if (error) throw error;
|
||||
if (!data || data.length === 0) {
|
||||
// Generate a random user_id if no data found
|
||||
const randomUserId =
|
||||
Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15);
|
||||
|
||||
const { error: insertError } = await this.client
|
||||
.from("memory_migrations")
|
||||
.insert({ user_id: randomUserId });
|
||||
|
||||
if (insertError) throw insertError;
|
||||
return randomUserId;
|
||||
}
|
||||
|
||||
return data[0].user_id;
|
||||
} catch (error) {
|
||||
console.error("Error getting user ID:", error);
|
||||
return "anonymous-supabase";
|
||||
}
|
||||
}
|
||||
|
||||
async setUserId(userId: string): Promise<void> {
|
||||
try {
|
||||
const { error: deleteError } = await this.client
|
||||
.from("memory_migrations")
|
||||
.delete()
|
||||
.neq("user_id", "");
|
||||
|
||||
if (deleteError) throw deleteError;
|
||||
|
||||
const { error: insertError } = await this.client
|
||||
.from("memory_migrations")
|
||||
.insert({ user_id: userId });
|
||||
|
||||
if (insertError) throw insertError;
|
||||
} catch (error) {
|
||||
console.error("Error setting user ID:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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
|
||||
|
||||
+28
-13
@@ -3,7 +3,6 @@ import os
|
||||
import warnings
|
||||
from functools import wraps
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from enum import Enum
|
||||
|
||||
import httpx
|
||||
|
||||
@@ -131,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()
|
||||
@@ -448,7 +448,7 @@ class MemoryClient:
|
||||
Returns:
|
||||
Dict containing the exported data
|
||||
"""
|
||||
response = self.client.get("/v1/exports/", params=self._prepare_params(kwargs))
|
||||
response = self.client.post("/v1/exports/get/", json=self._prepare_params(kwargs))
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.get_memory_export", self, {"keys": list(kwargs.keys())})
|
||||
return response.json()
|
||||
@@ -618,6 +618,23 @@ class MemoryClient:
|
||||
capture_client_event("client.delete_webhook", self, {"webhook_id": webhook_id})
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
def feedback(
|
||||
self, memory_id: str, feedback: Optional[str] = None, feedback_reason: Optional[str] = None
|
||||
) -> Dict[str, str]:
|
||||
VALID_FEEDBACK_VALUES = {"POSITIVE", "NEGATIVE", "VERY_NEGATIVE"}
|
||||
|
||||
feedback = feedback.upper() if feedback else None
|
||||
if feedback is not None and feedback not in VALID_FEEDBACK_VALUES:
|
||||
raise ValueError(f'feedback must be one of {", ".join(VALID_FEEDBACK_VALUES)} or None')
|
||||
|
||||
data = {"memory_id": memory_id, "feedback": feedback, "feedback_reason": feedback_reason}
|
||||
|
||||
response = self.client.post("/v1/feedback/", json=data)
|
||||
response.raise_for_status()
|
||||
capture_client_event("client.feedback", self, data)
|
||||
return response.json()
|
||||
|
||||
def _prepare_payload(
|
||||
self, messages: Union[str, List[Dict[str, str]], None], kwargs: Dict[str, Any]
|
||||
) -> Dict[str, Any]:
|
||||
@@ -913,7 +930,7 @@ class AsyncMemoryClient:
|
||||
Returns:
|
||||
Dict containing the exported data
|
||||
"""
|
||||
response = await self.async_client.get("/v1/exports/", params=self._prepare_params(kwargs))
|
||||
response = await self.async_client.post("/v1/exports/get/", json=self._prepare_params(kwargs))
|
||||
response.raise_for_status()
|
||||
capture_client_event("async_client.get_memory_export", self.sync_client, {"keys": list(kwargs.keys())})
|
||||
return response.json()
|
||||
@@ -1001,20 +1018,18 @@ class AsyncMemoryClient:
|
||||
return response.json()
|
||||
|
||||
@api_error_handler
|
||||
async def feedback(self, memory_id: str, feedback: Optional[str] = None, feedback_reason: Optional[str] = None) -> Dict[str, str]:
|
||||
async def feedback(
|
||||
self, memory_id: str, feedback: Optional[str] = None, feedback_reason: Optional[str] = None
|
||||
) -> Dict[str, str]:
|
||||
VALID_FEEDBACK_VALUES = {"POSITIVE", "NEGATIVE", "VERY_NEGATIVE"}
|
||||
|
||||
feedback = feedback.upper() if feedback else None
|
||||
if feedback is not None and feedback not in VALID_FEEDBACK_VALUES:
|
||||
raise ValueError(f'feedback must be one of {", ".join(VALID_FEEDBACK_VALUES)} or None')
|
||||
|
||||
data = {
|
||||
"memory_id": memory_id,
|
||||
"feedback": feedback,
|
||||
"feedback_reason": feedback_reason
|
||||
}
|
||||
|
||||
data = {"memory_id": memory_id, "feedback": feedback, "feedback_reason": feedback_reason}
|
||||
|
||||
response = await self.async_client.post("/v1/feedback/", json=data)
|
||||
response.raise_for_status()
|
||||
capture_client_event("async_client.feedback", self.sync_client, data)
|
||||
return response.json()
|
||||
return response.json()
|
||||
|
||||
@@ -30,6 +30,8 @@ class BaseEmbedderConfig(ABC):
|
||||
memory_add_embedding_type: Optional[str] = None,
|
||||
memory_update_embedding_type: Optional[str] = None,
|
||||
memory_search_embedding_type: Optional[str] = None,
|
||||
# LM Studio specific
|
||||
lmstudio_base_url: Optional[str] = "http://localhost:1234/v1",
|
||||
):
|
||||
"""
|
||||
Initializes a configuration class instance for the Embeddings.
|
||||
@@ -58,6 +60,8 @@ class BaseEmbedderConfig(ABC):
|
||||
:type memory_update_embedding_type: Optional[str], optional
|
||||
:param memory_search_embedding_type: The type of embedding to use for the search memory action, defaults to None
|
||||
:type memory_search_embedding_type: Optional[str], optional
|
||||
:param lmstudio_base_url: LM Studio base URL to be use, defaults to "http://localhost:1234/v1"
|
||||
:type lmstudio_base_url: Optional[str], optional
|
||||
"""
|
||||
|
||||
self.model = model
|
||||
@@ -82,3 +86,6 @@ class BaseEmbedderConfig(ABC):
|
||||
self.memory_add_embedding_type = memory_add_embedding_type
|
||||
self.memory_update_embedding_type = memory_update_embedding_type
|
||||
self.memory_search_embedding_type = memory_search_embedding_type
|
||||
|
||||
# LM Studio specific
|
||||
self.lmstudio_base_url = lmstudio_base_url
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class MemoryType(Enum):
|
||||
SEMANTIC = "semantic_memory"
|
||||
EPISODIC = "episodic_memory"
|
||||
PROCEDURAL = "procedural_memory"
|
||||
@@ -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,
|
||||
@@ -39,6 +39,8 @@ class BaseLlmConfig(ABC):
|
||||
deepseek_base_url: Optional[str] = None,
|
||||
# XAI specific
|
||||
xai_base_url: Optional[str] = None,
|
||||
# LM Studio specific
|
||||
lmstudio_base_url: Optional[str] = "http://localhost:1234/v1",
|
||||
):
|
||||
"""
|
||||
Initializes a configuration class instance for the LLM.
|
||||
@@ -83,6 +85,8 @@ class BaseLlmConfig(ABC):
|
||||
:type deepseek_base_url: Optional[str], optional
|
||||
:param xai_base_url: XAI base URL to be use, defaults to None
|
||||
:type xai_base_url: Optional[str], optional
|
||||
:param lmstudio_base_url: LM Studio base URL to be use, defaults to "http://localhost:1234/v1"
|
||||
:type lmstudio_base_url: Optional[str], optional
|
||||
"""
|
||||
|
||||
self.model = model
|
||||
@@ -116,3 +120,6 @@ class BaseLlmConfig(ABC):
|
||||
|
||||
# XAI specific
|
||||
self.xai_base_url = xai_base_url
|
||||
|
||||
# LM Studio specific
|
||||
self.lmstudio_base_url = lmstudio_base_url
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user