Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f77a084d1b | ||
|
|
33abf772ce | ||
|
|
c3c9205ffa | ||
|
|
9f204dc557 | ||
|
|
0e98773efb | ||
|
|
4431bd7d51 | ||
|
|
0354ab0d6b | ||
|
|
6dfc193296 | ||
|
|
9be6850b9d | ||
|
|
d33547a77a | ||
|
|
d77bed2d5d | ||
|
|
9c89e0ec95 | ||
|
|
ca1ee2d2d7 | ||
|
|
05f9607282 | ||
|
|
d9236de4ed | ||
|
|
942727fec6 | ||
|
|
72396e307d | ||
|
|
881cf5b5a6 | ||
|
|
5327d6e50d | ||
|
|
15a3e20371 | ||
|
|
19d7beef43 | ||
|
|
8b789adb15 | ||
|
|
fd065fe9cc | ||
|
|
b5127f7c62 | ||
|
|
37d9fed690 | ||
|
|
31861e9acb | ||
|
|
07462adc9a | ||
|
|
616313b8b5 | ||
|
|
44f2490667 | ||
|
|
3cc794fb98 | ||
|
|
ff6b251c66 | ||
|
|
55df395fd6 | ||
|
|
244e60cea6 | ||
|
|
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 |
@@ -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 pinecone pinecone-text
|
||||
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,287 @@
|
||||
---
|
||||
title: "Product Updates"
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Python">
|
||||
|
||||
<Update label="2025-04-11" description="v0.1.89">
|
||||
|
||||
**New Features:**
|
||||
- **Langchain Integration:** Added support for Langchain VectorStores
|
||||
- **Examples:**
|
||||
- Added personal assistant example
|
||||
- Added personal study buddy example
|
||||
- Added YouTube assistant Chrome extension example
|
||||
- Added agno example
|
||||
- Updated OpenAI Responses API examples
|
||||
- **Vector Store:** Added capability to store user_id in vector database
|
||||
- **Async Memory:** Added async support for OSS
|
||||
|
||||
**Improvements:**
|
||||
- **Documentation:** Updated formatting and examples
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-09" description="v0.1.87">
|
||||
|
||||
**New Features:**
|
||||
- **Upstash Vector:** Added support for Upstash Vector store
|
||||
|
||||
**Improvements:**
|
||||
- **Code Quality:** Removed redundant code lines
|
||||
- **Build:** Updated MAKEFILE
|
||||
- **Documentation:** Updated memory export documentation
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-07" description="v0.1.86">
|
||||
|
||||
**Improvements:**
|
||||
- **FAISS:** Added embedding_dims parameter to FAISS vector store
|
||||
|
||||
</Update>
|
||||
|
||||
<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-15" description="v2.1.17">
|
||||
**New Features:**
|
||||
- **OSS SDK:** Added support for Langchain LLM
|
||||
- **OSS SDK:** Added support for Langchain Embedder
|
||||
- **OSS SDK:** Added support for Langchain Vector Store
|
||||
- **OSS SDK:** Added support for Azure OpenAI Embedder
|
||||
|
||||
|
||||
**Improvements:**
|
||||
- **OSS SDK:** Changed `model` in LLM and Embedder to use type any from `string` to use langchain llm models
|
||||
- **OSS SDK:** Added client to vector store config for langchain vector store
|
||||
- **OSS SDK:** - Updated Azure OpenAI to use new OpenAI SDK
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-11" description="v2.1.16-patch.1">
|
||||
**Bug Fixes:**
|
||||
- **Azure OpenAI:** Fixed issues with Azure OpenAI
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-11" description="v2.1.16">
|
||||
**New Features:**
|
||||
- **Azure OpenAI:** Added support for Azure OpenAI
|
||||
- **Mistral LLM:** Added Mistral LLM integration in OSS
|
||||
|
||||
**Improvements:**
|
||||
- **Zod:** Updated Zod to 3.24.1 to avoid conflicts with other packages
|
||||
</Update>
|
||||
|
||||
<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-04-12" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Memory Management:**
|
||||
- Added ability to delete memories from Project level with filters
|
||||
- Added delete memories capability on Memories Page
|
||||
- **Memory Visualization:** Released V1 Graph Memory Visualization
|
||||
- **Graph Playground:** Enabled for @mem0.ai users
|
||||
- **Notifications:** Added email alerts to organization owners when new members join
|
||||
- **Memory Export:** Added date support for filtering memory exports
|
||||
|
||||
**Improvements:**
|
||||
- **Performance:**
|
||||
- Optimized graph for better performance
|
||||
- Optimized database calls in ADD method
|
||||
- **Analytics:** Added flagging of paid users in Posthog
|
||||
- **CI/CD:** Improved CI pipeline and fixed lint issues
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-10" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Notifications:** Implemented email notifications for organization owners when new members join
|
||||
|
||||
**Improvements:**
|
||||
- **CI/CD:** Fixed Dockerfile for CI tests
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-09" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Integrations:** Updated chat model for Together Qwen
|
||||
- **Platform:** Removed older platforms
|
||||
- **Bug Fixes:** Fixed FILTER_MAPPING
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-03" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Memory:** Added implicit memory capabilities
|
||||
- **API:** Improved implicit lambda and get_all v2 functionality
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-02" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Integrations:** Added Clay integration
|
||||
|
||||
**Improvements:**
|
||||
- **Integrations:** Removed deepseek coder from Together
|
||||
- **API:** Added custom instructions for add v2
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-03-31" description="">
|
||||
|
||||
**Security:**
|
||||
- **Validation:** Added key validation in messages
|
||||
|
||||
</Update>
|
||||
|
||||
<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 |
|
||||
|
||||
@@ -6,7 +6,8 @@ To use Azure OpenAI embedding models, set the `EMBEDDING_AZURE_OPENAI_API_KEY`,
|
||||
|
||||
### Usage
|
||||
|
||||
```python
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
@@ -46,6 +47,36 @@ messages = [
|
||||
m.add(messages, user_id="john")
|
||||
```
|
||||
|
||||
```typescript TypeScript
|
||||
import { Memory } from 'mem0ai/oss';
|
||||
|
||||
const config = {
|
||||
embedder: {
|
||||
provider: "azure_openai",
|
||||
config: {
|
||||
model: "text-embedding-3-large",
|
||||
modelProperties: {
|
||||
endpoint: "your-api-base-url",
|
||||
deployment: "your-deployment-name",
|
||||
apiVersion: "version-to-use",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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: "john" });
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Config
|
||||
|
||||
Here are the parameters available for configuring Azure OpenAI embedder:
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
---
|
||||
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"})
|
||||
```
|
||||
|
||||
```typescript TypeScript
|
||||
import { Memory } from "mem0ai";
|
||||
import { OpenAIEmbeddings } from "@langchain/openai";
|
||||
|
||||
const embeddings = new OpenAIEmbeddings();
|
||||
const config = {
|
||||
"embedder": {
|
||||
"provider": "langchain",
|
||||
"config": {
|
||||
"model": embeddings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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." }
|
||||
]
|
||||
|
||||
memory.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 |
|
||||
|
||||
@@ -2,11 +2,17 @@
|
||||
title: Azure OpenAI
|
||||
---
|
||||
|
||||
<Note> Mem0 Now Supports Azure OpenAI Models in TypeScript SDK </Note>
|
||||
|
||||
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
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
@@ -45,7 +51,38 @@ messages = [
|
||||
m.add(messages, user_id="alice", metadata={"category": "movies"})
|
||||
```
|
||||
|
||||
We also support the new [OpenAI structured-outputs](https://platform.openai.com/docs/guides/structured-outputs/introduction) model.
|
||||
```typescript TypeScript
|
||||
import { Memory } from 'mem0ai/oss';
|
||||
|
||||
const config = {
|
||||
llm: {
|
||||
provider: 'azure_openai',
|
||||
config: {
|
||||
apiKey: process.env.AZURE_OPENAI_API_KEY || '',
|
||||
modelProperties: {
|
||||
endpoint: 'https://your-api-base-url',
|
||||
deployment: 'your-deployment-name',
|
||||
modelName: 'your-model-name',
|
||||
apiVersion: 'version-to-use',
|
||||
// Any other parameters you want to pass to the Azure OpenAI API
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
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>
|
||||
|
||||
|
||||
We also support the new [OpenAI structured-outputs](https://platform.openai.com/docs/guides/structured-outputs/introduction) model. Typescript SDK does not support the `azure_openai_structured` model yet.
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
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"})
|
||||
```
|
||||
|
||||
```typescript TypeScript
|
||||
import { Memory } from "mem0ai";
|
||||
import { ChatOpenAI } from "@langchain/openai";
|
||||
|
||||
const openai_model = new ChatOpenAI({
|
||||
model: "gpt-4o",
|
||||
temperature: 0.2,
|
||||
max_tokens: 2000
|
||||
})
|
||||
|
||||
const config = {
|
||||
"llm": {
|
||||
"provider": "langchain",
|
||||
"config": {
|
||||
"model": openai_model
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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." }
|
||||
]
|
||||
|
||||
memory.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).
|
||||
@@ -2,11 +2,12 @@
|
||||
title: Mistral AI
|
||||
---
|
||||
|
||||
To use mistral's models, please Obtain the Mistral AI api key from their [console](https://console.mistral.ai/). Set the `MISTRAL_API_KEY` environment variable to use the model as given below in the example.
|
||||
To use mistral's models, please obtain the Mistral AI api key from their [console](https://console.mistral.ai/). Set the `MISTRAL_API_KEY` environment variable to use the model as given below in the example.
|
||||
|
||||
## Usage
|
||||
|
||||
```python
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
@@ -34,6 +35,32 @@ messages = [
|
||||
m.add(messages, user_id="alice", metadata={"category": "movies"})
|
||||
```
|
||||
|
||||
```typescript TypeScript
|
||||
import { Memory } from 'mem0ai/oss';
|
||||
|
||||
const config = {
|
||||
llm: {
|
||||
provider: 'mistral',
|
||||
config: {
|
||||
apiKey: process.env.MISTRAL_API_KEY || '',
|
||||
model: 'mistral-tiny-latest', // Or 'mistral-small-latest', 'mistral-medium-latest', etc.
|
||||
temperature: 0.1,
|
||||
maxTokens: 2000,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
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>
|
||||
|
||||
## Config
|
||||
|
||||
All available parameters for the `litellm` config are present in [Master List of All Params in Config](../config).
|
||||
@@ -92,10 +92,6 @@ config = {
|
||||
m = Memory.from_config(config)
|
||||
```
|
||||
|
||||
<Note>
|
||||
OpenAI structured-outputs is currently only available in the Python implementation.
|
||||
</Note>
|
||||
|
||||
## Config
|
||||
|
||||
All available parameters for the `openai` config are present in [Master List of All Params in Config](../config).
|
||||
All available parameters for the `openai` config are present in [Master List of All Params in Config](../config).
|
||||
|
||||
@@ -19,7 +19,7 @@ config = {
|
||||
"llm": {
|
||||
"provider": "xai",
|
||||
"config": {
|
||||
"model": "grok-2-latest",
|
||||
"model": "grok-3-beta",
|
||||
"temperature": 0.1,
|
||||
"max_tokens": 2000,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
+4
@@ -1,3 +1,7 @@
|
||||
---
|
||||
title: Azure AI Search
|
||||
---
|
||||
|
||||
[Azure AI Search](https://learn.microsoft.com/azure/search/search-what-is-azure-search/) (formerly known as "Azure Cognitive Search") provides secure information retrieval at scale over user-owned content in traditional and generative AI search applications.
|
||||
|
||||
## Usage
|
||||
@@ -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,112 @@
|
||||
---
|
||||
title: LangChain
|
||||
---
|
||||
|
||||
Mem0 supports LangChain as a provider for vector store integration. LangChain provides a unified interface to various vector databases, making it easy to integrate different vector store providers through a consistent API.
|
||||
|
||||
<Note>
|
||||
When using LangChain as your vector store provider, you must set the collection name to "mem0". This is a required configuration for proper integration with Mem0.
|
||||
</Note>
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
from langchain_community.vectorstores import Chroma
|
||||
from langchain_openai import OpenAIEmbeddings
|
||||
|
||||
# Initialize a LangChain vector store
|
||||
embeddings = OpenAIEmbeddings()
|
||||
vector_store = Chroma(
|
||||
persist_directory="./chroma_db",
|
||||
embedding_function=embeddings,
|
||||
collection_name="mem0" # Required collection name
|
||||
)
|
||||
|
||||
# Pass the initialized vector store to the config
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "langchain",
|
||||
"config": {
|
||||
"client": vector_store
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"})
|
||||
```
|
||||
|
||||
```typescript TypeScript
|
||||
import { Memory } from "mem0ai";
|
||||
import { OpenAIEmbeddings } from "@langchain/openai";
|
||||
import { MemoryVectorStore as LangchainMemoryStore } from "langchain/vectorstores/memory";
|
||||
|
||||
const embeddings = new OpenAIEmbeddings();
|
||||
const vectorStore = new LangchainVectorStore(embeddings);
|
||||
|
||||
const config = {
|
||||
"vector_store": {
|
||||
"provider": "langchain",
|
||||
"config": { "client": vectorStore }
|
||||
}
|
||||
}
|
||||
|
||||
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." }
|
||||
]
|
||||
|
||||
memory.add(messages, user_id="alice", metadata={"category": "movies"})
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Supported LangChain Vector Stores
|
||||
|
||||
LangChain supports a wide range of vector store providers, including:
|
||||
|
||||
- Chroma
|
||||
- FAISS
|
||||
- Pinecone
|
||||
- Weaviate
|
||||
- Milvus
|
||||
- Qdrant
|
||||
- And many more
|
||||
|
||||
You can use any of these vector store instances directly in your configuration. For a complete and up-to-date list of available providers, refer to the [LangChain Vector Stores documentation](https://python.langchain.com/docs/integrations/vectorstores).
|
||||
|
||||
## Limitations
|
||||
|
||||
When using LangChain as a vector store provider, there are some limitations to be aware of:
|
||||
|
||||
1. **Bulk Operations**: The `get_all` and `delete_all` operations are not supported when using LangChain as the vector store provider. This is because LangChain's vector store interface doesn't provide standardized methods for these bulk operations across all providers.
|
||||
|
||||
2. **Provider-Specific Features**: Some advanced features may not be available depending on the specific vector store implementation you're using through LangChain.
|
||||
|
||||
## Provider-Specific Configuration
|
||||
|
||||
When using LangChain as a vector store provider, you'll need to:
|
||||
|
||||
1. Set the appropriate environment variables for your chosen vector store provider
|
||||
2. Import and initialize the specific vector store class you want to use
|
||||
3. Pass the initialized vector store 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` vector store config are present in [Master List of All Params in Config](../config).
|
||||
@@ -1,6 +1,6 @@
|
||||
[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-ada-002 uses 1536 dimensions.
|
||||
> **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
|
||||
|
||||
|
||||
@@ -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,15 +18,18 @@ 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="Pinecone" href="/components/vectordbs/dbs/pinecone"></Card>
|
||||
<Card title="Azure AI Search" href="/components/vectordbs/dbs/azure_ai_search"></Card>
|
||||
<Card title="Azure" href="/components/vectordbs/dbs/azure"></Card>
|
||||
<Card title="Redis" href="/components/vectordbs/dbs/redis"></Card>
|
||||
<Card title="Elasticsearch" href="/components/vectordbs/dbs/elasticsearch"></Card>
|
||||
<Card title="OpenSearch" href="/components/vectordbs/dbs/opensearch"></Card>
|
||||
<Card title="Supabase" href="/components/vectordbs/dbs/supabase"></Card>
|
||||
<Card title="Vertex AI Vector Search" href="/components/vectordbs/dbs/vertex_ai_vector_search"></Card>
|
||||
<Card title="Vertex AI" href="/components/vectordbs/dbs/vertex_ai"></Card>
|
||||
<Card title="Weaviate" href="/components/vectordbs/dbs/weaviate"></Card>
|
||||
<Card title="FAISS" href="/components/vectordbs/dbs/faiss"></Card>
|
||||
<Card title="LangChain" href="/components/vectordbs/dbs/langchain"></Card>
|
||||
</CardGroup>
|
||||
|
||||
## Usage
|
||||
|
||||
+55
-11
@@ -73,6 +73,7 @@
|
||||
"group": "Features",
|
||||
"icon": "wrench",
|
||||
"pages": [
|
||||
"open-source/features/async-memory",
|
||||
"features/openai_compatibility",
|
||||
"features/custom-fact-extraction-prompt",
|
||||
"features/custom-update-memory-prompt",
|
||||
@@ -110,7 +111,9 @@
|
||||
"components/llms/models/aws_bedrock",
|
||||
"components/llms/models/gemini",
|
||||
"components/llms/models/deepseek",
|
||||
"components/llms/models/xAI"
|
||||
"components/llms/models/xAI",
|
||||
"components/llms/models/lmstudio",
|
||||
"components/llms/models/langchain"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -130,13 +133,15 @@
|
||||
"components/vectordbs/dbs/pgvector",
|
||||
"components/vectordbs/dbs/milvus",
|
||||
"components/vectordbs/dbs/pinecone",
|
||||
"components/vectordbs/dbs/azure_ai_search",
|
||||
"components/vectordbs/dbs/azure",
|
||||
"components/vectordbs/dbs/redis",
|
||||
"components/vectordbs/dbs/elasticsearch",
|
||||
"components/vectordbs/dbs/opensearch",
|
||||
"components/vectordbs/dbs/supabase",
|
||||
"components/vectordbs/dbs/vertex_ai_vector_search",
|
||||
"components/vectordbs/dbs/weaviate"
|
||||
"components/vectordbs/dbs/vertex_ai",
|
||||
"components/vectordbs/dbs/weaviate",
|
||||
"components/vectordbs/dbs/faiss",
|
||||
"components/vectordbs/dbs/langchain"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -156,7 +161,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"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -180,9 +188,10 @@
|
||||
"group": "💡 Examples",
|
||||
"icon": "lightbulb",
|
||||
"pages": [
|
||||
"examples/overview",
|
||||
"examples",
|
||||
"examples/mem0-demo",
|
||||
"examples/ai_companion_js",
|
||||
"examples/mem0-mastra",
|
||||
"examples/mem0-with-ollama",
|
||||
"examples/personal-ai-tutor",
|
||||
"examples/customer-support-agent",
|
||||
@@ -193,7 +202,10 @@
|
||||
"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",
|
||||
"examples/youtube-assistant"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -205,8 +217,9 @@
|
||||
"group": "Integrations",
|
||||
"icon": "plug",
|
||||
"pages": [
|
||||
"integrations/overview",
|
||||
"integrations",
|
||||
"integrations/vercel-ai-sdk",
|
||||
"integrations/flowise",
|
||||
"integrations/crewai",
|
||||
"integrations/autogen",
|
||||
"integrations/langchain",
|
||||
@@ -214,7 +227,12 @@
|
||||
"integrations/llama-index",
|
||||
"integrations/langchain-tools",
|
||||
"integrations/dify",
|
||||
"integrations/mcp-server"
|
||||
"integrations/mcp-server",
|
||||
"integrations/livekit",
|
||||
"integrations/elevenlabs",
|
||||
"integrations/pipecat",
|
||||
"integrations/agno",
|
||||
"integrations/keywords"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -227,7 +245,7 @@
|
||||
"group": "API Reference",
|
||||
"icon": "terminal",
|
||||
"pages": [
|
||||
"api-reference/overview",
|
||||
"api-reference",
|
||||
{
|
||||
"group": "Memory APIs",
|
||||
"icon": "microchip",
|
||||
@@ -245,7 +263,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"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -268,6 +287,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",
|
||||
@@ -281,6 +312,19 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Changelog",
|
||||
"icon": "clock",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Product Updates",
|
||||
"icon": "rocket",
|
||||
"pages": [
|
||||
"changelog"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -47,6 +47,10 @@ Explore how **Mem0** can power real-world applications and bring personalized, i
|
||||
Add **long-term memory** to ChatGPT, Claude, or Perplexity via the **Mem0 Chrome Extension** — personalize your AI chats anywhere.
|
||||
</Card>
|
||||
|
||||
<Card title="YouTube Assistant" icon="puzzle-piece" href="/examples/youtube-assistant">
|
||||
Integrate **Mem0** into **YouTube's** native UI, providing personalized responses with video context.
|
||||
</Card>
|
||||
|
||||
<Card title="Document Writing Assistant" icon="pen" href="/examples/document-writing">
|
||||
Create a **Writing Assistant** that understands and adapts to your unique style, improving consistency and productivity.
|
||||
</Card>
|
||||
@@ -55,7 +59,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 +70,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>
|
||||
@@ -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...
|
||||
```
|
||||
@@ -20,6 +20,7 @@ pip install openai mem0ai
|
||||
Below is the complete code to create and interact with a Personalized AI Tutor using Mem0:
|
||||
|
||||
```python
|
||||
import os
|
||||
from openai import OpenAI
|
||||
from mem0 import Memory
|
||||
|
||||
@@ -54,22 +55,21 @@ class PersonalAITutor:
|
||||
:param question: The question to ask the AI.
|
||||
:param user_id: Optional user ID to associate with the memory.
|
||||
"""
|
||||
# Start a streaming chat completion request to the AI
|
||||
stream = self.client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
stream=True,
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a personal AI Tutor."},
|
||||
{"role": "user", "content": question}
|
||||
]
|
||||
# Start a streaming response request to the AI
|
||||
response = self.client.responses.create(
|
||||
model="gpt-4o",
|
||||
instructions="You are a personal AI Tutor.",
|
||||
input=question,
|
||||
stream=True
|
||||
)
|
||||
|
||||
# Store the question in memory
|
||||
self.memory.add(question, user_id=user_id, metadata={"app_id": self.app_id})
|
||||
|
||||
# Print the response from the AI in real-time
|
||||
for chunk in stream:
|
||||
if chunk.choices[0].delta.content is not None:
|
||||
print(chunk.choices[0].delta.content, end="")
|
||||
for event in response:
|
||||
if event.type == "response.output_text.delta":
|
||||
print(event.delta, end="")
|
||||
|
||||
def get_memories(self, user_id=None):
|
||||
"""
|
||||
|
||||
@@ -63,18 +63,23 @@ class PersonalTravelAssistant:
|
||||
def ask_question(self, question, user_id):
|
||||
# Fetch previous related memories
|
||||
previous_memories = self.search_memories(question, user_id=user_id)
|
||||
prompt = question
|
||||
if previous_memories:
|
||||
prompt = f"User input: {question}\n Previous memories: {previous_memories}"
|
||||
self.messages.append({"role": "user", "content": prompt})
|
||||
|
||||
# Generate response using GPT-4o
|
||||
response = self.client.chat.completions.create(
|
||||
# Build the prompt
|
||||
system_message = "You are a personal AI Assistant."
|
||||
|
||||
if previous_memories:
|
||||
prompt = f"{system_message}\n\nUser input: {question}\nPrevious memories: {', '.join(previous_memories)}"
|
||||
else:
|
||||
prompt = f"{system_message}\n\nUser input: {question}"
|
||||
|
||||
# Generate response using Responses API
|
||||
response = self.client.responses.create(
|
||||
model="gpt-4o",
|
||||
messages=self.messages
|
||||
input=prompt
|
||||
)
|
||||
answer = response.choices[0].message.content
|
||||
self.messages.append({"role": "assistant", "content": answer})
|
||||
|
||||
# Extract answer from the response
|
||||
answer = response.output[0].content[0].text
|
||||
|
||||
# Store the question in memory
|
||||
self.memory.add(question, user_id=user_id)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: YouTube Assistant Extension
|
||||
---
|
||||
|
||||
Enhance your YouTube experience with Mem0's **YouTube Assistant**, a Chrome extension that brings AI-powered chat directly to your YouTube videos. Get instant, personalized answers about video content while leveraging your own knowledge and memories - all without leaving the page.
|
||||
|
||||
## Features
|
||||
|
||||
- **Contextual AI Chat**: Ask questions about videos you're watching
|
||||
- **Seamless Integration**: Chat interface sits alongside YouTube's native UI
|
||||
- **Memory Integration**: Personalized responses based on your knowledge through Mem0
|
||||
- **Real-Time Memory**: Memories are updated in real-time based on your interactions
|
||||
|
||||
## Demo Video
|
||||
|
||||
<video
|
||||
autoPlay
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
width="700"
|
||||
height="400"
|
||||
src="https://github.com/user-attachments/assets/c0334ccd-311b-4dd7-8034-ef88204fc751"
|
||||
></video>
|
||||
|
||||
## Installation
|
||||
|
||||
This extension is not available on the Chrome Web Store yet. You can install it manually using below method:
|
||||
|
||||
### Manual Installation (Developer Mode)
|
||||
|
||||
1. **Download the Extension**: Clone or download the extension files from the [Mem0 GitHub repository](https://github.com/mem0ai/mem0/tree/main/examples).
|
||||
2. **Build**: Run `npm install` followed by `npm run build` to install the dependencies and build the extension.
|
||||
3. **Access Chrome Extensions**: Open Google Chrome and navigate to `chrome://extensions`.
|
||||
4. **Enable Developer Mode**: Toggle the "Developer mode" switch in the top right corner.
|
||||
5. **Load Unpacked Extension**: Click "Load unpacked" and select the directory containing the extension files.
|
||||
6. **Confirm Installation**: The Mem0 YouTube Assistant Extension should now appear in your Chrome toolbar.
|
||||
|
||||
## Setup
|
||||
|
||||
1. **Configure API Settings**: Click the extension icon and enter your OpenAI API key (required to use the extension)
|
||||
2. **Customize Settings**: Configure additional settings such as model, temperature, and memory settings
|
||||
3. **Navigate to YouTube**: Start using the assistant on any YouTube video
|
||||
4. **Memories**: Enter your Mem0 API key to enable personalized responses, and feed initial memories from settings
|
||||
|
||||
## Example Prompts
|
||||
|
||||
- "Can you summarize the main points of this video?"
|
||||
- "Explain the concept they just mentioned"
|
||||
- "How does this relate to what I already know?"
|
||||
- "What are some practical applications of this topic related to my work?"
|
||||
|
||||
|
||||
## Privacy and Data Security
|
||||
|
||||
Your API keys are stored locally in your browser. Your messages are sent to the Mem0 API for extracting and retrieving memories. Mem0 is committed to ensuring your data's privacy and security.
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ messages = [
|
||||
|
||||
<CodeGroup>
|
||||
```python Code
|
||||
client.add(messages, user_id="alice", includes=includes)
|
||||
client.add(messages, user_id="alice", excludes=excludes)
|
||||
```
|
||||
|
||||
```json Stored Memories
|
||||
|
||||
@@ -220,4 +220,104 @@ 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>
|
||||
<Card
|
||||
title="Agno"
|
||||
icon={
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path d="M8 4h8v12h8" stroke="currentColor" strokeWidth="2" fill="none" transform="rotate(15, 12, 12)"/>
|
||||
</svg>
|
||||
}
|
||||
href="/integrations/agno"
|
||||
>
|
||||
Build autonomous agents with memory using Agno framework.
|
||||
</Card>
|
||||
<Card
|
||||
title="Keywords AI"
|
||||
icon={
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.07513 1.1863C9.21663 1.07722 9.39144 1.01009 9.56624 1.01009C9.83261 1.01009 10.0823 1.12756 10.2405 1.33734L15.0101 7.4964V12.4136L16.4335 13.8401C16.7582 14.1673 16.7582 14.7043 16.4335 15.0316C16.1089 15.3588 15.5762 15.3588 15.2515 15.0316L13.3453 13.1016V8.07538L8.92529 2.36944V2.36105C8.64228 2.00024 8.70887 1.4716 9.07513 1.1863ZM18.976 14.4133C18.8344 14.3778 18.7003 14.3042 18.5894 14.1925L16.9163 12.5059C16.7249 12.3129 16.6416 12.0528 16.6749 11.8094V6.88385H16.6499L11.8553 0.691225C11.7282 0.529117 11.6716 0.333133 11.6803 0.140562C11.134 0.0481292 10.5726 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20C13.9387 20 17.3456 17.7229 18.976 14.4133Z" fill="currentColor"></path>
|
||||
</svg>
|
||||
}
|
||||
href="/integrations/keywords"
|
||||
>
|
||||
Build AI applications with persistent memory and comprehensive LLM observability.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,173 @@
|
||||
---
|
||||
title: Agno
|
||||
---
|
||||
|
||||
Integrate [**Mem0**](https://github.com/mem0ai/mem0) with [Agno](https://github.com/agno-ai/agno), a Python framework for building autonomous agents. This integration enables Agno agents to access persistent memory across conversations, enhancing context retention and personalization.
|
||||
|
||||
## Overview
|
||||
|
||||
1. 🧠 Store and retrieve memories from Mem0 within Agno agents
|
||||
2. 🖼️ Support for multimodal interactions (text and images)
|
||||
3. 🔍 Semantic search for relevant past conversations
|
||||
4. 🌐 Personalized responses based on user history
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before setting up Mem0 with Agno, ensure you have:
|
||||
|
||||
1. Installed the required packages:
|
||||
```bash
|
||||
pip install agno-ai mem0ai
|
||||
```
|
||||
|
||||
2. Valid API keys:
|
||||
- [Mem0 API Key](https://app.mem0.ai/dashboard/api-keys)
|
||||
- OpenAI API Key (for the agent model)
|
||||
|
||||
## Integration Example
|
||||
|
||||
The following example demonstrates how to create an Agno agent with Mem0 memory integration, including support for image processing:
|
||||
|
||||
```python
|
||||
import base64
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from agno.agent import Agent
|
||||
from agno.media import Image
|
||||
from agno.models.openai import OpenAIChat
|
||||
from mem0 import MemoryClient
|
||||
|
||||
|
||||
# Initialize the Mem0 client
|
||||
client = MemoryClient()
|
||||
|
||||
# Define the agent
|
||||
agent = Agent(
|
||||
name="Personal Agent",
|
||||
model=OpenAIChat(id="gpt-4"),
|
||||
description="You are a helpful personal agent that helps me with day to day activities."
|
||||
"You can process both text and images.",
|
||||
markdown=True
|
||||
)
|
||||
|
||||
|
||||
def chat_user(
|
||||
user_input: Optional[str] = None,
|
||||
user_id: str = "user_123",
|
||||
image_path: Optional[str] = None
|
||||
) -> str:
|
||||
"""
|
||||
Handle user input with memory integration, supporting both text and images.
|
||||
|
||||
Args:
|
||||
user_input: The user's text input
|
||||
user_id: Unique identifier for the user
|
||||
image_path: Path to an image file if provided
|
||||
|
||||
Returns:
|
||||
The agent's response as a string
|
||||
"""
|
||||
if image_path:
|
||||
# Convert image to base64
|
||||
with open(image_path, "rb") as image_file:
|
||||
base64_image = base64.b64encode(image_file.read()).decode("utf-8")
|
||||
|
||||
# Create message objects for text and image
|
||||
messages = []
|
||||
|
||||
if user_input:
|
||||
messages.append({
|
||||
"role": "user",
|
||||
"content": user_input
|
||||
})
|
||||
|
||||
messages.append({
|
||||
"role": "user",
|
||||
"content": {
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": f"data:image/jpeg;base64,{base64_image}"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# Store messages in memory
|
||||
client.add(messages, user_id=user_id)
|
||||
print("✅ Image and text stored in memory.")
|
||||
|
||||
if user_input:
|
||||
# Search for relevant memories
|
||||
memories = client.search(user_input, user_id=user_id)
|
||||
memory_context = "\n".join(f"- {m['memory']}" for m in memories)
|
||||
|
||||
# Construct the prompt
|
||||
prompt = f"""
|
||||
You are a helpful personal assistant who helps users with their day-to-day activities and keeps track of everything.
|
||||
|
||||
Your task is to:
|
||||
1. Analyze the given image (if present) and extract meaningful details to answer the user's question.
|
||||
2. Use your past memory of the user to personalize your answer.
|
||||
3. Combine the image content and memory to generate a helpful, context-aware response.
|
||||
|
||||
Here is what I remember about the user:
|
||||
{memory_context}
|
||||
|
||||
User question:
|
||||
{user_input}
|
||||
"""
|
||||
# Get response from agent
|
||||
if image_path:
|
||||
response = agent.run(prompt, images=[Image(filepath=Path(image_path))])
|
||||
else:
|
||||
response = agent.run(prompt)
|
||||
|
||||
# Store the interaction in memory
|
||||
client.add(f"User: {user_input}\nAssistant: {response.content}", user_id=user_id)
|
||||
return response.content
|
||||
|
||||
return "No user input or image provided."
|
||||
|
||||
|
||||
# Example Usage
|
||||
if __name__ == "__main__":
|
||||
response = chat_user(
|
||||
"This is the picture of what I brought with me in the trip to Bahamas",
|
||||
image_path="travel_items.jpeg",
|
||||
user_id="user_123"
|
||||
)
|
||||
print(response)
|
||||
```
|
||||
|
||||
## Key Features
|
||||
|
||||
### 1. Multimodal Memory Storage
|
||||
|
||||
The integration supports storing both text and image data:
|
||||
|
||||
- **Text Storage**: Conversation history is saved in a structured format
|
||||
- **Image Analysis**: Agents can analyze images and store visual information
|
||||
- **Combined Context**: Memory retrieval combines both text and visual data
|
||||
|
||||
### 2. Personalized Agent Responses
|
||||
|
||||
Improve your agent's context awareness:
|
||||
|
||||
- **Memory Retrieval**: Semantic search finds relevant past interactions
|
||||
- **User Preferences**: Personalize responses based on stored user information
|
||||
- **Continuity**: Maintain conversation threads across multiple sessions
|
||||
|
||||
### 3. Flexible Configuration
|
||||
|
||||
Customize the integration to your needs:
|
||||
|
||||
- **User Identification**: Organize memories by user ID
|
||||
- **Memory Search**: Configure search relevance and result count
|
||||
- **Memory Formatting**: Support for various OpenAI message formats
|
||||
|
||||
## Help & Resources
|
||||
|
||||
- [Agno Documentation](https://docs.agno.com/introduction)
|
||||
- [Mem0 Platform](https://app.mem0.ai/)
|
||||
|
||||
<Snippet file="get-help.mdx" />
|
||||
@@ -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,140 @@
|
||||
---
|
||||
title: Keywords AI
|
||||
---
|
||||
|
||||
Build AI applications with persistent memory and comprehensive LLM observability by integrating Mem0 with Keywords AI.
|
||||
|
||||
## Overview
|
||||
|
||||
Mem0 is a self-improving memory layer for LLM applications, enabling personalized AI experiences that save costs and delight users. Keywords AI provides complete LLM observability.
|
||||
|
||||
Combining Mem0 with Keywords AI allows you to:
|
||||
1. Add persistent memory to your AI applications
|
||||
2. Track interactions across sessions
|
||||
3. Monitor memory usage and retrieval with Keywords AI observability
|
||||
4. Optimize token usage and reduce costs
|
||||
|
||||
<Note>
|
||||
You can get your Mem0 API key, user_id, and org_id from the [Mem0 dashboard](https://app.mem0.ai/). These are required for proper integration.
|
||||
</Note>
|
||||
|
||||
## Setup and Configuration
|
||||
|
||||
Install the necessary libraries:
|
||||
|
||||
```bash
|
||||
pip install mem0 keywordsai-sdk
|
||||
```
|
||||
|
||||
Set up your environment variables:
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
# Set your API keys
|
||||
os.environ["MEM0_API_KEY"] = "your-mem0-api-key"
|
||||
os.environ["KEYWORDSAI_API_KEY"] = "your-keywords-api-key"
|
||||
os.environ["KEYWORDSAI_BASE_URL"] = "https://api.keywordsai.co/api/"
|
||||
```
|
||||
|
||||
## Basic Integration Example
|
||||
|
||||
Here's a simple example of using Mem0 with Keywords AI:
|
||||
|
||||
```python
|
||||
from mem0 import Memory
|
||||
import os
|
||||
|
||||
# Configuration
|
||||
api_key = os.getenv("MEM0_API_KEY")
|
||||
keywordsai_api_key = os.getenv("KEYWORDSAI_API_KEY")
|
||||
base_url = os.getenv("KEYWORDSAI_BASE_URL") # "https://api.keywordsai.co/api/"
|
||||
|
||||
# Set up Mem0 with Keywords AI as the LLM provider
|
||||
config = {
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4o-mini",
|
||||
"temperature": 0.0,
|
||||
"api_key": keywordsai_api_key,
|
||||
"openai_base_url": base_url,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
# Initialize Memory
|
||||
memory = Memory.from_config(config_dict=config)
|
||||
|
||||
# Add a memory
|
||||
result = memory.add(
|
||||
"I like to take long walks on weekends.",
|
||||
user_id="alice",
|
||||
metadata={"category": "hobbies"},
|
||||
)
|
||||
|
||||
print(result)
|
||||
```
|
||||
|
||||
## Advanced Integration with OpenAI SDK
|
||||
|
||||
For more advanced use cases, you can integrate Keywords AI with Mem0 through the OpenAI SDK:
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
import os
|
||||
import json
|
||||
|
||||
# Initialize client
|
||||
client = OpenAI(
|
||||
api_key=os.environ.get("KEYWORDSAI_API_KEY"),
|
||||
base_url=os.environ.get("KEYWORDSAI_BASE_URL"),
|
||||
)
|
||||
|
||||
# Sample conversation messages
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
|
||||
{"role": "assistant", "content": "How about 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."}
|
||||
]
|
||||
|
||||
# Add memory and generate a response
|
||||
response = client.chat.completions.create(
|
||||
model="openai/gpt-4o",
|
||||
messages=messages,
|
||||
extra_body={
|
||||
"mem0_params": {
|
||||
"user_id": "test_user",
|
||||
"org_id": "org_1",
|
||||
"api_key": os.environ.get("MEM0_API_KEY"),
|
||||
"add_memories": {
|
||||
"messages": messages,
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
print(json.dumps(response.model_dump(), indent=4))
|
||||
```
|
||||
|
||||
For detailed information on this integration, refer to the official [Keywords AI Mem0 integration documentation](https://docs.keywordsai.co/integration/development-frameworks/mem0).
|
||||
|
||||
## Key Features
|
||||
|
||||
1. **Memory Integration**: Store and retrieve relevant information from past interactions
|
||||
2. **LLM Observability**: Track memory usage and retrieval patterns with Keywords AI
|
||||
3. **Session Persistence**: Maintain context across multiple user sessions
|
||||
4. **Cost Optimization**: Reduce token usage through efficient memory retrieval
|
||||
|
||||
## Conclusion
|
||||
|
||||
Integrating Mem0 with Keywords AI provides a powerful combination for building AI applications with persistent memory and comprehensive observability. This integration enables more personalized user experiences while providing insights into your application's memory usage.
|
||||
|
||||
## Help
|
||||
|
||||
For more information on using Mem0 and Keywords AI together, refer to:
|
||||
- [Mem0 Documentation](https://docs.mem0.ai)
|
||||
- [Keywords AI Documentation](https://docs.keywordsai.co)
|
||||
|
||||
<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")
|
||||
```
|
||||
@@ -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.
|
||||
@@ -0,0 +1,169 @@
|
||||
---
|
||||
title: Async Memory
|
||||
description: 'Asynchronous memory for Mem0'
|
||||
icon: "bolt"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
## AsyncMemory
|
||||
|
||||
The `AsyncMemory` class is a direct asynchronous interface to Mem0's in-process memory operations. Unlike the memory, which interacts with an API, `AsyncMemory` works directly with the underlying storage systems. This makes it ideal for applications where you want to embed Mem0 directly into your codebase.
|
||||
|
||||
### Initialization
|
||||
|
||||
To use `AsyncMemory`, import it from the `mem0.memory` module:
|
||||
|
||||
```python Python
|
||||
import asyncio
|
||||
from mem0 import AsyncMemory
|
||||
|
||||
# Initialize with default configuration
|
||||
memory = AsyncMemory()
|
||||
|
||||
# Or initialize with custom configuration
|
||||
from mem0.configs.base import MemoryConfig
|
||||
custom_config = MemoryConfig(
|
||||
# Your custom configuration here
|
||||
)
|
||||
memory = AsyncMemory(config=custom_config)
|
||||
```
|
||||
|
||||
### Key Features
|
||||
|
||||
1. **Non-blocking Operations** - All memory operations use `asyncio` to avoid blocking the event loop
|
||||
2. **Concurrent Processing** - Parallel execution of vector store and graph operations
|
||||
3. **Efficient Resource Utilization** - Better handling of I/O bound operations
|
||||
4. **Compatible with Async Frameworks** - Seamless integration with FastAPI, aiohttp, and other async frameworks
|
||||
|
||||
### Methods
|
||||
|
||||
All methods in `AsyncMemory` have the same parameters as the synchronous `Memory` class but are designed to be used with `async/await`.
|
||||
|
||||
#### Create memories
|
||||
|
||||
Add a new memory asynchronously:
|
||||
|
||||
```python Python
|
||||
await memory.add(
|
||||
messages=[
|
||||
{"role": "user", "content": "I'm travelling to SF"},
|
||||
{"role": "assistant", "content": "That's great to hear!"}
|
||||
],
|
||||
user_id="alice"
|
||||
)
|
||||
```
|
||||
|
||||
#### Retrieve memories
|
||||
|
||||
Retrieve memories related to a query:
|
||||
|
||||
```python Python
|
||||
await memory.search(
|
||||
query="Where am I travelling?",
|
||||
user_id="alice"
|
||||
)
|
||||
```
|
||||
|
||||
#### List memories
|
||||
|
||||
List all memories for a `user_id`, `agent_id`, or `run_id`:
|
||||
|
||||
```python Python
|
||||
await memory.get_all(user_id="alice")
|
||||
```
|
||||
|
||||
#### Get specific memory
|
||||
|
||||
Retrieve a specific memory by its ID:
|
||||
|
||||
```python Python
|
||||
await memory.get(memory_id="memory-id-here")
|
||||
```
|
||||
|
||||
#### Update memory
|
||||
|
||||
Update an existing memory by ID:
|
||||
|
||||
```python Python
|
||||
await memory.update(
|
||||
memory_id="memory-id-here",
|
||||
data="I'm travelling to Seattle"
|
||||
)
|
||||
```
|
||||
|
||||
#### Delete memory
|
||||
|
||||
Delete a specific memory by ID:
|
||||
|
||||
```python Python
|
||||
await memory.delete(memory_id="memory-id-here")
|
||||
```
|
||||
|
||||
#### Delete all memories
|
||||
|
||||
Delete all memories for a specific user, agent, or run:
|
||||
|
||||
```python Python
|
||||
await memory.delete_all(user_id="alice")
|
||||
```
|
||||
|
||||
Note: At least one filter (user_id, agent_id, or run_id) is required when using delete_all.
|
||||
|
||||
#### Memory History
|
||||
|
||||
Get the history of changes for a specific memory:
|
||||
|
||||
```python Python
|
||||
await memory.history(memory_id="memory-id-here")
|
||||
```
|
||||
|
||||
### Example: Concurrent Usage with Other APIs
|
||||
|
||||
`AsyncMemory` can be effectively combined with other async operations. Here's an example showing how to use it alongside OpenAI API calls in separate threads:
|
||||
|
||||
```python Python
|
||||
import asyncio
|
||||
from openai import AsyncOpenAI
|
||||
from mem0 import AsyncMemory
|
||||
|
||||
async_openai_client = AsyncOpenAI()
|
||||
async_memory = AsyncMemory()
|
||||
|
||||
async def chat_with_memories(message: str, user_id: str = "default_user") -> str:
|
||||
# Retrieve relevant memories
|
||||
search_result = await async_memory.search(query=message, user_id=user_id, limit=3)
|
||||
relevant_memories = search_result["results"]
|
||||
memories_str = "\n".join(f"- {entry['memory']}" for entry in relevant_memories)
|
||||
|
||||
# Generate Assistant response
|
||||
system_prompt = f"You are a helpful AI. Answer the question based on query and memories.\nUser Memories:\n{memories_str}"
|
||||
messages = [{"role": "system", "content": system_prompt}, {"role": "user", "content": message}]
|
||||
response = await async_openai_client.chat.completions.create(model="gpt-4o-mini", messages=messages)
|
||||
assistant_response = response.choices[0].message.content
|
||||
|
||||
# Create new memories from the conversation
|
||||
messages.append({"role": "assistant", "content": assistant_response})
|
||||
await async_memory.add(messages, user_id=user_id)
|
||||
|
||||
return assistant_response
|
||||
|
||||
async def async_main():
|
||||
print("Chat with AI (type 'exit' to quit)")
|
||||
while True:
|
||||
user_input = input("You: ").strip()
|
||||
if user_input.lower() == 'exit':
|
||||
print("Goodbye!")
|
||||
break
|
||||
response = await chat_with_memories(user_input)
|
||||
print(f"AI: {response}")
|
||||
|
||||
def main():
|
||||
asyncio.run(async_main())
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
If you have any questions or need further assistance, please don't hesitate to reach out:
|
||||
|
||||
<Snippet file="get-help.mdx" />
|
||||
@@ -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.",
|
||||
}
|
||||
```
|
||||
|
||||
@@ -28,6 +28,16 @@ from mem0 import Memory
|
||||
os.environ["OPENAI_API_KEY"] = "your-api-key"
|
||||
|
||||
m = Memory()
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Async">
|
||||
```python
|
||||
import os
|
||||
from mem0 import AsyncMemory
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "your-api-key"
|
||||
|
||||
m = AsyncMemory()
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Advanced">
|
||||
|
||||
+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",
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ iconType: "solid"
|
||||
---
|
||||
|
||||
<Note type="info">
|
||||
🎉 We now support [Claude 3.7 Sonnet](components/llms/models/anthropic)! Enhance your AI assistants with the latest and most capable language model from Anthropic.
|
||||
🎉 We now support [Grok 3](components/llms/models/xAI)! Enhance your AI assistants with the latest and most capable language model from xAI.
|
||||
</Note>
|
||||
|
||||
# Introduction
|
||||
|
||||
+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,88 @@
|
||||
"""
|
||||
Memory-Powered Movie Recommendation Assistant (Grok 3 + Mem0)
|
||||
This script builds a personalized movie recommender that remembers your preferences
|
||||
(e.g. dislikes horror, loves romcoms) using Mem0 as a memory layer and Grok 3 for responses.
|
||||
|
||||
In order to run this file, you need to set up your Mem0 API at Mem0 platform and also need an XAI API key.
|
||||
export XAI_API_KEY="your_xai_api_key"
|
||||
export MEM0_API_KEY="your_mem0_api_key"
|
||||
"""
|
||||
|
||||
from mem0 import Memory
|
||||
from openai import OpenAI
|
||||
|
||||
|
||||
# Configure Mem0 with Grok 3 and Qdrant
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "qdrant",
|
||||
"config": {
|
||||
"embedding_model_dims": 384
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
"provider": "xai",
|
||||
"config": {
|
||||
"model": "grok-3-beta",
|
||||
"temperature": 0.1,
|
||||
"max_tokens": 2000,
|
||||
}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "huggingface",
|
||||
"config": {
|
||||
"model": "all-MiniLM-L6-v2" # open embedding model
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Instantiate memory layer
|
||||
memory = Memory.from_config(config)
|
||||
|
||||
# Initialize Grok 3 client
|
||||
grok_client = OpenAI(
|
||||
api_key=XAI_API_KEY,
|
||||
base_url="https://api.x.ai/v1",
|
||||
)
|
||||
|
||||
|
||||
def recommend_movie_with_memory(user_id: str, user_query: str):
|
||||
# Retrieve prior memory about movies
|
||||
past_memories = memory.search("movie preferences", user_id=user_id)
|
||||
|
||||
prompt = user_query
|
||||
if past_memories:
|
||||
prompt += f"\nPreviously, the user mentioned: {past_memories}"
|
||||
|
||||
# Generate movie recommendation using Grok 3
|
||||
response = grok_client.chat.completions.create(
|
||||
model="grok-3-beta",
|
||||
messages=[
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
)
|
||||
recommendation = response.choices[0].message.content
|
||||
|
||||
# Store conversation in memory
|
||||
memory.add(
|
||||
[{"role": "user", "content": user_query},
|
||||
{"role": "assistant", "content": recommendation}],
|
||||
user_id=user_id,
|
||||
metadata={"category": "movie"}
|
||||
)
|
||||
|
||||
return recommendation
|
||||
|
||||
|
||||
# Example Usage
|
||||
if __name__ == "__main__":
|
||||
user_id = "arshi"
|
||||
recommend_movie_with_memory(user_id, "I'm looking for a movie to watch tonight. Any suggestions?")
|
||||
# OUTPUT: You have watched Intersteller last weekend and you don't like horror movies, maybe you can watch "Purple Hearts" today.
|
||||
recommend_movie_with_memory(user_id, "Can we skip the tearjerkers? I really enjoyed Notting Hill and Crazy Rich Asians.")
|
||||
# OUTPUT: Got it — no sad endings! You might enjoy "The Proposal" or "Love, Rosie". They’re both light-hearted romcoms with happy vibes.
|
||||
recommend_movie_with_memory(user_id, "Any light-hearted movie I can watch after work today?")
|
||||
# OUTPUT: Since you liked Crazy Rich Asians and The Proposal, how about "The Intern" or "Isn’t It Romantic"? Both are upbeat, funny, and perfect for relaxing.
|
||||
recommend_movie_with_memory(user_id, "I’ve already watched The Intern. Something new maybe?")
|
||||
# OUTPUT: No problem! Try "Your Place or Mine" - romcoms that match your taste and are tear-free!
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
Create your personal AI Assistant powered by memory that supports both text and images and remembers your preferences
|
||||
|
||||
In order to run this file, you need to set up your Mem0 API at Mem0 platform and also need a OpenAI API key.
|
||||
export OPENAI_API_KEY="your_openai_api_key"
|
||||
export MEM0_API_KEY="your_mem0_api_key"
|
||||
"""
|
||||
|
||||
import base64
|
||||
from pathlib import Path
|
||||
|
||||
from agno.agent import Agent
|
||||
from agno.media import Image
|
||||
from agno.models.openai import OpenAIChat
|
||||
from mem0 import MemoryClient
|
||||
|
||||
|
||||
# Initialize the Mem0 client
|
||||
client = MemoryClient()
|
||||
|
||||
# Define the agent
|
||||
agent = Agent(
|
||||
name="Personal Agent",
|
||||
model=OpenAIChat(id="gpt-4o"),
|
||||
description="You are a helpful personal agent that helps me with day to day activities."
|
||||
"You can process both text and images.",
|
||||
markdown=True
|
||||
)
|
||||
|
||||
|
||||
# Function to handle user input with memory integration with support for images
|
||||
def chat_user(user_input: str = None, user_id: str = "user_123", image_path: str = None):
|
||||
if image_path:
|
||||
with open(image_path, "rb") as image_file:
|
||||
base64_image = base64.b64encode(image_file.read()).decode("utf-8")
|
||||
|
||||
# First: the text message
|
||||
text_msg = {
|
||||
"role": "user",
|
||||
"content": user_input
|
||||
}
|
||||
|
||||
# Second: the image message
|
||||
image_msg = {
|
||||
"role": "user",
|
||||
"content": {
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": f"data:image/jpeg;base64,{base64_image}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Send both as separate message objects
|
||||
client.add([text_msg, image_msg], user_id=user_id, output_format='v1.1')
|
||||
print("✅ Image uploaded and stored in memory.")
|
||||
|
||||
if user_input:
|
||||
memories = client.search(user_input, user_id=user_id)
|
||||
memory_context = "\n".join(f"- {m['memory']}" for m in memories)
|
||||
|
||||
prompt = f"""
|
||||
You are a helpful personal assistant who helps user with his day-to-day activities and keep track of everything.
|
||||
|
||||
Your task is to:
|
||||
1. Analyze the given image (if present) and extract meaningful details to answer the user's question.
|
||||
2. Use your past memory of the user to personalize your answer.
|
||||
3. Combine the image content and memory to generate a helpful, context-aware response.
|
||||
|
||||
Here is what remember about the user:
|
||||
{memory_context}
|
||||
|
||||
User question:
|
||||
{user_input}
|
||||
"""
|
||||
if image_path:
|
||||
response = agent.run(prompt, images=[Image(filepath=Path(image_path))])
|
||||
else:
|
||||
response = agent.run(prompt)
|
||||
client.add(f"User: {user_input}\nAssistant: {response.content}", user_id=user_id)
|
||||
return response.content
|
||||
|
||||
return "No user input or image provided."
|
||||
|
||||
|
||||
# Example Usage
|
||||
user_id = "user_123"
|
||||
print(chat_user("What did I ask you to remind me about?", user_id))
|
||||
# # OUTPUT: You asked me to remind you to call your mom tomorrow. 📞
|
||||
#
|
||||
print(chat_user("When is my test?", user_id=user_id))
|
||||
# OUTPUT: Your pilot's test is on your birthday, which is in five days. You're turning 25!
|
||||
# Good luck with your preparations, and remember to take some time to relax amidst the studying.
|
||||
|
||||
print(chat_user("This is the picture of what I brought with me in the trip to Bahamas",
|
||||
image_path="travel_items.jpeg", # this will be added to Mem0 memory
|
||||
user_id=user_id))
|
||||
print(chat_user("hey can you quickly tell me if brought my sunglasses to my trip, not able to find",
|
||||
user_id=user_id))
|
||||
# OUTPUT: Yes, you did bring your sunglasses on your trip to the Bahamas along with your laptop, face masks and other items..
|
||||
# Since you can't find them now, perhaps check the pockets of jackets you wore or in your luggage compartments.
|
||||
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
Create your personal AI Study Buddy that remembers what you’ve studied (and where you struggled),
|
||||
helps with spaced repetition and topic review, personalizes responses using your past interactions.
|
||||
Supports both text and PDF/image inputs.
|
||||
|
||||
In order to run this file, you need to set up your Mem0 API at Mem0 platform and also need a OpenAI API key.
|
||||
export OPENAI_API_KEY="your_openai_api_key"
|
||||
export MEM0_API_KEY="your_mem0_api_key"
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
from mem0 import MemoryClient
|
||||
from agents import Agent, Runner
|
||||
|
||||
|
||||
client = MemoryClient()
|
||||
|
||||
# Define your study buddy agent
|
||||
study_agent = Agent(
|
||||
name="StudyBuddy",
|
||||
instructions="""You are a helpful study coach. You:
|
||||
- Track what the user has studied before
|
||||
- Identify topics the user has struggled with (e.g., "I'm confused", "this is hard")
|
||||
- Help with spaced repetition by suggesting topics to revisit based on last review time
|
||||
- Personalize answers using stored memories
|
||||
- Summarize PDFs or notes the user uploads""")
|
||||
|
||||
|
||||
# Upload and store PDF to Mem0
|
||||
def upload_pdf(pdf_url: str, user_id: str):
|
||||
pdf_message = {
|
||||
"role": "user",
|
||||
"content": {
|
||||
"type": "pdf_url",
|
||||
"pdf_url": {"url": pdf_url}
|
||||
}
|
||||
}
|
||||
client.add([pdf_message], user_id=user_id)
|
||||
print("✅ PDF uploaded and processed into memory.")
|
||||
|
||||
|
||||
# Main interaction loop with your personal study buddy
|
||||
async def study_buddy(user_id: str, topic: str, user_input: str):
|
||||
|
||||
memories = client.search(f"{topic}", user_id=user_id)
|
||||
memory_context = "n".join(f"- {m['memory']}" for m in memories)
|
||||
|
||||
prompt = f"""
|
||||
You are helping the user study the topic: {topic}.
|
||||
Here are past memories from previous sessions:
|
||||
{memory_context}
|
||||
|
||||
Now respond to the user's new question or comment:
|
||||
{user_input}
|
||||
"""
|
||||
result = await Runner.run(study_agent, prompt)
|
||||
response = result.final_output
|
||||
|
||||
client.add([
|
||||
{"role": "user", "content": f'''Topic: {topic}nUser: {user_input}nnStudy Assistant: {response}'''}
|
||||
], user_id=user_id, metadata={"topic": topic})
|
||||
|
||||
return response
|
||||
|
||||
|
||||
# Example usage
|
||||
async def main():
|
||||
user_id = "Ajay"
|
||||
pdf_url = "https://pages.physics.ua.edu/staff/fabi/ph101/classnotes/8RotD101.pdf"
|
||||
upload_pdf(pdf_url, user_id) # Upload a relevant lecture PDF to memory
|
||||
|
||||
topic = "Lagrangian Mechanics"
|
||||
# Demonstrate tracking previously learned topics
|
||||
print(await study_buddy(user_id, topic, "Can you remind me of what we discussed about generalized coordinates?"))
|
||||
|
||||
# Demonstrate weakness detection
|
||||
print(await study_buddy(user_id, topic, "I still don’t get what frequency domain really means."))
|
||||
|
||||
# Demonstrate spaced repetition prompting
|
||||
topic = "Momentum Conservation"
|
||||
print(await study_buddy(user_id, topic, "I think we covered this last week. Is it time to review momentum conservation again?"))
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,240 @@
|
||||
"""
|
||||
Personal Voice Assistant with Memory (Whisper + CrewAI + Mem0 + ElevenLabs)
|
||||
This script creates a personalized AI assistant that can:
|
||||
- Understand voice commands using Whisper (OpenAI STT)
|
||||
- Respond intelligently using CrewAI Agent and LLMs
|
||||
- Remember user preferences and facts using Mem0 memory
|
||||
- Speak responses back using ElevenLabs text-to-speech
|
||||
Initial user memory is bootstrapped from predefined preferences, and the assistant can remember new context dynamically over time.
|
||||
|
||||
To run this file, you need to set the following environment variables:
|
||||
|
||||
export OPENAI_API_KEY="your_openai_api_key"
|
||||
export MEM0_API_KEY="your_mem0_api_key"
|
||||
export ELEVENLABS_API_KEY="your_elevenlabs_api_key"
|
||||
|
||||
You must also have:
|
||||
- A working microphone setup (pyaudio)
|
||||
- A valid ElevenLabs voice ID
|
||||
- Python packages: openai, elevenlabs, crewai, mem0ai, pyaudio
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
import wave
|
||||
import pyaudio
|
||||
from elevenlabs.client import ElevenLabs
|
||||
from elevenlabs import play
|
||||
from crewai import Agent, Task, Crew, Process
|
||||
from openai import OpenAI
|
||||
from mem0 import MemoryClient
|
||||
|
||||
# ------------------ SETUP ------------------
|
||||
USER_ID = "Alex"
|
||||
openai_client = OpenAI()
|
||||
tts_client = ElevenLabs()
|
||||
memory_client = MemoryClient()
|
||||
|
||||
|
||||
# Function to store user preferences in memory
|
||||
def store_user_preferences(user_id: str, conversation: list):
|
||||
"""Store user preferences from conversation history"""
|
||||
memory_client.add(conversation, user_id=user_id)
|
||||
|
||||
|
||||
# Initialize memory with some basic preferences
|
||||
def initialize_memory():
|
||||
# Example conversation storage with voice assistant relevant preferences
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hi, my name is Alex Thompson. I'm 32 years old and work as a software engineer at TechCorp.",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Hello Alex Thompson! Nice to meet you. I've noted that you're 32 and work as a software engineer at TechCorp. How can I help you today?",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I prefer brief and concise responses without unnecessary explanations. I get frustrated when assistants are too wordy or repeat information I already know."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Got it. I'll keep my responses short, direct, and without redundancy.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I like to listen to jazz music when I'm working, especially artists like Miles Davis and John Coltrane. I find it helps me focus and be more productive."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "I'll remember your preference for jazz while working, particularly Miles Davis and John Coltrane. It's great for focus.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I usually wake up at 7 AM and prefer reminders for meetings 30 minutes in advance. My most productive hours are between 9 AM and noon, so I try to schedule important tasks during that time."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Noted. You wake up at 7 AM, need meeting reminders 30 minutes ahead, and are most productive between 9 AM and noon for important tasks.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "My favorite color is navy blue, and I prefer dark mode in all my apps. I'm allergic to peanuts, so please remind me to check ingredients when I ask about recipes or restaurants."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "I've noted that you prefer navy blue and dark mode interfaces. I'll also help you remember to check for peanuts in food recommendations due to your allergy.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "My partner's name is Jamie, and we have a golden retriever named Max who is 3 years old. My parents live in Chicago, and I try to visit them once every two months."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "I'll remember that your partner is Jamie, your dog Max is a 3-year-old golden retriever, and your parents live in Chicago whom you visit bimonthly.",
|
||||
},
|
||||
]
|
||||
|
||||
# Store the initial preferences
|
||||
store_user_preferences(USER_ID, messages)
|
||||
print("✅ Memory initialized with user preferences")
|
||||
|
||||
|
||||
voice_agent = Agent(
|
||||
role="Memory-based Voice Assistant",
|
||||
goal="Help the user with day-to-day tasks and remember their preferences over time.",
|
||||
backstory="You are a voice assistant who understands the user well and converse with them.",
|
||||
verbose=True,
|
||||
memory=True,
|
||||
memory_config={
|
||||
"provider": "mem0",
|
||||
"config": {"user_id": USER_ID},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# ------------------ AUDIO RECORDING ------------------
|
||||
def record_audio(filename="input.wav", record_seconds=5):
|
||||
print("🎙️ Recording (speak now)...")
|
||||
chunk = 1024
|
||||
fmt = pyaudio.paInt16
|
||||
channels = 1
|
||||
rate = 44100
|
||||
|
||||
p = pyaudio.PyAudio()
|
||||
stream = p.open(format=fmt, channels=channels, rate=rate, input=True, frames_per_buffer=chunk)
|
||||
frames = []
|
||||
|
||||
for _ in range(0, int(rate / chunk * record_seconds)):
|
||||
data = stream.read(chunk)
|
||||
frames.append(data)
|
||||
|
||||
stream.stop_stream()
|
||||
stream.close()
|
||||
p.terminate()
|
||||
|
||||
with wave.open(filename, 'wb') as wf:
|
||||
wf.setnchannels(channels)
|
||||
wf.setsampwidth(p.get_sample_size(fmt))
|
||||
wf.setframerate(rate)
|
||||
wf.writeframes(b''.join(frames))
|
||||
|
||||
|
||||
# ------------------ STT USING WHISPER ------------------
|
||||
def transcribe_whisper(audio_path):
|
||||
print("🔎 Transcribing with Whisper...")
|
||||
try:
|
||||
with open(audio_path, "rb") as audio_file:
|
||||
transcript = openai_client.audio.transcriptions.create(
|
||||
model="whisper-1",
|
||||
file=audio_file
|
||||
)
|
||||
print(f"🗣️ You said: {transcript.text}")
|
||||
return transcript.text
|
||||
except Exception as e:
|
||||
print(f"Error during transcription: {e}")
|
||||
return ""
|
||||
|
||||
|
||||
# ------------------ AGENT RESPONSE ------------------
|
||||
def get_agent_response(user_input):
|
||||
if not user_input:
|
||||
return "I didn't catch that. Could you please repeat?"
|
||||
|
||||
try:
|
||||
task = Task(
|
||||
description=f"Respond to: {user_input}",
|
||||
expected_output="A short and relevant reply.",
|
||||
agent=voice_agent
|
||||
)
|
||||
crew = Crew(
|
||||
agents=[voice_agent],
|
||||
tasks=[task],
|
||||
process=Process.sequential,
|
||||
verbose=True,
|
||||
memory=True,
|
||||
memory_config={
|
||||
"provider": "mem0",
|
||||
"config": {"user_id": USER_ID}
|
||||
}
|
||||
)
|
||||
result = crew.kickoff()
|
||||
|
||||
# Extract the text response from the complex result object
|
||||
if hasattr(result, 'raw'):
|
||||
return result.raw
|
||||
elif isinstance(result, dict) and 'raw' in result:
|
||||
return result['raw']
|
||||
elif isinstance(result, dict) and 'tasks_output' in result:
|
||||
outputs = result['tasks_output']
|
||||
if outputs and isinstance(outputs, list) and len(outputs) > 0:
|
||||
return outputs[0].get('raw', str(result))
|
||||
|
||||
# Fallback to string representation if we can't extract the raw response
|
||||
return str(result)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error getting agent response: {e}")
|
||||
return "I'm having trouble processing that request. Can we try again?"
|
||||
|
||||
|
||||
# ------------------ SPEAK WITH ELEVENLABS ------------------
|
||||
def speak_response(text):
|
||||
print(f"🤖 Agent: {text}")
|
||||
audio = tts_client.text_to_speech.convert(
|
||||
text=text,
|
||||
voice_id="JBFqnCBsd6RMkjVDRZzb",
|
||||
model_id="eleven_multilingual_v2",
|
||||
output_format="mp3_44100_128"
|
||||
)
|
||||
play(audio)
|
||||
|
||||
|
||||
# ------------------ MAIN LOOP ------------------
|
||||
def run_voice_agent():
|
||||
print("🧠 Voice agent (Whisper + Mem0 + ElevenLabs) is ready! Say something.")
|
||||
while True:
|
||||
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp_audio:
|
||||
record_audio(tmp_audio.name)
|
||||
try:
|
||||
user_text = transcribe_whisper(tmp_audio.name)
|
||||
if user_text.lower() in ['exit', 'quit', 'stop']:
|
||||
print("👋 Exiting.")
|
||||
break
|
||||
response = get_agent_response(user_text)
|
||||
speak_response(response)
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
# Initialize memory with user preferences before starting the voice agent (this can be done once)
|
||||
initialize_memory()
|
||||
|
||||
# Run the voice assistant
|
||||
run_voice_agent()
|
||||
except KeyboardInterrupt:
|
||||
print("\n👋 Program interrupted. Exiting.")
|
||||
except Exception as e:
|
||||
print(f"❌ Fatal error: {e}")
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
.env*
|
||||
dist
|
||||
package-lock.json
|
||||
@@ -0,0 +1,88 @@
|
||||
# Mem0 Assistant Chrome Extension
|
||||
|
||||
A powerful Chrome extension that combines AI chat with your personal knowledge base through mem0. Get instant, personalized answers about video content while leveraging your own knowledge and memories - all without leaving the page.
|
||||
|
||||
## Development
|
||||
|
||||
1. Install dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Start development mode:
|
||||
```bash
|
||||
npm run watch
|
||||
```
|
||||
|
||||
3. Build for production:
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- AI-powered chat interface directly in YouTube
|
||||
- Memory capabilities powered by Mem0
|
||||
- Dark mode support
|
||||
- Customizable options
|
||||
|
||||
## Permissions
|
||||
|
||||
- activeTab: For accessing the current tab
|
||||
- storage: For saving user preferences
|
||||
- scripting: For injecting content scripts
|
||||
|
||||
## Host Permissions
|
||||
|
||||
- youtube.com
|
||||
- openai.com
|
||||
- mem0.ai
|
||||
|
||||
## Features
|
||||
|
||||
- **Contextual AI Chat**: Ask questions about videos you're watching
|
||||
- **Seamless Integration**: Chat interface sits alongside YouTube's native UI
|
||||
- **OpenAI-Powered**: Uses GPT models for intelligent responses
|
||||
- **Customizable**: Configure model settings, appearance, and behavior
|
||||
- **Future mem0 Integration**: Personalized responses based on your knowledge (coming soon)
|
||||
|
||||
## Installation
|
||||
|
||||
### From Source (Developer Mode)
|
||||
|
||||
1. Download or clone this repository
|
||||
2. Open Chrome and navigate to `chrome://extensions/`
|
||||
3. Enable "Developer mode" (toggle in the top-right corner)
|
||||
4. Click "Load unpacked" and select the extension directory
|
||||
5. The extension should now be installed and visible in your toolbar
|
||||
|
||||
### Setup
|
||||
|
||||
1. Click the extension icon in your toolbar
|
||||
2. Enter your OpenAI API key (required to use the extension)
|
||||
3. Configure additional settings if desired
|
||||
4. Navigate to YouTube to start using the assistant
|
||||
|
||||
## Usage
|
||||
|
||||
1. Visit any YouTube video
|
||||
2. Click the AI assistant icon in the corner of the page to open the chat interface
|
||||
3. Ask questions about the video content
|
||||
4. The AI will respond with contextual information
|
||||
|
||||
### Example Prompts
|
||||
|
||||
- "Can you summarize the main points of this video?"
|
||||
- "What is the speaker explaining at 5:23?"
|
||||
- "Explain the concept they just mentioned"
|
||||
- "How does this relate to [topic I'm learning about]?"
|
||||
- "What are some practical applications of what's being discussed?"
|
||||
|
||||
- **API Settings**: Change model, adjust tokens, modify temperature
|
||||
- **Interface Settings**: Control where and how the chat appears
|
||||
- **Behavior Settings**: Configure auto-context extraction
|
||||
|
||||
## Privacy & Data
|
||||
|
||||
- Your API keys are stored locally in your browser
|
||||
- Video context and transcript is processed locally and only sent to OpenAI when you ask questions
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "YouTube Assistant powered by Mem0",
|
||||
"version": "1.0",
|
||||
"description": "An AI-powered YouTube assistant with memory capabilities from Mem0",
|
||||
"permissions": [
|
||||
"activeTab",
|
||||
"storage",
|
||||
"scripting"
|
||||
],
|
||||
"host_permissions": [
|
||||
"https://*.youtube.com/*",
|
||||
"https://*.openai.com/*",
|
||||
"https://*.mem0.ai/*"
|
||||
],
|
||||
"content_security_policy": {
|
||||
"extension_pages": "script-src 'self'; object-src 'self'",
|
||||
"sandbox": "sandbox allow-scripts; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self'"
|
||||
},
|
||||
"action": {
|
||||
"default_popup": "public/popup.html"
|
||||
},
|
||||
"options_page": "public/options.html",
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["https://*.youtube.com/*"],
|
||||
"js": ["dist/content.bundle.js"],
|
||||
"css": ["styles/content.css"]
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "src/background.js"
|
||||
},
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": [
|
||||
"assets/*",
|
||||
"dist/*",
|
||||
"styles/*",
|
||||
"node_modules/mem0ai/dist/*"
|
||||
],
|
||||
"matches": ["https://*.youtube.com/*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "mem0-assistant",
|
||||
"version": "1.0.0",
|
||||
"description": "A Chrome extension that integrates AI chat functionality directly into YouTube and other sites. Get instant answers about video content without leaving the page.",
|
||||
"main": "background.js",
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"watch": "webpack --config webpack.config.js --watch"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.0",
|
||||
"@babel/preset-env": "^7.22.0",
|
||||
"babel-loader": "^9.1.2",
|
||||
"css-loader": "^7.1.2",
|
||||
"style-loader": "^4.0.0",
|
||||
"webpack": "^5.85.0",
|
||||
"webpack-cli": "^5.1.1",
|
||||
"youtube-transcript": "^1.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"mem0ai": "^2.1.15"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>YouTube Assistant powered by Mem0</title>
|
||||
<link rel="stylesheet" href="../styles/options.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-content">
|
||||
<header>
|
||||
<div class="title-container">
|
||||
<h1>YouTube Assistant</h1>
|
||||
<div class="branding-container">
|
||||
<span class="powered-by">powered by</span>
|
||||
<a href="https://mem0.ai" target="_blank">
|
||||
<img src="../assets/dark.svg" alt="Mem0 Logo" class="logo-img">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="description">
|
||||
Configure your YouTube Assistant preferences.
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div id="status-container"></div>
|
||||
|
||||
<div class="section">
|
||||
<h2>Model Settings</h2>
|
||||
<div class="form-group">
|
||||
<label for="model">OpenAI Model</label>
|
||||
<select id="model">
|
||||
<option value="o3">o3</option>
|
||||
<option value="o1">o1</option>
|
||||
<option value="o1-mini">o1-mini</option>
|
||||
<option value="o1-pro">o1-pro</option>
|
||||
<option value="gpt-4o">GPT-4o</option>
|
||||
<option value="gpt-4o-mini">GPT-4o mini</option>
|
||||
</select>
|
||||
<div class="description" style="margin-top: 8px; font-size: 13px">
|
||||
Choose the OpenAI model to use depending on your needs.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="max-tokens">Maximum Response Length</label>
|
||||
<input
|
||||
type="number"
|
||||
id="max-tokens"
|
||||
min="50"
|
||||
max="4000"
|
||||
value="2000"
|
||||
/>
|
||||
<div class="description" style="margin-top: 8px; font-size: 13px">
|
||||
Maximum number of tokens in the AI's response. Higher values allow
|
||||
for longer responses but may increase processing time.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="temperature">Response Creativity</label>
|
||||
<input
|
||||
type="range"
|
||||
id="temperature"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.1"
|
||||
value="0.7"
|
||||
/>
|
||||
<div
|
||||
id="temperature-value"
|
||||
style="display: inline-block; margin-left: 10px"
|
||||
>
|
||||
0.7
|
||||
</div>
|
||||
<div class="description" style="margin-top: 8px; font-size: 13px">
|
||||
Controls response randomness. Lower values (0.1-0.3) are more
|
||||
focused and deterministic, higher values (0.7-0.9) are more creative
|
||||
and diverse.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2>Create Memories</h2>
|
||||
<div class="description">
|
||||
Add information about yourself that you want the AI to remember. This
|
||||
information will be used to provide more personalized responses.
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="memory-input">Your Information</label>
|
||||
<textarea
|
||||
id="memory-input"
|
||||
class="memory-input"
|
||||
placeholder="Enter information about yourself that you want the AI to remember..."
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button id="add-memory" class="primary">
|
||||
<span class="button-text">Add Memory</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="memory-result" class="memory-result"></div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button id="reset-defaults" class="secondary-button">
|
||||
Reset to Defaults
|
||||
</button>
|
||||
<button id="save-options">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Memories Sidebar -->
|
||||
<div class="memories-sidebar" id="memories-sidebar">
|
||||
<div class="memories-header">
|
||||
<h2 class="memories-title">Your Memories</h2>
|
||||
<div class="memories-actions">
|
||||
<button
|
||||
id="refresh-memories"
|
||||
class="memory-action-btn"
|
||||
title="Refresh Memories"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M23 4v6h-6"></path>
|
||||
<path d="M1 20v-6h6"></path>
|
||||
<path
|
||||
d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
id="delete-all-memories"
|
||||
class="memory-action-btn delete"
|
||||
title="Delete All Memories"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M3 6h18"></path>
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
|
||||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="memories-list" id="memories-list">
|
||||
<!-- Memories will be populated here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Memory Modal -->
|
||||
<div class="edit-memory-modal" id="edit-memory-modal">
|
||||
<div class="edit-memory-content">
|
||||
<div class="edit-memory-header">
|
||||
<h3 class="edit-memory-title">Edit Memory</h3>
|
||||
<button class="edit-memory-close" id="close-edit-modal">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<textarea class="edit-memory-textarea" id="edit-memory-text"></textarea>
|
||||
<div class="edit-memory-actions">
|
||||
<button class="memory-action-btn delete" id="delete-memory">
|
||||
Delete
|
||||
</button>
|
||||
<button class="memory-action-btn" id="save-memory">
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../dist/options.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,165 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>YouTube Assistant powered by Mem0</title>
|
||||
<link rel="stylesheet" href="../styles/popup.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>YouTube Assistant</h1>
|
||||
<div class="branding-container">
|
||||
<span class="powered-by">powered by</span>
|
||||
<a href="https://mem0.ai" target="_blank">
|
||||
<img src="../assets/dark.svg" alt="Mem0 Logo" class="logo-img">
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<!-- Status area -->
|
||||
<div id="status-container"></div>
|
||||
|
||||
<!-- API key input, only shown if not set -->
|
||||
<div id="api-key-section" class="api-key-section">
|
||||
<label for="api-key">OpenAI API Key</label>
|
||||
<div class="api-key-input-wrapper">
|
||||
<input type="password" id="api-key" placeholder="sk-..." />
|
||||
<button class="toggle-password" id="toggle-openai-key">
|
||||
<svg
|
||||
class="icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<button id="save-api-key" class="save-button">
|
||||
<svg
|
||||
class="icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"
|
||||
></path>
|
||||
<polyline points="17 21 17 13 7 13 7 21"></polyline>
|
||||
<polyline points="7 3 7 8 15 8"></polyline>
|
||||
</svg>
|
||||
Save OpenAI Key
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- mem0 API key input -->
|
||||
<div id="mem0-api-key-section" class="api-key-section">
|
||||
<label for="mem0-api-key">Mem0 API Key</label>
|
||||
<div class="api-key-input-wrapper">
|
||||
<input
|
||||
type="password"
|
||||
id="mem0-api-key"
|
||||
placeholder="Enter your mem0 API key"
|
||||
/>
|
||||
<button class="toggle-password" id="toggle-mem0-key">
|
||||
<svg
|
||||
class="icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="api-key-actions">
|
||||
<p>Get your API key from <a href="https://mem0.ai" target="_blank" class="get-key-link">mem0.ai</a> to integrate memory features in the chat.</p>
|
||||
<button id="save-mem0-api-key" class="save-button">
|
||||
<svg
|
||||
class="icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"
|
||||
></path>
|
||||
<polyline points="17 21 17 13 7 13 7 21"></polyline>
|
||||
<polyline points="7 3 7 8 15 8"></polyline>
|
||||
</svg>
|
||||
Save Mem0 Key
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action buttons -->
|
||||
<div class="actions">
|
||||
<button id="toggle-chat">
|
||||
<svg
|
||||
class="icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
|
||||
></path>
|
||||
</svg>
|
||||
Chat
|
||||
</button>
|
||||
<button id="open-options">
|
||||
<svg
|
||||
class="icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
<path
|
||||
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"
|
||||
></path>
|
||||
</svg>
|
||||
Settings
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Future mem0 integration status -->
|
||||
<div class="mem0-status">
|
||||
<p>
|
||||
Mem0 integration:
|
||||
<span id="mem0-status-text">Not configured</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../src/popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,255 @@
|
||||
// Background script to handle API calls to OpenAI and manage extension state
|
||||
|
||||
// Configuration (will be stored in sync storage eventually)
|
||||
let config = {
|
||||
apiKey: "", // Will be set by user in options
|
||||
mem0ApiKey: "", // Will be set by user in options
|
||||
model: "gpt-4",
|
||||
maxTokens: 2000,
|
||||
temperature: 0.7,
|
||||
enabledSites: ["youtube.com"],
|
||||
};
|
||||
|
||||
// Track if config is loaded
|
||||
let isConfigLoaded = false;
|
||||
|
||||
// Initialize configuration from storage
|
||||
chrome.storage.sync.get(
|
||||
["apiKey", "mem0ApiKey", "model", "maxTokens", "temperature", "enabledSites"],
|
||||
(result) => {
|
||||
if (result.apiKey) config.apiKey = result.apiKey;
|
||||
if (result.mem0ApiKey) config.mem0ApiKey = result.mem0ApiKey;
|
||||
if (result.model) config.model = result.model;
|
||||
if (result.maxTokens) config.maxTokens = result.maxTokens;
|
||||
if (result.temperature) config.temperature = result.temperature;
|
||||
if (result.enabledSites) config.enabledSites = result.enabledSites;
|
||||
|
||||
isConfigLoaded = true;
|
||||
}
|
||||
);
|
||||
|
||||
// Listen for messages from content script or popup
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
// Handle different message types
|
||||
switch (request.action) {
|
||||
case "sendChatRequest":
|
||||
sendChatRequest(request.messages, request.model || config.model)
|
||||
.then((response) => sendResponse(response))
|
||||
.catch((error) => sendResponse({ error: error.message }));
|
||||
return true; // Required for async response
|
||||
|
||||
case "saveConfig":
|
||||
saveConfig(request.config)
|
||||
.then(() => sendResponse({ success: true }))
|
||||
.catch((error) => sendResponse({ error: error.message }));
|
||||
return true;
|
||||
|
||||
case "getConfig":
|
||||
// If config isn't loaded yet, load it first
|
||||
if (!isConfigLoaded) {
|
||||
chrome.storage.sync.get(
|
||||
[
|
||||
"apiKey",
|
||||
"mem0ApiKey",
|
||||
"model",
|
||||
"maxTokens",
|
||||
"temperature",
|
||||
"enabledSites",
|
||||
],
|
||||
(result) => {
|
||||
if (result.apiKey) config.apiKey = result.apiKey;
|
||||
if (result.mem0ApiKey) config.mem0ApiKey = result.mem0ApiKey;
|
||||
if (result.model) config.model = result.model;
|
||||
if (result.maxTokens) config.maxTokens = result.maxTokens;
|
||||
if (result.temperature) config.temperature = result.temperature;
|
||||
if (result.enabledSites) config.enabledSites = result.enabledSites;
|
||||
isConfigLoaded = true;
|
||||
sendResponse({ config });
|
||||
}
|
||||
);
|
||||
return true;
|
||||
}
|
||||
sendResponse({ config });
|
||||
return false;
|
||||
|
||||
case "openOptions":
|
||||
// Open options page
|
||||
chrome.runtime.openOptionsPage(() => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error(
|
||||
"Error opening options page:",
|
||||
chrome.runtime.lastError
|
||||
);
|
||||
// Fallback: Try to open directly in a new tab
|
||||
chrome.tabs.create({ url: chrome.runtime.getURL("options.html") });
|
||||
}
|
||||
sendResponse({ success: true });
|
||||
});
|
||||
return true;
|
||||
|
||||
case "toggleChat":
|
||||
// Forward the toggle request to the active tab
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||
if (tabs[0]) {
|
||||
chrome.tabs
|
||||
.sendMessage(tabs[0].id, { action: "toggleChat" })
|
||||
.then((response) => sendResponse(response))
|
||||
.catch((error) => sendResponse({ error: error.message }));
|
||||
} else {
|
||||
sendResponse({ error: "No active tab found" });
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Handle extension icon click - toggle chat visibility
|
||||
chrome.action.onClicked.addListener((tab) => {
|
||||
chrome.tabs
|
||||
.sendMessage(tab.id, { action: "toggleChat" })
|
||||
.catch((error) => console.error("Error toggling chat:", error));
|
||||
});
|
||||
|
||||
// Save configuration to sync storage
|
||||
async function saveConfig(newConfig) {
|
||||
// Validate API key if provided
|
||||
if (newConfig.apiKey) {
|
||||
try {
|
||||
const isValid = await validateApiKey(newConfig.apiKey);
|
||||
if (!isValid) {
|
||||
throw new Error("Invalid API key");
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`API key validation failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Update local config
|
||||
config = { ...config, ...newConfig };
|
||||
|
||||
// Save to sync storage
|
||||
return chrome.storage.sync.set(newConfig);
|
||||
}
|
||||
|
||||
// Validate OpenAI API key with a simple request
|
||||
async function validateApiKey(apiKey) {
|
||||
try {
|
||||
const response = await fetch("https://api.openai.com/v1/models", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`API returned ${response.status}`);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("API key validation error:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Send a chat request to OpenAI API
|
||||
async function sendChatRequest(messages, model) {
|
||||
// Check if API key is set
|
||||
if (!config.apiKey) {
|
||||
return {
|
||||
error:
|
||||
"API key not configured. Please set your OpenAI API key in the extension options.",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: model || config.model,
|
||||
messages: messages.map((msg) => ({
|
||||
role: msg.role,
|
||||
content: msg.content,
|
||||
})),
|
||||
max_tokens: config.maxTokens,
|
||||
temperature: config.temperature,
|
||||
stream: true, // Enable streaming
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(
|
||||
errorData.error?.message || `API returned ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
// Create a ReadableStream from the response
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = "";
|
||||
|
||||
// Process the stream
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
// Decode the chunk and add to buffer
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
|
||||
// Process complete lines
|
||||
const lines = buffer.split("\n");
|
||||
buffer = lines.pop() || ""; // Keep the last incomplete line in the buffer
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("data: ")) {
|
||||
const data = line.slice(6);
|
||||
if (data === "[DONE]") {
|
||||
// Stream complete
|
||||
return { done: true };
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (parsed.choices[0].delta.content) {
|
||||
// Send the chunk to the content script
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||
if (tabs[0]) {
|
||||
chrome.tabs.sendMessage(tabs[0].id, {
|
||||
action: "streamChunk",
|
||||
chunk: parsed.choices[0].delta.content,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error parsing chunk:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { done: true };
|
||||
} catch (error) {
|
||||
console.error("Error sending chat request:", error);
|
||||
return { error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// Future: Add mem0 integration functions here
|
||||
// When ready, replace with actual implementation
|
||||
function mem0Integration() {
|
||||
// Placeholder for future mem0 integration
|
||||
return {
|
||||
getUserMemories: async (userId) => {
|
||||
return { memories: [] };
|
||||
},
|
||||
saveMemory: async (userId, memory) => {
|
||||
return { success: true };
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,657 @@
|
||||
// Main content script that injects the AI chat into YouTube
|
||||
import { YoutubeTranscript } from "youtube-transcript";
|
||||
import { MemoryClient } from "mem0ai";
|
||||
|
||||
// Configuration
|
||||
const config = {
|
||||
apiEndpoint: "https://api.openai.com/v1/chat/completions",
|
||||
model: "gpt-4o",
|
||||
chatPosition: "right", // Where to display the chat panel
|
||||
autoExtract: true, // Automatically extract video context
|
||||
mem0ApiKey: "", // Will be set through extension options
|
||||
};
|
||||
|
||||
// Initialize Mem0AI - will be initialized properly when API key is available
|
||||
let mem0client = null;
|
||||
let mem0Initializing = false;
|
||||
|
||||
// Function to initialize Mem0AI with API key from storage
|
||||
async function initializeMem0AI() {
|
||||
if (mem0Initializing) return; // Prevent multiple simultaneous initialization attempts
|
||||
mem0Initializing = true;
|
||||
|
||||
try {
|
||||
// Get API key from storage
|
||||
const items = await chrome.storage.sync.get(["mem0ApiKey"]);
|
||||
if (items.mem0ApiKey) {
|
||||
try {
|
||||
// Create new client instance with v2.1.11 configuration
|
||||
mem0client = new MemoryClient({
|
||||
apiKey: items.mem0ApiKey,
|
||||
projectId: "youtube-assistant", // Add a project ID for organization
|
||||
isExtension: true,
|
||||
});
|
||||
|
||||
// Set up custom instructions for the YouTube educational assistant
|
||||
await mem0client.updateProject({
|
||||
custom_instructions: `Your task: Create memories for a YouTube AI assistant. Focus on capturing:
|
||||
|
||||
1. User's Knowledge & Experience:
|
||||
- Direct statements about their skills, knowledge, or experience
|
||||
- Their level of expertise in specific areas
|
||||
- Technologies, frameworks, or tools they work with
|
||||
- Their learning journey or background
|
||||
|
||||
2. User's Interests & Goals:
|
||||
- What they're trying to learn or understand (user messages may include the video title)
|
||||
- Their specific questions or areas of confusion
|
||||
- Their learning objectives or career goals
|
||||
- Topics they want to explore further
|
||||
|
||||
3. Personal Context:
|
||||
- Their current role or position
|
||||
- Their learning style or preferences
|
||||
- Their experience level in the video's topic
|
||||
- Any challenges or difficulties they're facing
|
||||
|
||||
4. Video Engagement:
|
||||
- Their reactions to the content
|
||||
- Points they agree or disagree with
|
||||
- Areas they want to discuss further
|
||||
- Connections they make to other topics
|
||||
|
||||
For each message:
|
||||
- Extract both explicit statements and implicit knowledge
|
||||
- Capture both video-related and personal context
|
||||
- Note any relationships between user's knowledge and video content
|
||||
|
||||
Remember: The goal is to build a comprehensive understanding of both the user's knowledge and their learning journey through YouTube.`,
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error initializing Mem0AI:", error);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
console.log("No Mem0AI API key found in storage");
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error accessing storage:", error);
|
||||
return false;
|
||||
} finally {
|
||||
mem0Initializing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Global state
|
||||
let chatState = {
|
||||
messages: [],
|
||||
isVisible: false,
|
||||
isLoading: false,
|
||||
videoContext: null,
|
||||
transcript: null, // Add transcript to state
|
||||
userMemories: null, // Will store retrieved memories
|
||||
currentStreamingMessage: null, // Track the current streaming message
|
||||
};
|
||||
|
||||
// Function to extract video ID from YouTube URL
|
||||
function getYouTubeVideoId(url) {
|
||||
const urlObj = new URL(url);
|
||||
const searchParams = new URLSearchParams(urlObj.search);
|
||||
return searchParams.get("v");
|
||||
}
|
||||
|
||||
// Function to fetch and log transcript
|
||||
async function fetchAndLogTranscript() {
|
||||
try {
|
||||
// Check if we're on a YouTube video page
|
||||
if (
|
||||
window.location.hostname.includes("youtube.com") &&
|
||||
window.location.pathname.includes("/watch")
|
||||
) {
|
||||
const videoId = getYouTubeVideoId(window.location.href);
|
||||
|
||||
if (videoId) {
|
||||
// Fetch transcript using youtube-transcript package
|
||||
const transcript = await YoutubeTranscript.fetchTranscript(videoId);
|
||||
|
||||
// Decode HTML entities in transcript text
|
||||
const decodedTranscript = transcript.map((entry) => ({
|
||||
...entry,
|
||||
text: entry.text
|
||||
.replace(/&#39;/g, "'")
|
||||
.replace(/&quot;/g, '"')
|
||||
.replace(/&lt;/g, "<")
|
||||
.replace(/&gt;/g, ">")
|
||||
.replace(/&amp;/g, "&"),
|
||||
}));
|
||||
|
||||
// Store transcript in state
|
||||
chatState.transcript = decodedTranscript;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching transcript:", error);
|
||||
chatState.transcript = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize when the DOM is fully loaded
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
init();
|
||||
fetchAndLogTranscript();
|
||||
await initializeMem0AI(); // Initialize Mem0AI
|
||||
});
|
||||
|
||||
// Also attempt to initialize on window load to handle YouTube's SPA behavior
|
||||
window.addEventListener("load", async () => {
|
||||
init();
|
||||
fetchAndLogTranscript();
|
||||
await initializeMem0AI(); // Initialize Mem0AI
|
||||
});
|
||||
|
||||
// Add another listener for YouTube's navigation events
|
||||
window.addEventListener("yt-navigate-finish", () => {
|
||||
init();
|
||||
fetchAndLogTranscript();
|
||||
});
|
||||
|
||||
// Main initialization function
|
||||
function init() {
|
||||
// Check if we're on a YouTube page
|
||||
if (
|
||||
!window.location.hostname.includes("youtube.com") ||
|
||||
!window.location.pathname.includes("/watch")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Give YouTube's DOM a moment to settle
|
||||
setTimeout(() => {
|
||||
// Only inject if not already present
|
||||
if (!document.getElementById("ai-chat-assistant-container")) {
|
||||
injectChatInterface();
|
||||
setupEventListeners();
|
||||
extractVideoContext();
|
||||
}
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
// Extract context from the current YouTube video
|
||||
function extractVideoContext() {
|
||||
if (!config.autoExtract) return;
|
||||
|
||||
try {
|
||||
const videoTitle =
|
||||
document.querySelector(
|
||||
"h1.title.style-scope.ytd-video-primary-info-renderer"
|
||||
)?.textContent ||
|
||||
document.querySelector("h1.title")?.textContent ||
|
||||
"Unknown Video";
|
||||
const channelName =
|
||||
document.querySelector("ytd-channel-name yt-formatted-string")
|
||||
?.textContent ||
|
||||
document.querySelector("ytd-channel-name")?.textContent ||
|
||||
"Unknown Channel";
|
||||
|
||||
// Video ID from URL
|
||||
const videoId = new URLSearchParams(window.location.search).get("v");
|
||||
|
||||
// Update state with basic video context first
|
||||
chatState.videoContext = {
|
||||
title: videoTitle,
|
||||
channel: channelName,
|
||||
videoId: videoId,
|
||||
url: window.location.href,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error extracting video context:", error);
|
||||
chatState.videoContext = {
|
||||
title: "Error extracting video information",
|
||||
url: window.location.href,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Inject the chat interface into the YouTube page
|
||||
function injectChatInterface() {
|
||||
// Create main container
|
||||
const container = document.createElement("div");
|
||||
container.id = "ai-chat-assistant-container";
|
||||
container.className = "ai-chat-container";
|
||||
|
||||
// Set up basic HTML structure
|
||||
container.innerHTML = `
|
||||
<div class="ai-chat-header">
|
||||
<div class="ai-chat-tabs">
|
||||
<button class="ai-chat-tab active" data-tab="chat">Chat</button>
|
||||
<button class="ai-chat-tab" data-tab="memories">Memories</button>
|
||||
</div>
|
||||
<div class="ai-chat-controls">
|
||||
<button id="ai-chat-minimize" class="ai-chat-btn" title="Minimize">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="ai-chat-close" class="ai-chat-btn" title="Close">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-chat-body">
|
||||
<div id="ai-chat-content" class="ai-chat-content">
|
||||
<div id="ai-chat-messages" class="ai-chat-messages"></div>
|
||||
<div class="ai-chat-input-container">
|
||||
<textarea id="ai-chat-input" placeholder="Ask about this video..."></textarea>
|
||||
<button id="ai-chat-send" class="ai-chat-send-btn" title="Send message">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="22" y1="2" x2="11" y2="13"></line>
|
||||
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ai-chat-memories" class="ai-chat-memories" style="display: none;">
|
||||
<div class="memories-header">
|
||||
<div class="memories-title">
|
||||
Manage memories <a href="#" id="manage-memories-link" title="Open options page">here <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
|
||||
<polyline points="15 3 21 3 21 9"></polyline>
|
||||
<line x1="10" y1="14" x2="21" y2="3"></line>
|
||||
</svg></a>
|
||||
</div>
|
||||
<button id="refresh-memories" class="ai-chat-btn" title="Refresh memories">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M23 4v6h-6"></path>
|
||||
<path d="M1 20v-6h6"></path>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="memories-list" class="memories-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Append to body
|
||||
document.body.appendChild(container);
|
||||
|
||||
// Add welcome message
|
||||
addMessage(
|
||||
"assistant",
|
||||
"Hello! I can help answer questions about this video. What would you like to know?"
|
||||
);
|
||||
}
|
||||
|
||||
// Set up event listeners for the chat interface
|
||||
function setupEventListeners() {
|
||||
// Tab switching
|
||||
const tabs = document.querySelectorAll(".ai-chat-tab");
|
||||
tabs.forEach((tab) => {
|
||||
tab.addEventListener("click", () => {
|
||||
// Update active tab
|
||||
tabs.forEach((t) => t.classList.remove("active"));
|
||||
tab.classList.add("active");
|
||||
|
||||
// Show corresponding content
|
||||
const tabName = tab.dataset.tab;
|
||||
document.getElementById("ai-chat-content").style.display =
|
||||
tabName === "chat" ? "flex" : "none";
|
||||
document.getElementById("ai-chat-memories").style.display =
|
||||
tabName === "memories" ? "flex" : "none";
|
||||
|
||||
// Load memories if switching to memories tab
|
||||
if (tabName === "memories") {
|
||||
loadMemories();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Refresh memories button
|
||||
document
|
||||
.getElementById("refresh-memories")
|
||||
?.addEventListener("click", loadMemories);
|
||||
|
||||
// Toggle chat visibility
|
||||
document.getElementById("ai-chat-toggle")?.addEventListener("click", () => {
|
||||
const container = document.getElementById("ai-chat-assistant-container");
|
||||
chatState.isVisible = !chatState.isVisible;
|
||||
|
||||
if (chatState.isVisible) {
|
||||
container.classList.add("visible");
|
||||
} else {
|
||||
container.classList.remove("visible");
|
||||
}
|
||||
});
|
||||
|
||||
// Close button
|
||||
document.getElementById("ai-chat-close")?.addEventListener("click", () => {
|
||||
const container = document.getElementById("ai-chat-assistant-container");
|
||||
container.classList.remove("visible");
|
||||
chatState.isVisible = false;
|
||||
});
|
||||
|
||||
// Minimize button
|
||||
document.getElementById("ai-chat-minimize")?.addEventListener("click", () => {
|
||||
const container = document.getElementById("ai-chat-assistant-container");
|
||||
container.classList.toggle("minimized");
|
||||
});
|
||||
|
||||
// Send message on button click
|
||||
document
|
||||
.getElementById("ai-chat-send")
|
||||
?.addEventListener("click", sendMessage);
|
||||
|
||||
// Send message on Enter key (but allow Shift+Enter for new lines)
|
||||
document.getElementById("ai-chat-input")?.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
|
||||
// Add click handler for manage memories link
|
||||
document
|
||||
.getElementById("manage-memories-link")
|
||||
.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
chrome.runtime.sendMessage({ action: "openOptions" }, (response) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error("Error opening options:", chrome.runtime.lastError);
|
||||
// Fallback: Try to open directly in a new tab
|
||||
chrome.tabs.create({ url: chrome.runtime.getURL("options.html") });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Add a message to the chat
|
||||
function addMessage(role, text, isStreaming = false) {
|
||||
const messagesContainer = document.getElementById("ai-chat-messages");
|
||||
if (!messagesContainer) return;
|
||||
|
||||
const messageElement = document.createElement("div");
|
||||
messageElement.className = `ai-chat-message ${role}`;
|
||||
|
||||
// Enhanced markdown-like formatting
|
||||
let formattedText = text
|
||||
// Code blocks
|
||||
.replace(/```([\s\S]*?)```/g, "<pre><code>$1</code></pre>")
|
||||
// Inline code
|
||||
.replace(/`([^`]+)`/g, "<code>$1</code>")
|
||||
// Links
|
||||
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>')
|
||||
// Bold text
|
||||
.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
|
||||
// Italic text
|
||||
.replace(/\*([^*]+)\*/g, "<em>$1</em>")
|
||||
// Lists
|
||||
.replace(/^\s*[-*]\s+(.+)$/gm, "<li>$1</li>")
|
||||
.replace(/(<li>.*<\/li>)/s, "<ul>$1</ul>")
|
||||
// Line breaks
|
||||
.replace(/\n/g, "<br>");
|
||||
|
||||
messageElement.innerHTML = formattedText;
|
||||
messagesContainer.appendChild(messageElement);
|
||||
|
||||
// Scroll to bottom
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
|
||||
// Add to messages array if not streaming
|
||||
if (!isStreaming) {
|
||||
chatState.messages.push({ role, content: text });
|
||||
}
|
||||
|
||||
return messageElement;
|
||||
}
|
||||
|
||||
// Format streaming text with markdown
|
||||
function formatStreamingText(text) {
|
||||
return text
|
||||
// Code blocks
|
||||
.replace(/```([\s\S]*?)```/g, "<pre><code>$1</code></pre>")
|
||||
// Inline code
|
||||
.replace(/`([^`]+)`/g, "<code>$1</code>")
|
||||
// Links
|
||||
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>')
|
||||
// Bold text
|
||||
.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
|
||||
// Italic text
|
||||
.replace(/\*([^*]+)\*/g, "<em>$1</em>")
|
||||
// Lists
|
||||
.replace(/^\s*[-*]\s+(.+)$/gm, "<li>$1</li>")
|
||||
.replace(/(<li>.*<\/li>)/s, "<ul>$1</ul>")
|
||||
// Line breaks
|
||||
.replace(/\n/g, "<br>");
|
||||
}
|
||||
|
||||
// Send a message to the AI
|
||||
async function sendMessage() {
|
||||
const inputElement = document.getElementById("ai-chat-input");
|
||||
if (!inputElement) return;
|
||||
|
||||
const userMessage = inputElement.value.trim();
|
||||
if (!userMessage) return;
|
||||
|
||||
// Clear input
|
||||
inputElement.value = "";
|
||||
|
||||
// Add user message to chat
|
||||
addMessage("user", userMessage);
|
||||
|
||||
// Show loading indicator
|
||||
chatState.isLoading = true;
|
||||
const loadingMessage = document.createElement("div");
|
||||
loadingMessage.className = "ai-chat-message assistant loading";
|
||||
loadingMessage.textContent = "Thinking...";
|
||||
document.getElementById("ai-chat-messages").appendChild(loadingMessage);
|
||||
|
||||
try {
|
||||
// If mem0client is available, store the message as a memory and search for relevant memories
|
||||
if (mem0client) {
|
||||
try {
|
||||
// Store the message as a memory
|
||||
await mem0client.add(
|
||||
[
|
||||
{
|
||||
role: "user",
|
||||
content: `${userMessage}\n\nVideo title: ${chatState.videoContext?.title}`,
|
||||
},
|
||||
],
|
||||
{
|
||||
user_id: "youtube-assistant-mem0", // Required parameter
|
||||
metadata: {
|
||||
videoId: chatState.videoContext?.videoId || "",
|
||||
videoTitle: chatState.videoContext?.title || "",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Search for relevant memories
|
||||
const searchResults = await mem0client.search(userMessage, {
|
||||
user_id: "youtube-assistant-mem0", // Required parameter
|
||||
limit: 5,
|
||||
});
|
||||
|
||||
// Store the retrieved memories
|
||||
chatState.userMemories = searchResults || null;
|
||||
} catch (memoryError) {
|
||||
console.error("Error with Mem0AI operations:", memoryError);
|
||||
// Continue with the chat process even if memory operations fail
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare messages with context (now includes memories if available)
|
||||
const contextualizedMessages = prepareMessagesWithContext();
|
||||
|
||||
// Remove loading message
|
||||
document.getElementById("ai-chat-messages").removeChild(loadingMessage);
|
||||
|
||||
// Create a new message element for streaming
|
||||
chatState.currentStreamingMessage = addMessage("assistant", "", true);
|
||||
|
||||
// Send to background script to handle API call
|
||||
chrome.runtime.sendMessage(
|
||||
{
|
||||
action: "sendChatRequest",
|
||||
messages: contextualizedMessages,
|
||||
model: config.model,
|
||||
},
|
||||
(response) => {
|
||||
chatState.isLoading = false;
|
||||
|
||||
if (response.error) {
|
||||
addMessage("system", `Error: ${response.error}`);
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
// Remove loading indicator
|
||||
document.getElementById("ai-chat-messages").removeChild(loadingMessage);
|
||||
chatState.isLoading = false;
|
||||
|
||||
// Show error
|
||||
addMessage("system", `Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare messages with added context
|
||||
function prepareMessagesWithContext() {
|
||||
const messages = [...chatState.messages];
|
||||
|
||||
// If we have video context, add it as system message at the beginning
|
||||
if (chatState.videoContext) {
|
||||
let transcriptSection = "";
|
||||
|
||||
// Add transcript if available
|
||||
if (chatState.transcript) {
|
||||
// Format transcript into a readable string
|
||||
const formattedTranscript = chatState.transcript
|
||||
.map((entry) => `${entry.text}`)
|
||||
.join("\n");
|
||||
|
||||
transcriptSection = `\n\nTranscript:\n${formattedTranscript}`;
|
||||
}
|
||||
|
||||
// Add user memories if available
|
||||
let userMemoriesSection = "";
|
||||
if (chatState.userMemories && chatState.userMemories.length > 0) {
|
||||
const formattedMemories = chatState.userMemories
|
||||
.map((memory) => `${memory.memory}`)
|
||||
.join("\n");
|
||||
|
||||
userMemoriesSection = `\n\nUser Memories:\n${formattedMemories}\n\n`;
|
||||
}
|
||||
|
||||
const systemContent = `You are an AI assistant helping with a YouTube video. Here's the context:
|
||||
Title: ${chatState.videoContext.title}
|
||||
Channel: ${chatState.videoContext.channel}
|
||||
URL: ${chatState.videoContext.url}
|
||||
|
||||
${
|
||||
userMemoriesSection
|
||||
? `Use the user memories below to personalize your response based on their past interactions and interests. These memories represent relevant past conversations and information about the user.
|
||||
${userMemoriesSection}
|
||||
`
|
||||
: ""
|
||||
}
|
||||
|
||||
Please provide helpful, relevant information based on the video's content.
|
||||
${
|
||||
transcriptSection
|
||||
? `"Use the transcript below to provide accurate answers about the video. Ignore if the transcript doesn't make sense."
|
||||
${transcriptSection}
|
||||
`
|
||||
: "Since the transcript is not available, focus on general questions about the topic and use the video title for context. If asked about specific parts of the video content, politely explain that the video doesn't have a transcript."
|
||||
}
|
||||
|
||||
Be concise and helpful in your responses.
|
||||
`;
|
||||
|
||||
messages.unshift({
|
||||
role: "system",
|
||||
content: systemContent,
|
||||
});
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
// Listen for commands from the background script or popup
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.action === "toggleChat") {
|
||||
const container = document.getElementById("ai-chat-assistant-container");
|
||||
chatState.isVisible = !chatState.isVisible;
|
||||
|
||||
if (chatState.isVisible) {
|
||||
container.classList.add("visible");
|
||||
} else {
|
||||
container.classList.remove("visible");
|
||||
}
|
||||
|
||||
sendResponse({ success: true });
|
||||
} else if (message.action === "streamChunk") {
|
||||
// Handle streaming chunks
|
||||
if (chatState.currentStreamingMessage) {
|
||||
const currentContent = chatState.currentStreamingMessage.innerHTML;
|
||||
chatState.currentStreamingMessage.innerHTML = formatStreamingText(currentContent + message.chunk);
|
||||
|
||||
// Scroll to bottom
|
||||
const messagesContainer = document.getElementById("ai-chat-messages");
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Load memories from mem0
|
||||
async function loadMemories() {
|
||||
try {
|
||||
const memoriesContainer = document.getElementById("memories-list");
|
||||
memoriesContainer.innerHTML =
|
||||
'<div class="loading">Loading memories...</div>';
|
||||
|
||||
// If client isn't initialized, try to initialize it
|
||||
if (!mem0client) {
|
||||
const initialized = await initializeMem0AI();
|
||||
if (!initialized) {
|
||||
memoriesContainer.innerHTML =
|
||||
'<div class="error">Please set your Mem0 API key in the extension options.</div>';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const response = await mem0client.getAll({
|
||||
user_id: "youtube-assistant-mem0",
|
||||
page: 1,
|
||||
page_size: 50,
|
||||
});
|
||||
|
||||
if (response && response.results) {
|
||||
memoriesContainer.innerHTML = "";
|
||||
response.results.forEach((memory) => {
|
||||
const memoryElement = document.createElement("div");
|
||||
memoryElement.className = "memory-item";
|
||||
memoryElement.textContent = memory.memory;
|
||||
memoriesContainer.appendChild(memoryElement);
|
||||
});
|
||||
|
||||
if (response.results.length === 0) {
|
||||
memoriesContainer.innerHTML =
|
||||
'<div class="no-memories">No memories found</div>';
|
||||
}
|
||||
} else {
|
||||
memoriesContainer.innerHTML =
|
||||
'<div class="no-memories">No memories found</div>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading memories:", error);
|
||||
document.getElementById("memories-list").innerHTML =
|
||||
'<div class="error">Error loading memories. Please try again.</div>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
// Options page functionality for AI Chat Assistant
|
||||
import { MemoryClient } from "mem0ai";
|
||||
|
||||
// Default configuration
|
||||
const defaultConfig = {
|
||||
model: "gpt-4o",
|
||||
maxTokens: 2000,
|
||||
temperature: 0.7,
|
||||
enabledSites: ["youtube.com"],
|
||||
};
|
||||
|
||||
// Initialize Mem0AI client
|
||||
let mem0client = null;
|
||||
|
||||
// Initialize when the DOM is fully loaded
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
|
||||
// Initialize options page
|
||||
async function init() {
|
||||
// Set up event listeners
|
||||
document
|
||||
.getElementById("save-options")
|
||||
.addEventListener("click", saveOptions);
|
||||
document
|
||||
.getElementById("reset-defaults")
|
||||
.addEventListener("click", resetToDefaults);
|
||||
document.getElementById("add-memory").addEventListener("click", addMemory);
|
||||
|
||||
// Set up slider value display
|
||||
const temperatureSlider = document.getElementById("temperature");
|
||||
const temperatureValue = document.getElementById("temperature-value");
|
||||
|
||||
temperatureSlider.addEventListener("input", () => {
|
||||
temperatureValue.textContent = temperatureSlider.value;
|
||||
});
|
||||
|
||||
// Set up memories sidebar functionality
|
||||
document
|
||||
.getElementById("refresh-memories")
|
||||
.addEventListener("click", fetchMemories);
|
||||
document
|
||||
.getElementById("delete-all-memories")
|
||||
.addEventListener("click", deleteAllMemories);
|
||||
document
|
||||
.getElementById("close-edit-modal")
|
||||
.addEventListener("click", closeEditModal);
|
||||
document.getElementById("save-memory").addEventListener("click", saveMemory);
|
||||
document
|
||||
.getElementById("delete-memory")
|
||||
.addEventListener("click", deleteMemory);
|
||||
|
||||
// Load current configuration
|
||||
await loadConfig();
|
||||
// Initialize Mem0AI and load memories
|
||||
await initializeMem0AI();
|
||||
await fetchMemories();
|
||||
}
|
||||
|
||||
// Initialize Mem0AI with API key from storage
|
||||
async function initializeMem0AI() {
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({ action: "getConfig" });
|
||||
const mem0ApiKey = response.config.mem0ApiKey;
|
||||
|
||||
if (!mem0ApiKey) {
|
||||
showMemoriesError("Please configure your Mem0 API key in the popup");
|
||||
return false;
|
||||
}
|
||||
|
||||
mem0client = new MemoryClient({
|
||||
apiKey: mem0ApiKey,
|
||||
projectId: "youtube-assistant",
|
||||
isExtension: true,
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error initializing Mem0AI:", error);
|
||||
showMemoriesError("Failed to initialize Mem0AI");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Load configuration from storage
|
||||
async function loadConfig() {
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({ action: "getConfig" });
|
||||
const config = response.config;
|
||||
|
||||
// Update form fields with current values
|
||||
if (config.model) {
|
||||
document.getElementById("model").value = config.model;
|
||||
}
|
||||
|
||||
if (config.maxTokens) {
|
||||
document.getElementById("max-tokens").value = config.maxTokens;
|
||||
}
|
||||
|
||||
if (config.temperature !== undefined) {
|
||||
const temperatureSlider = document.getElementById("temperature");
|
||||
temperatureSlider.value = config.temperature;
|
||||
document.getElementById("temperature-value").textContent =
|
||||
config.temperature;
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus(`Error loading configuration: ${error.message}`, "error");
|
||||
}
|
||||
}
|
||||
|
||||
// Save options to storage
|
||||
async function saveOptions() {
|
||||
// Get values from form
|
||||
const model = document.getElementById("model").value;
|
||||
const maxTokens = parseInt(document.getElementById("max-tokens").value);
|
||||
const temperature = parseFloat(document.getElementById("temperature").value);
|
||||
|
||||
// Validate inputs
|
||||
if (maxTokens < 50 || maxTokens > 4000) {
|
||||
showStatus("Maximum tokens must be between 50 and 4000", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (temperature < 0 || temperature > 1) {
|
||||
showStatus("Temperature must be between 0 and 1", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare config object
|
||||
const config = {
|
||||
model,
|
||||
maxTokens,
|
||||
temperature,
|
||||
};
|
||||
|
||||
// Show loading status
|
||||
showStatus("Saving options...", "warning");
|
||||
|
||||
try {
|
||||
// Send to background script for saving
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
action: "saveConfig",
|
||||
config,
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
showStatus(`Error: ${response.error}`, "error");
|
||||
} else {
|
||||
showStatus("Options saved successfully", "success");
|
||||
loadConfig(); // Refresh the UI with the latest saved values
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus(`Error: ${error.message}`, "error");
|
||||
}
|
||||
}
|
||||
|
||||
// Reset options to defaults
|
||||
function resetToDefaults() {
|
||||
if (
|
||||
confirm(
|
||||
"Are you sure you want to reset all options to their default values?"
|
||||
)
|
||||
) {
|
||||
// Set form fields to default values
|
||||
document.getElementById("model").value = defaultConfig.model;
|
||||
document.getElementById("max-tokens").value = defaultConfig.maxTokens;
|
||||
|
||||
const temperatureSlider = document.getElementById("temperature");
|
||||
temperatureSlider.value = defaultConfig.temperature;
|
||||
document.getElementById("temperature-value").textContent =
|
||||
defaultConfig.temperature;
|
||||
|
||||
showStatus("Restored default values. Click Save to apply.", "warning");
|
||||
}
|
||||
}
|
||||
|
||||
// Memories functionality
|
||||
let currentMemory = null;
|
||||
|
||||
async function fetchMemories() {
|
||||
try {
|
||||
if (!mem0client) {
|
||||
const initialized = await initializeMem0AI();
|
||||
if (!initialized) return;
|
||||
}
|
||||
|
||||
const memories = await mem0client.getAll({
|
||||
user_id: "youtube-assistant-mem0",
|
||||
page: 1,
|
||||
page_size: 50,
|
||||
});
|
||||
displayMemories(memories.results);
|
||||
} catch (error) {
|
||||
console.error("Error fetching memories:", error);
|
||||
showMemoriesError("Failed to load memories");
|
||||
}
|
||||
}
|
||||
|
||||
function displayMemories(memories) {
|
||||
const memoriesList = document.getElementById("memories-list");
|
||||
memoriesList.innerHTML = "";
|
||||
|
||||
if (memories.length === 0) {
|
||||
memoriesList.innerHTML = `
|
||||
<div class="memory-item">
|
||||
<div class="memory-content">No memories found. Your memories will appear here.</div>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
memories.forEach((memory) => {
|
||||
const memoryElement = document.createElement("div");
|
||||
memoryElement.className = "memory-item";
|
||||
memoryElement.innerHTML = `
|
||||
<div class="memory-content">${memory.memory}</div>
|
||||
<div class="memory-meta">Last updated: ${new Date(
|
||||
memory.updated_at
|
||||
).toLocaleString()}</div>
|
||||
<div class="memory-actions">
|
||||
<button class="memory-action-btn edit" data-id="${
|
||||
memory.id
|
||||
}">Edit</button>
|
||||
<button class="memory-action-btn delete" data-id="${
|
||||
memory.id
|
||||
}">Delete</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add event listeners
|
||||
memoryElement
|
||||
.querySelector(".edit")
|
||||
.addEventListener("click", () => editMemory(memory));
|
||||
memoryElement
|
||||
.querySelector(".delete")
|
||||
.addEventListener("click", () => deleteMemory(memory.id));
|
||||
|
||||
memoriesList.appendChild(memoryElement);
|
||||
});
|
||||
}
|
||||
|
||||
function showMemoriesError(message) {
|
||||
const memoriesList = document.getElementById("memories-list");
|
||||
memoriesList.innerHTML = `
|
||||
<div class="memory-item">
|
||||
<div class="memory-content">${message}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async function deleteAllMemories() {
|
||||
if (
|
||||
!confirm(
|
||||
"Are you sure you want to delete all memories? This action cannot be undone."
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!mem0client) {
|
||||
const initialized = await initializeMem0AI();
|
||||
if (!initialized) return;
|
||||
}
|
||||
|
||||
await mem0client.deleteAll({
|
||||
user_id: "youtube-assistant-mem0",
|
||||
});
|
||||
showStatus("All memories deleted successfully", "success");
|
||||
await fetchMemories();
|
||||
} catch (error) {
|
||||
console.error("Error deleting memories:", error);
|
||||
showStatus("Failed to delete memories", "error");
|
||||
}
|
||||
}
|
||||
|
||||
function editMemory(memory) {
|
||||
currentMemory = memory;
|
||||
const modal = document.getElementById("edit-memory-modal");
|
||||
const textarea = document.getElementById("edit-memory-text");
|
||||
textarea.value = memory.memory;
|
||||
modal.classList.add("open");
|
||||
}
|
||||
|
||||
function closeEditModal() {
|
||||
const modal = document.getElementById("edit-memory-modal");
|
||||
modal.classList.remove("open");
|
||||
currentMemory = null;
|
||||
}
|
||||
|
||||
async function saveMemory() {
|
||||
if (!currentMemory) return;
|
||||
|
||||
try {
|
||||
if (!mem0client) {
|
||||
const initialized = await initializeMem0AI();
|
||||
if (!initialized) return;
|
||||
}
|
||||
|
||||
const textarea = document.getElementById("edit-memory-text");
|
||||
const updatedMemory = textarea.value.trim();
|
||||
|
||||
if (!updatedMemory) {
|
||||
showStatus("Memory cannot be empty", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
await mem0client.update(currentMemory.id, updatedMemory);
|
||||
|
||||
showStatus("Memory updated successfully", "success");
|
||||
closeEditModal();
|
||||
await fetchMemories();
|
||||
} catch (error) {
|
||||
console.error("Error updating memory:", error);
|
||||
showStatus("Failed to update memory", "error");
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteMemory(memoryId) {
|
||||
if (
|
||||
!confirm(
|
||||
"Are you sure you want to delete this memory? This action cannot be undone."
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!mem0client) {
|
||||
const initialized = await initializeMem0AI();
|
||||
if (!initialized) return;
|
||||
}
|
||||
|
||||
await mem0client.delete(memoryId);
|
||||
showStatus("Memory deleted successfully", "success");
|
||||
await fetchMemories();
|
||||
} catch (error) {
|
||||
console.error("Error deleting memory:", error);
|
||||
showStatus("Failed to delete memory", "error");
|
||||
}
|
||||
}
|
||||
|
||||
// Show status message
|
||||
function showStatus(message, type = "info") {
|
||||
const statusContainer = document.getElementById("status-container");
|
||||
|
||||
// Clear previous status
|
||||
statusContainer.innerHTML = "";
|
||||
|
||||
// Create status element
|
||||
const statusElement = document.createElement("div");
|
||||
statusElement.className = `status ${type}`;
|
||||
statusElement.textContent = message;
|
||||
|
||||
// Add to container
|
||||
statusContainer.appendChild(statusElement);
|
||||
|
||||
// Auto-clear success messages after 3 seconds
|
||||
if (type === "success") {
|
||||
setTimeout(() => {
|
||||
statusElement.style.opacity = "0";
|
||||
setTimeout(() => {
|
||||
if (statusContainer.contains(statusElement)) {
|
||||
statusContainer.removeChild(statusElement);
|
||||
}
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
// Add memory to Mem0
|
||||
async function addMemory() {
|
||||
const memoryInput = document.getElementById("memory-input");
|
||||
const addButton = document.getElementById("add-memory");
|
||||
const memoryResult = document.getElementById("memory-result");
|
||||
const buttonText = addButton.querySelector(".button-text");
|
||||
|
||||
const content = memoryInput.value.trim();
|
||||
|
||||
if (!content) {
|
||||
showMemoryResult(
|
||||
"Please enter some information to add as a memory",
|
||||
"error"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
addButton.disabled = true;
|
||||
buttonText.textContent = "Adding...";
|
||||
addButton.innerHTML =
|
||||
'<div class="loading-spinner"></div><span class="button-text">Adding...</span>';
|
||||
memoryResult.style.display = "none";
|
||||
|
||||
try {
|
||||
if (!mem0client) {
|
||||
const initialized = await initializeMem0AI();
|
||||
if (!initialized) return;
|
||||
}
|
||||
|
||||
const result = await mem0client.add(
|
||||
[
|
||||
{
|
||||
role: "user",
|
||||
content: content,
|
||||
},
|
||||
],
|
||||
{
|
||||
user_id: "youtube-assistant-mem0",
|
||||
}
|
||||
);
|
||||
|
||||
// Show success message with number of memories added
|
||||
showMemoryResult(
|
||||
`Added ${result.length || 0} new ${
|
||||
result.length === 1 ? "memory" : "memories"
|
||||
}`,
|
||||
"success"
|
||||
);
|
||||
|
||||
// Clear the input
|
||||
memoryInput.value = "";
|
||||
|
||||
// Refresh the memories list
|
||||
await fetchMemories();
|
||||
} catch (error) {
|
||||
showMemoryResult(`Error adding memory: ${error.message}`, "error");
|
||||
} finally {
|
||||
// Reset button state
|
||||
addButton.disabled = false;
|
||||
buttonText.textContent = "Add Memory";
|
||||
addButton.innerHTML = '<span class="button-text">Add Memory</span>';
|
||||
}
|
||||
}
|
||||
|
||||
// Show memory result message
|
||||
function showMemoryResult(message, type) {
|
||||
const memoryResult = document.getElementById("memory-result");
|
||||
memoryResult.textContent = message;
|
||||
memoryResult.className = `memory-result ${type}`;
|
||||
memoryResult.style.display = "block";
|
||||
|
||||
// Auto-clear success messages after 3 seconds
|
||||
if (type === "success") {
|
||||
setTimeout(() => {
|
||||
memoryResult.style.opacity = "0";
|
||||
setTimeout(() => {
|
||||
memoryResult.style.display = "none";
|
||||
memoryResult.style.opacity = "1";
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
// Popup functionality for AI Chat Assistant
|
||||
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
|
||||
// Initialize popup
|
||||
async function init() {
|
||||
try {
|
||||
// Set up event listeners
|
||||
document
|
||||
.getElementById("toggle-chat")
|
||||
.addEventListener("click", toggleChat);
|
||||
document
|
||||
.getElementById("open-options")
|
||||
.addEventListener("click", openOptions);
|
||||
document
|
||||
.getElementById("save-api-key")
|
||||
.addEventListener("click", saveApiKey);
|
||||
document
|
||||
.getElementById("save-mem0-api-key")
|
||||
.addEventListener("click", saveMem0ApiKey);
|
||||
|
||||
// Set up password toggle listeners
|
||||
document
|
||||
.getElementById("toggle-openai-key")
|
||||
.addEventListener("click", () => togglePasswordVisibility("api-key"));
|
||||
document
|
||||
.getElementById("toggle-mem0-key")
|
||||
.addEventListener("click", () =>
|
||||
togglePasswordVisibility("mem0-api-key")
|
||||
);
|
||||
|
||||
// Load current configuration and wait for it to complete
|
||||
await loadConfig();
|
||||
} catch (error) {
|
||||
console.error("Initialization error:", error);
|
||||
showStatus("Error initializing popup", "error");
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle chat visibility in the active tab
|
||||
function toggleChat() {
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||
if (tabs[0]) {
|
||||
// First check if we can inject the content script
|
||||
chrome.scripting
|
||||
.executeScript({
|
||||
target: { tabId: tabs[0].id },
|
||||
files: ["dist/content.bundle.js"],
|
||||
})
|
||||
.then(() => {
|
||||
// Now try to toggle the chat
|
||||
chrome.tabs
|
||||
.sendMessage(tabs[0].id, { action: "toggleChat" })
|
||||
.then((response) => {
|
||||
if (response && response.error) {
|
||||
console.error("Error toggling chat:", response.error);
|
||||
showStatus(
|
||||
"Chat interface not available on this page",
|
||||
"warning"
|
||||
);
|
||||
} else {
|
||||
// Close the popup after successful toggle
|
||||
window.close();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error toggling chat:", error);
|
||||
showStatus(
|
||||
"Chat interface not available on this page",
|
||||
"warning"
|
||||
);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error injecting content script:", error);
|
||||
showStatus("Cannot inject chat interface on this page", "error");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Open options page
|
||||
function openOptions() {
|
||||
// Send message to background script to handle opening options
|
||||
chrome.runtime.sendMessage({ action: "openOptions" }, (response) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error("Error opening options:", chrome.runtime.lastError);
|
||||
|
||||
// Direct fallback if communication with background script fails
|
||||
try {
|
||||
chrome.tabs.create({ url: chrome.runtime.getURL("options.html") });
|
||||
} catch (err) {
|
||||
console.error("Fallback failed:", err);
|
||||
// Last resort
|
||||
window.open(chrome.runtime.getURL("options.html"), "_blank");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Toggle password visibility
|
||||
function togglePasswordVisibility(inputId) {
|
||||
const input = document.getElementById(inputId);
|
||||
const type = input.type === "password" ? "text" : "password";
|
||||
input.type = type;
|
||||
|
||||
// Update the eye icon
|
||||
const button = input.nextElementSibling;
|
||||
const icon = button.querySelector(".icon");
|
||||
if (type === "text") {
|
||||
icon.innerHTML =
|
||||
'<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>';
|
||||
} else {
|
||||
icon.innerHTML =
|
||||
'<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle>';
|
||||
}
|
||||
}
|
||||
|
||||
// Save API key to storage
|
||||
async function saveApiKey() {
|
||||
const apiKeyInput = document.getElementById("api-key");
|
||||
const apiKey = apiKeyInput.value.trim();
|
||||
|
||||
// Show loading status
|
||||
showStatus("Saving API key...", "warning");
|
||||
|
||||
try {
|
||||
// Send to background script for validation and saving
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
action: "saveConfig",
|
||||
config: { apiKey },
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
showStatus(`Error: ${response.error}`, "error");
|
||||
} else {
|
||||
showStatus("API key saved successfully", "success");
|
||||
loadConfig(); // Refresh the UI
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus(`Error: ${error.message}`, "error");
|
||||
}
|
||||
}
|
||||
|
||||
// Save mem0 API key to storage
|
||||
async function saveMem0ApiKey() {
|
||||
const apiKeyInput = document.getElementById("mem0-api-key");
|
||||
const apiKey = apiKeyInput.value.trim();
|
||||
|
||||
// Show loading status
|
||||
showStatus("Saving Mem0 API key...", "warning");
|
||||
|
||||
try {
|
||||
// Send to background script for saving
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
action: "saveConfig",
|
||||
config: { mem0ApiKey: apiKey },
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
showStatus(`Error: ${response.error}`, "error");
|
||||
} else {
|
||||
showStatus("Mem0 API key saved successfully", "success");
|
||||
loadConfig(); // Refresh the UI
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus(`Error: ${error.message}`, "error");
|
||||
}
|
||||
}
|
||||
|
||||
// Load configuration from storage
|
||||
async function loadConfig() {
|
||||
try {
|
||||
// Add a small delay to ensure background script is ready
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const response = await chrome.runtime.sendMessage({ action: "getConfig" });
|
||||
const config = response.config || {};
|
||||
|
||||
// Update OpenAI API key field
|
||||
const apiKeyInput = document.getElementById("api-key");
|
||||
if (config.apiKey) {
|
||||
apiKeyInput.value = config.apiKey;
|
||||
apiKeyInput.type = "password"; // Ensure it's hidden by default
|
||||
document.getElementById("api-key-section").style.display = "block";
|
||||
} else {
|
||||
apiKeyInput.value = "";
|
||||
document.getElementById("api-key-section").style.display = "block";
|
||||
showStatus("Please set your OpenAI API key", "warning");
|
||||
}
|
||||
|
||||
// Update mem0 API key field
|
||||
const mem0ApiKeyInput = document.getElementById("mem0-api-key");
|
||||
if (config.mem0ApiKey) {
|
||||
mem0ApiKeyInput.value = config.mem0ApiKey;
|
||||
mem0ApiKeyInput.type = "password"; // Ensure it's hidden by default
|
||||
document.getElementById("mem0-api-key-section").style.display = "block";
|
||||
document.getElementById("mem0-status-text").textContent = "Connected";
|
||||
document.getElementById("mem0-status-text").style.color =
|
||||
"var(--success-color)";
|
||||
} else {
|
||||
mem0ApiKeyInput.value = "";
|
||||
document.getElementById("mem0-api-key-section").style.display = "block";
|
||||
document.getElementById("mem0-status-text").textContent =
|
||||
"Not configured";
|
||||
document.getElementById("mem0-status-text").style.color =
|
||||
"var(--warning-color)";
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading configuration:", error);
|
||||
showStatus(`Error loading configuration: ${error.message}`, "error");
|
||||
}
|
||||
}
|
||||
|
||||
// Show status message
|
||||
function showStatus(message, type = "info") {
|
||||
const statusContainer = document.getElementById("status-container");
|
||||
|
||||
// Clear previous status
|
||||
statusContainer.innerHTML = "";
|
||||
|
||||
// Create status element
|
||||
const statusElement = document.createElement("div");
|
||||
statusElement.className = `status ${type}`;
|
||||
statusElement.textContent = message;
|
||||
|
||||
// Add to container
|
||||
statusContainer.appendChild(statusElement);
|
||||
|
||||
// Auto-clear success messages after 3 seconds
|
||||
if (type === "success") {
|
||||
setTimeout(() => {
|
||||
statusElement.style.opacity = "0";
|
||||
setTimeout(() => {
|
||||
if (statusContainer.contains(statusElement)) {
|
||||
statusContainer.removeChild(statusElement);
|
||||
}
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
/* Styles for the AI Chat Assistant */
|
||||
/* Modern Dark Theme with Blue Accents */
|
||||
|
||||
:root {
|
||||
--chat-dark-bg: #1a1a1a;
|
||||
--chat-darker-bg: #121212;
|
||||
--chat-light-text: #f1f1f1;
|
||||
--chat-blue-accent: #3d84f7;
|
||||
--chat-blue-hover: #2d74e7;
|
||||
--chat-blue-light: rgba(61, 132, 247, 0.15);
|
||||
--chat-error: #ff4a4a;
|
||||
--chat-border-radius: 12px;
|
||||
--chat-message-radius: 12px;
|
||||
--chat-transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/* Main container */
|
||||
#ai-chat-assistant-container {
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
width: 380px;
|
||||
height: 550px;
|
||||
background-color: var(--chat-dark-bg);
|
||||
border-radius: var(--chat-border-radius);
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 9999;
|
||||
overflow: hidden;
|
||||
transition: var(--chat-transition);
|
||||
opacity: 0;
|
||||
transform: translateY(20px) scale(0.98);
|
||||
pointer-events: none;
|
||||
font-family: 'Roboto', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
/* When visible */
|
||||
#ai-chat-assistant-container.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
/* When minimized */
|
||||
#ai-chat-assistant-container.minimized {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#ai-chat-assistant-container.minimized .ai-chat-body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.ai-chat-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
background-color: var(--chat-darker-bg);
|
||||
color: var(--chat-light-text);
|
||||
border-top-left-radius: var(--chat-border-radius);
|
||||
border-top-right-radius: var(--chat-border-radius);
|
||||
cursor: move;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.ai-chat-title {
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.ai-chat-title::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--chat-blue-accent);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 10px var(--chat-blue-accent);
|
||||
}
|
||||
|
||||
.ai-chat-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.ai-chat-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--chat-light-text);
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: var(--chat-transition);
|
||||
}
|
||||
|
||||
.ai-chat-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
/* Body */
|
||||
.ai-chat-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
background-color: var(--chat-dark-bg);
|
||||
}
|
||||
|
||||
/* Messages container */
|
||||
.ai-chat-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(255, 255, 255, 0.1) transparent;
|
||||
}
|
||||
|
||||
.ai-chat-messages::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.ai-chat-messages::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.ai-chat-messages::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* Individual message */
|
||||
.ai-chat-message {
|
||||
max-width: 85%;
|
||||
padding: 12px 16px;
|
||||
border-radius: var(--chat-message-radius);
|
||||
line-height: 1.5;
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
animation: message-fade-in 0.3s ease;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@keyframes message-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* User message */
|
||||
.ai-chat-message.user {
|
||||
align-self: flex-end;
|
||||
background-color: var(--chat-blue-accent);
|
||||
color: white;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
/* Assistant message */
|
||||
.ai-chat-message.assistant {
|
||||
align-self: flex-start;
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
color: var(--chat-light-text);
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
/* System message */
|
||||
.ai-chat-message.system {
|
||||
align-self: center;
|
||||
background-color: rgba(255, 76, 76, 0.1);
|
||||
color: var(--chat-error);
|
||||
max-width: 90%;
|
||||
font-size: 13px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 76, 76, 0.2);
|
||||
}
|
||||
|
||||
/* Loading animation */
|
||||
.ai-chat-message.loading {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.ai-chat-message.loading:after {
|
||||
content: "...";
|
||||
animation: thinking 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes thinking {
|
||||
0% { content: "."; }
|
||||
33% { content: ".."; }
|
||||
66% { content: "..."; }
|
||||
}
|
||||
|
||||
/* Input area */
|
||||
.ai-chat-input-container {
|
||||
display: flex;
|
||||
padding: 12px 16px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
background-color: var(--chat-darker-bg);
|
||||
}
|
||||
|
||||
#ai-chat-input {
|
||||
flex: 1;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
color: var(--chat-light-text);
|
||||
border-radius: 20px;
|
||||
padding: 10px 16px;
|
||||
font-size: 14px;
|
||||
resize: none;
|
||||
max-height: 100px;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
transition: var(--chat-transition);
|
||||
}
|
||||
|
||||
#ai-chat-input::placeholder {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
#ai-chat-input:focus {
|
||||
border-color: var(--chat-blue-accent);
|
||||
background-color: rgba(255, 255, 255, 0.07);
|
||||
box-shadow: 0 0 0 1px rgba(61, 132, 247, 0.1);
|
||||
}
|
||||
|
||||
.ai-chat-send-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--chat-blue-accent);
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
margin-left: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: var(--chat-transition);
|
||||
}
|
||||
|
||||
.ai-chat-send-btn:hover {
|
||||
background-color: var(--chat-blue-light);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Toggle button */
|
||||
.ai-chat-toggle {
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--chat-blue-accent);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 15px rgba(61, 132, 247, 0.35);
|
||||
z-index: 9998;
|
||||
transition: var(--chat-transition);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.ai-chat-toggle:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 6px 20px rgba(61, 132, 247, 0.45);
|
||||
}
|
||||
|
||||
#ai-chat-assistant-container.visible + .ai-chat-toggle {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Code formatting */
|
||||
.ai-chat-message pre {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
overflow-x: auto;
|
||||
margin: 10px 0;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.ai-chat-message code {
|
||||
font-family: 'Cascadia Code', 'Fira Code', 'Source Code Pro', monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ai-chat-message.user code {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
padding: 2px 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.ai-chat-message.assistant code {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
padding: 2px 5px;
|
||||
border-radius: 3px;
|
||||
color: #e2e2e2;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
.ai-chat-message a {
|
||||
color: var(--chat-blue-accent);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted rgba(61, 132, 247, 0.5);
|
||||
transition: var(--chat-transition);
|
||||
}
|
||||
|
||||
.ai-chat-message a:hover {
|
||||
border-bottom: 1px solid var(--chat-blue-accent);
|
||||
}
|
||||
|
||||
.ai-chat-message.user a {
|
||||
color: white;
|
||||
border-bottom: 1px dotted rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.ai-chat-message.user a:hover {
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
#ai-chat-assistant-container {
|
||||
width: calc(100% - 20px);
|
||||
height: 60vh;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
.ai-chat-toggle {
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tab styles */
|
||||
.ai-chat-tabs {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.ai-chat-tab {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--chat-light-text);
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
transition: var(--chat-transition);
|
||||
}
|
||||
|
||||
.ai-chat-tab:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.ai-chat-tab.active {
|
||||
background-color: var(--chat-blue-accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Content area */
|
||||
.ai-chat-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Memories tab styles */
|
||||
.ai-chat-memories {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: var(--chat-dark-bg);
|
||||
}
|
||||
|
||||
.memories-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.memories-title {
|
||||
display: inline;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: var(--chat-light-text);
|
||||
}
|
||||
|
||||
.memories-title a {
|
||||
color: var(--chat-blue-accent);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: var(--chat-transition);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.memories-title a:hover {
|
||||
color: var(--chat-blue-hover);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.memories-title a svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.memories-title svg {
|
||||
vertical-align: middle;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.memories-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 10px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(255, 255, 255, 0.1) transparent;
|
||||
}
|
||||
|
||||
.memories-list::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.memories-list::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.memories-list::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.memory-item {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: var(--chat-message-radius);
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
color: var(--chat-light-text);
|
||||
}
|
||||
|
||||
.memory-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.loading, .no-memories, .error, .info {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
font-size: 14px;
|
||||
color: var(--chat-light-text);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--chat-error);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.info {
|
||||
color: var(--chat-blue-accent);
|
||||
}
|
||||
@@ -0,0 +1,587 @@
|
||||
:root {
|
||||
--dark-bg: #1a1a1a;
|
||||
--darker-bg: #121212;
|
||||
--section-bg: #202020;
|
||||
--light-text: #f1f1f1;
|
||||
--dim-text: rgba(255, 255, 255, 0.7);
|
||||
--dim-text-2: rgba(255, 255, 255, 0.5);
|
||||
--blue-accent: #3d84f7;
|
||||
--blue-hover: #2d74e7;
|
||||
--blue-light: rgba(61, 132, 247, 0.15);
|
||||
--error-color: #ff4a4a;
|
||||
--warning-color: #ffaa33;
|
||||
--success-color: #4caf50;
|
||||
--border-radius: 8px;
|
||||
--transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Roboto", -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px 20px 40px;
|
||||
color: var(--light-text);
|
||||
background-color: var(--dark-bg);
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header {
|
||||
max-width: 800px;
|
||||
padding-left: 28px;
|
||||
padding-top: 10px;
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
margin: 0 0 12px 0;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.title-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.logo-img {
|
||||
height: 20px;
|
||||
width: auto;
|
||||
margin-left: 8px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.powered-by {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.branding-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: var(--dim-text);
|
||||
margin-bottom: 20px;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 30px;
|
||||
background: var(--section-bg);
|
||||
padding: 28px;
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 15px;
|
||||
color: var(--light-text);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
h2::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 5px;
|
||||
height: 20px;
|
||||
background-color: var(--blue-accent);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
color: var(--light-text);
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="number"],
|
||||
select {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
color: var(--light-text);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="number"]:focus,
|
||||
select:focus {
|
||||
border-color: var(--blue-accent);
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px rgba(61, 132, 247, 0.2);
|
||||
}
|
||||
|
||||
select {
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%207l5%205%205-5%22%20stroke%3D%22%23fff%22%20stroke-width%3D%221.5%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 12px center;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked {
|
||||
background-color: var(--blue-accent);
|
||||
border-color: var(--blue-accent);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 2px;
|
||||
width: 6px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--light-text);
|
||||
}
|
||||
|
||||
.checkbox-label label {
|
||||
margin-bottom: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: var(--blue-accent);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 20px;
|
||||
border-radius: var(--border-radius);
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: var(--transition);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: var(--blue-hover);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(1px);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
color: var(--dim-text-2);
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 15px;
|
||||
border-radius: var(--border-radius);
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
animation: fade-in 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.status.error {
|
||||
background-color: rgba(255, 74, 74, 0.1);
|
||||
color: var(--error-color);
|
||||
border: 1px solid rgba(255, 74, 74, 0.2);
|
||||
}
|
||||
|
||||
.status.success {
|
||||
background-color: rgba(76, 175, 80, 0.1);
|
||||
color: var(--success-color);
|
||||
border: 1px solid rgba(76, 175, 80, 0.2);
|
||||
}
|
||||
|
||||
.status.warning {
|
||||
background-color: rgba(255, 170, 51, 0.1);
|
||||
color: var(--warning-color);
|
||||
border: 1px solid rgba(255, 170, 51, 0.2);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.secondary-button {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
color: var(--light-text);
|
||||
}
|
||||
|
||||
.secondary-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.api-key-container {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.api-key-container input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Slider styles */
|
||||
.slider-container {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.slider {
|
||||
-webkit-appearance: none;
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: var(--blue-accent);
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-thumb:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: var(--blue-accent);
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
|
||||
transition: var(--transition);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
/* Add styles for memory creation section */
|
||||
.memory-input {
|
||||
width: 100%;
|
||||
min-height: 150px;
|
||||
padding: 12px;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
color: var(--light-text);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
transition: var(--transition);
|
||||
resize: vertical;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.memory-input:focus {
|
||||
border-color: var(--blue-accent);
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px rgba(61, 132, 247, 0.2);
|
||||
}
|
||||
|
||||
.memory-result {
|
||||
margin-top: 15px;
|
||||
padding: 12px;
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 14px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.memory-result.success {
|
||||
background-color: rgba(76, 175, 80, 0.1);
|
||||
color: var(--success-color);
|
||||
border: 1px solid rgba(76, 175, 80, 0.2);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.memory-result.error {
|
||||
background-color: rgba(255, 74, 74, 0.1);
|
||||
color: var(--error-color);
|
||||
border: 1px solid rgba(255, 74, 74, 0.2);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: var(--light-text);
|
||||
animation: spin 1s linear infinite;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add new styles for the memories sidebar */
|
||||
.memories-sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 384px;
|
||||
height: 100vh;
|
||||
background: var(--section-bg);
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.05);
|
||||
transition: transform 0.3s ease;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.memories-sidebar.collapsed {
|
||||
transform: translateX(384px);
|
||||
}
|
||||
|
||||
.memories-header {
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.memories-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: var(--light-text);
|
||||
}
|
||||
|
||||
.memories-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.memories-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.memory-item {
|
||||
padding: 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: var(--border-radius);
|
||||
margin-bottom: 12px;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.memory-item:hover {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.memory-content {
|
||||
font-size: 14px;
|
||||
color: var(--light-text);
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
text-wrap-style: pretty;
|
||||
}
|
||||
|
||||
.memory-item .memory-content {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.memory-meta {
|
||||
font-size: 12px;
|
||||
color: var(--dim-text);
|
||||
}
|
||||
|
||||
.memory-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.memory-action-btn {
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
border-radius: 6px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: var(--light-text);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.memory-action-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.memory-action-btn.delete:hover {
|
||||
background-color: var(--error-color);
|
||||
}
|
||||
|
||||
.edit-memory-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1100;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.edit-memory-modal.open {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.edit-memory-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--section-bg);
|
||||
padding: 24px;
|
||||
border-radius: var(--border-radius);
|
||||
width: 90%;
|
||||
max-width: 600px;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.edit-memory-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.edit-memory-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: var(--light-text);
|
||||
}
|
||||
|
||||
.edit-memory-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--dim-text);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
font-size: 20px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.edit-memory-textarea {
|
||||
min-height: 20px;
|
||||
max-height: 70px;
|
||||
padding: 12px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--light-text);
|
||||
font-family: inherit;
|
||||
margin-bottom: 16px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.edit-memory-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-right: 400px;
|
||||
transition: margin-right 0.3s ease;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.main-content.sidebar-collapsed {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#status-container {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
:root {
|
||||
--dark-bg: #1a1a1a;
|
||||
--darker-bg: #121212;
|
||||
--light-text: #f1f1f1;
|
||||
--blue-accent: #3d84f7;
|
||||
--blue-hover: #2d74e7;
|
||||
--blue-light: rgba(61, 132, 247, 0.15);
|
||||
--error-color: #ff4a4a;
|
||||
--warning-color: #ffaa33;
|
||||
--success-color: #4caf50;
|
||||
--border-radius: 8px;
|
||||
--transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Roboto", -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
width: 320px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: var(--light-text);
|
||||
background-color: var(--dark-bg);
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: var(--darker-bg);
|
||||
color: var(--light-text);
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 18px;
|
||||
margin: 0 0 8px 0;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.logo-img {
|
||||
height: 16px;
|
||||
width: auto;
|
||||
margin-left: 8px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.powered-by {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.branding-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 12px;
|
||||
border-radius: var(--border-radius);
|
||||
margin-bottom: 16px;
|
||||
font-size: 14px;
|
||||
animation: fade-in 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.status.error {
|
||||
background-color: rgba(255, 74, 74, 0.1);
|
||||
color: var(--error-color);
|
||||
border: 1px solid rgba(255, 74, 74, 0.2);
|
||||
}
|
||||
|
||||
.status.success {
|
||||
background-color: rgba(76, 175, 80, 0.1);
|
||||
color: var(--success-color);
|
||||
border: 1px solid rgba(76, 175, 80, 0.2);
|
||||
}
|
||||
|
||||
.status.warning {
|
||||
background-color: rgba(255, 170, 51, 0.1);
|
||||
color: var(--warning-color);
|
||||
border: 1px solid rgba(255, 170, 51, 0.2);
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: var(--blue-accent);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: var(--transition);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: var(--blue-hover);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.api-key-section {
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.api-key-input-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toggle-password {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
width: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.toggle-password:hover {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
background: none;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.toggle-password .icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"] {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
padding-right: 40px;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
color: var(--light-text);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: var(--border-radius);
|
||||
margin-top: 6px;
|
||||
box-sizing: border-box;
|
||||
transition: var(--transition);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
input[type="password"]:focus {
|
||||
border-color: var(--blue-accent);
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px rgba(61, 132, 247, 0.2);
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.save-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.mem0-status {
|
||||
margin-top: 20px;
|
||||
padding: 12px;
|
||||
background-color: rgba(255, 255, 255, 0.03);
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 13px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.mem0-status p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#mem0-status-text {
|
||||
color: var(--blue-accent);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.get-key-link {
|
||||
color: var(--blue-accent);
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.get-key-link:hover {
|
||||
color: var(--blue-accent-hover);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.get-key-link:visited {
|
||||
color: var(--blue-accent);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
entry: {
|
||||
content: './src/content.js',
|
||||
options: './src/options.js',
|
||||
popup: './src/popup.js',
|
||||
background: './src/background.js'
|
||||
},
|
||||
output: {
|
||||
filename: '[name].bundle.js',
|
||||
path: path.resolve(__dirname, 'dist')
|
||||
},
|
||||
devtool: 'source-map',
|
||||
optimization: {
|
||||
minimize: false
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-env']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ['style-loader', 'css-loader']
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js']
|
||||
}
|
||||
};
|
||||
+20
-20
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mem0ai",
|
||||
"version": "2.1.8",
|
||||
"version": "2.1.17",
|
||||
"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,26 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "1.7.7",
|
||||
"neo4j-driver": "^5.28.1",
|
||||
"openai": "4.28.0",
|
||||
"openai": "^4.93.0",
|
||||
"uuid": "9.0.1",
|
||||
"zod": "3.22.4"
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@anthropic-ai/sdk": "0.18.0",
|
||||
"@google/genai": "^0.7.0",
|
||||
"@mistralai/mistralai": "^1.5.2",
|
||||
"@qdrant/js-client-rest": "1.13.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",
|
||||
"@langchain/core": "^0.3.44",
|
||||
"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"
|
||||
"redis": "^4.6.13",
|
||||
"sqlite3": "5.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
Generated
+7978
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.16";
|
||||
|
||||
// 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,78 @@
|
||||
import dotenv from "dotenv";
|
||||
import { MistralLLM } from "../../src/llms/mistral";
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
async function testMistral() {
|
||||
// Check for API key
|
||||
if (!process.env.MISTRAL_API_KEY) {
|
||||
console.error("MISTRAL_API_KEY environment variable is required");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("Testing Mistral LLM implementation...");
|
||||
|
||||
// Initialize MistralLLM
|
||||
const mistral = new MistralLLM({
|
||||
apiKey: process.env.MISTRAL_API_KEY,
|
||||
model: "mistral-tiny-latest", // You can change to other models like mistral-small-latest
|
||||
});
|
||||
|
||||
try {
|
||||
// Test simple chat completion
|
||||
console.log("Testing simple chat completion:");
|
||||
const chatResponse = await mistral.generateChat([
|
||||
{ role: "system", content: "You are a helpful assistant." },
|
||||
{ role: "user", content: "What is the capital of France?" },
|
||||
]);
|
||||
|
||||
console.log("Chat response:");
|
||||
console.log(`Role: ${chatResponse.role}`);
|
||||
console.log(`Content: ${chatResponse.content}\n`);
|
||||
|
||||
// Test with functions/tools
|
||||
console.log("Testing tool calling:");
|
||||
const tools = [
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "get_weather",
|
||||
description: "Get the current weather in a given location",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
location: {
|
||||
type: "string",
|
||||
description: "The city and state, e.g. San Francisco, CA",
|
||||
},
|
||||
unit: {
|
||||
type: "string",
|
||||
enum: ["celsius", "fahrenheit"],
|
||||
description: "The unit of temperature",
|
||||
},
|
||||
},
|
||||
required: ["location"],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const toolResponse = await mistral.generateResponse(
|
||||
[
|
||||
{ role: "system", content: "You are a helpful assistant." },
|
||||
{ role: "user", content: "What's the weather like in Paris, France?" },
|
||||
],
|
||||
undefined,
|
||||
tools,
|
||||
);
|
||||
|
||||
console.log("Tool response:", toolResponse);
|
||||
|
||||
console.log("\n✅ All tests completed successfully");
|
||||
} catch (error) {
|
||||
console.error("Error testing Mistral LLM:", error);
|
||||
}
|
||||
}
|
||||
|
||||
testMistral().catch(console.error);
|
||||
@@ -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);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user