Compare commits

..
6 Commits
28 changed files with 1464 additions and 39 deletions
+1
View File
@@ -84,6 +84,7 @@ Here's a comprehensive list of all parameters that can be used across different
| `memory_add_embedding_type` | The type of embedding to use for the add memory action | VertexAI |
| `memory_update_embedding_type` | The type of embedding to use for the update memory action | VertexAI |
| `memory_search_embedding_type` | The type of embedding to use for the search memory action | VertexAI |
| `lmstudio_base_url` | Base URL for LM Studio API | LM Studio |
</Tab>
<Tab title="TypeScript">
| Parameter | Description | Provider |
@@ -0,0 +1,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` |
+1
View File
@@ -22,6 +22,7 @@ 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>
</CardGroup>
## Usage
+1
View File
@@ -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 |
+82
View File
@@ -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).
+1
View File
@@ -32,6 +32,7 @@ To view all supported llms, visit the [Supported LLMs](./models).
<Card title="Gemini" href="/components/llms/models/gemini" />
<Card title="DeepSeek" href="/components/llms/models/deepseek" />
<Card title="xAI" href="/components/llms/models/xAI" />
<Card title="LM Studio" href="/components/llms/models/lmstudio" />
</CardGroup>
## Structured vs Unstructured Outputs
+2 -1
View File
@@ -193,7 +193,8 @@
"examples/multimodal-demo",
"examples/personalized-deep-research",
"examples/mem0-agentic-tool",
"examples/openai-inbuilt-tools"
"examples/openai-inbuilt-tools",
"examples/mem0-openai-voice-demo"
]
}
]
+538
View File
@@ -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...
```
+4
View File
@@ -66,4 +66,8 @@ 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="robot" href="/examples/mem0-openai-voice-demo">
Use Mem0's memory capabilities with OpenAI's Inbuilt Tools to create AI agents with persistent memory.
</Card>
</CardGroup>
+35 -1
View File
@@ -783,6 +783,13 @@
"title": "Immutable",
"default": false
},
"expiration_date": {
"type": "string",
"format": "date-time",
"description": "The date and time when the memory will expire. The default expiration date is 30 days from the date of creation. Format: YYYY-MM-DD",
"title": "Expiration date",
"nullable": true
},
"organization": {
"type": "string"
},
@@ -1183,6 +1190,13 @@
"title": "Immutable",
"default": false
},
"expiration_date": {
"type": "string",
"format": "date-time",
"description": "The date and time when the memory will expire. The default expiration date is 30 days from the date of creation. Format: YYYY-MM-DD",
"title": "Expiration date",
"nullable": true
},
"organization": {
"type": "string"
},
@@ -1324,6 +1338,13 @@
"title": "Immutable",
"default": false
},
"expiration_date": {
"type": "string",
"format": "date-time",
"description": "The date and time when the memory will expire. The default expiration date is 30 days from the date of creation. Format: YYYY-MM-DD",
"title": "Expiration date",
"nullable": true
},
"created_at": {
"type": "string",
"format": "date-time",
@@ -1451,6 +1472,13 @@
"title": "Immutable",
"default": false
},
"expiration_date": {
"type": "string",
"format": "date-time",
"description": "The date and time when the memory will expire. The default expiration date is 30 days from the date of creation. Format: YYYY-MM-DD",
"title": "Expiration date",
"nullable": true
},
"created_at": {
"type": "string",
"format": "date-time",
@@ -1761,7 +1789,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",
@@ -4798,6 +4826,12 @@
"type": "boolean",
"default": false
},
"expiration_date": {
"description": "The date and time when the memory will expire. The default expiration date is 30 days from the date of creation. Format: YYYY-MM-DD",
"title": "Expiration date",
"type": "string",
"nullable": true
},
"org_id": {
"description": "The unique identifier of the organization associated with this memory.",
"title": "Organization id",
+122 -35
View File
@@ -416,6 +416,89 @@ curl -X POST "https://api.mem0.ai/v1/memories/" \
</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
[
{
"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
@@ -470,6 +553,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"
}
@@ -486,6 +570,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"
}
@@ -531,6 +616,7 @@ curl -X POST "https://api.mem0.ai/v1/memories/search/?version=v2" \
"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"
}
@@ -621,6 +707,7 @@ curl -X POST "https://api.mem0.ai/v1/memories/search/?version=v2" \
"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"
}
@@ -686,6 +773,7 @@ curl -X POST "https://api.mem0.ai/v1/memories/search/?version=v2" \
"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"
}
@@ -760,6 +848,7 @@ curl -X POST "https://api.mem0.ai/v1/memories/search/?version=v2" \
"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"
}
@@ -860,6 +949,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
@@ -871,6 +961,7 @@ 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
@@ -894,6 +985,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,6 +997,7 @@ 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
@@ -949,6 +1042,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",
"categories":None
@@ -960,6 +1054,7 @@ 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",
"categories":None
@@ -983,6 +1078,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,6 +1089,7 @@ 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"
}
@@ -1035,6 +1132,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":None
@@ -1046,6 +1144,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":None
@@ -1057,6 +1156,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:51:09.642275-07:00",
"updated_at":"2024-07-26T00:51:09.642295-07:00",
"categories":None
@@ -1080,6 +1180,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 +1192,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,6 +1204,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:51:09.642275-07:00",
"updated_at": "2024-07-26T00:51:09.642295-07:00",
"categories": None
@@ -1141,6 +1244,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 +1325,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 +1337,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 +1465,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 +1486,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 +1576,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 +1907,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",
+7 -1
View File
@@ -56,7 +56,13 @@
"sourcemap": true,
"clean": true,
"treeshake": true,
"minify": false
"minify": false,
"external": [
"@mem0/community"
],
"noExternal": [
"!src/community/**"
]
},
"keywords": [
"mem0",
+28
View File
@@ -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*
+91
View File
@@ -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"
}
}
+1
View File
@@ -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();
}
}
+21
View File
@@ -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"]
}
+2
View File
@@ -3,11 +3,13 @@ export * from "./memory/memory.types";
export * from "./types";
export * from "./embeddings/base";
export * from "./embeddings/openai";
export * from "./embeddings/ollama";
export * from "./llms/base";
export * from "./llms/openai";
export * from "./llms/openai_structured";
export * from "./llms/anthropic";
export * from "./llms/groq";
export * from "./llms/ollama";
export * from "./vector_stores/base";
export * from "./vector_stores/memory";
export * from "./vector_stores/qdrant";
+7
View File
@@ -30,6 +30,8 @@ class BaseEmbedderConfig(ABC):
memory_add_embedding_type: Optional[str] = None,
memory_update_embedding_type: Optional[str] = None,
memory_search_embedding_type: Optional[str] = None,
# LM Studio specific
lmstudio_base_url: Optional[str] = "http://localhost:1234/v1",
):
"""
Initializes a configuration class instance for the Embeddings.
@@ -58,6 +60,8 @@ class BaseEmbedderConfig(ABC):
:type memory_update_embedding_type: Optional[str], optional
:param memory_search_embedding_type: The type of embedding to use for the search memory action, defaults to None
:type memory_search_embedding_type: Optional[str], optional
:param lmstudio_base_url: LM Studio base URL to be use, defaults to "http://localhost:1234/v1"
:type lmstudio_base_url: Optional[str], optional
"""
self.model = model
@@ -82,3 +86,6 @@ class BaseEmbedderConfig(ABC):
self.memory_add_embedding_type = memory_add_embedding_type
self.memory_update_embedding_type = memory_update_embedding_type
self.memory_search_embedding_type = memory_search_embedding_type
# LM Studio specific
self.lmstudio_base_url = lmstudio_base_url
+7
View File
@@ -39,6 +39,8 @@ class BaseLlmConfig(ABC):
deepseek_base_url: Optional[str] = None,
# XAI specific
xai_base_url: Optional[str] = None,
# LM Studio specific
lmstudio_base_url: Optional[str] = "http://localhost:1234/v1",
):
"""
Initializes a configuration class instance for the LLM.
@@ -83,6 +85,8 @@ class BaseLlmConfig(ABC):
:type deepseek_base_url: Optional[str], optional
:param xai_base_url: XAI base URL to be use, defaults to None
:type xai_base_url: Optional[str], optional
:param lmstudio_base_url: LM Studio base URL to be use, defaults to "http://localhost:1234/v1"
:type lmstudio_base_url: Optional[str], optional
"""
self.model = model
@@ -116,3 +120,6 @@ class BaseLlmConfig(ABC):
# XAI specific
self.xai_base_url = xai_base_url
# LM Studio specific
self.lmstudio_base_url = lmstudio_base_url
+1 -1
View File
@@ -13,7 +13,7 @@ class EmbedderConfig(BaseModel):
@field_validator("config")
def validate_config(cls, v, values):
provider = values.data.get("provider")
if provider in ["openai", "ollama", "huggingface", "azure_openai", "gemini", "vertexai", "together"]:
if provider in ["openai", "ollama", "huggingface", "azure_openai", "gemini", "vertexai", "together", "lmstudio"]:
return v
else:
raise ValueError(f"Unsupported embedding provider: {provider}")
+33
View File
@@ -0,0 +1,33 @@
from typing import Literal, Optional
from openai import OpenAI
from mem0.configs.embeddings.base import BaseEmbedderConfig
from mem0.embeddings.base import EmbeddingBase
class LMStudioEmbedding(EmbeddingBase):
def __init__(self, config: Optional[BaseEmbedderConfig] = None):
super().__init__(config)
self.config.model = self.config.model or "nomic-ai/nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf"
self.config.embedding_dims = self.config.embedding_dims or 1536
self.config.api_key = self.config.api_key or "lm-studio"
self.client = OpenAI(base_url=self.config.lmstudio_base_url, api_key=self.config.api_key)
def embed(self, text, memory_action: Optional[Literal["add", "search", "update"]] = None):
"""
Get the embedding for the given text using LM Studio.
Args:
text (str): The text to embed.
memory_action (optional): The type of embedding to use. Must be one of "add", "search", or "update". Defaults to None.
Returns:
list: The embedding vector.
"""
text = text.replace("\n", " ")
return (
self.client.embeddings.create(input=[text], model=self.config.model)
.data[0]
.embedding
)
+1
View File
@@ -24,6 +24,7 @@ class LlmConfig(BaseModel):
"gemini",
"deepseek",
"xai",
"lmstudio",
):
return v
else:
+48
View File
@@ -0,0 +1,48 @@
from typing import Dict, List, Optional
from openai import OpenAI
from mem0.configs.llms.base import BaseLlmConfig
from mem0.llms.base import LLMBase
class LMStudioLLM(LLMBase):
def __init__(self, config: Optional[BaseLlmConfig] = None):
super().__init__(config)
self.config.model = self.config.model or "lmstudio-community/Meta-Llama-3.1-70B-Instruct-GGUF/Meta-Llama-3.1-70B-Instruct-IQ2_M.gguf"
self.config.api_key = self.config.api_key or "lm-studio"
self.client = OpenAI(base_url=self.config.lmstudio_base_url, api_key=self.config.api_key)
def generate_response(
self,
messages: List[Dict[str, str]],
response_format: dict = {"type": "json_object"},
tools: Optional[List[Dict]] = None,
tool_choice: str = "auto"
):
"""
Generate a response based on the given messages using LM Studio.
Args:
messages (list): List of message dicts containing 'role' and 'content'.
response_format (str or object, optional): Format of the response. Defaults to "text".
tools (list, optional): List of tools that the model can call. Defaults to None.
tool_choice (str, optional): Tool choice method. Defaults to "auto".
Returns:
str: The generated response.
"""
params = {
"model": self.config.model,
"messages": messages,
"temperature": self.config.temperature,
"max_tokens": self.config.max_tokens,
"top_p": self.config.top_p
}
if response_format:
params["response_format"] = response_format
response = self.client.chat.completions.create(**params)
return response.choices[0].message.content
+2
View File
@@ -25,6 +25,7 @@ class LlmFactory:
"gemini": "mem0.llms.gemini.GeminiLLM",
"deepseek": "mem0.llms.deepseek.DeepSeekLLM",
"xai": "mem0.llms.xai.XAILLM",
"lmstudio": "mem0.llms.lmstudio.LMStudioLLM",
}
@classmethod
@@ -47,6 +48,7 @@ class EmbedderFactory:
"gemini": "mem0.embeddings.gemini.GoogleGenAIEmbedding",
"vertexai": "mem0.embeddings.vertexai.VertexAIEmbedding",
"together": "mem0.embeddings.together.TogetherEmbedding",
"lmstudio": "mem0.embeddings.lmstudio.LMStudioEmbedding",
}
@classmethod
@@ -0,0 +1,41 @@
import pytest
from unittest.mock import Mock, patch
from mem0.embeddings.lmstudio import LMStudioEmbedding
from mem0.configs.embeddings.base import BaseEmbedderConfig
@pytest.fixture
def mock_lm_studio_client():
with patch("mem0.embeddings.lmstudio.Client") as mock_lm_studio:
mock_client = Mock()
mock_client.list.return_value = {"models": [{"name": "nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf"}]}
mock_lm_studio.return_value = mock_client
yield mock_client
def test_embed_text(mock_lm_studio_client):
config = BaseEmbedderConfig(model="nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf", embedding_dims=512)
embedder = LMStudioEmbedding(config)
mock_response = {"embedding": [0.1, 0.2, 0.3, 0.4, 0.5]}
mock_lm_studio_client.embeddings.return_value = mock_response
text = "Sample text to embed."
embedding = embedder.embed(text)
mock_lm_studio_client.embeddings.assert_called_once_with(model="nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf", prompt=text)
assert embedding == [0.1, 0.2, 0.3, 0.4, 0.5]
def test_ensure_model_exists(mock_lm_studio_client):
config = BaseEmbedderConfig(model="nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.f16.gguf", embedding_dims=512)
embedder = LMStudioEmbedding(config)
mock_lm_studio_client.pull.assert_not_called()
mock_lm_studio_client.list.return_value = {"models": []}
embedder._ensure_model_exists()
mock_lm_studio_client.pull.assert_called_once_with("nomic-embed-text")
+34
View File
@@ -0,0 +1,34 @@
from unittest.mock import Mock, patch
import pytest
from mem0.configs.llms.base import BaseLlmConfig
from mem0.llms.lmstudio import LMStudioLLM
@pytest.fixture
def mock_lm_studio_client():
with patch("mem0.llms.lmstudio.Client") as mock_lm_studio:
mock_client = Mock()
mock_client.list.return_value = {"models": [{"name": "lmstudio-community/Meta-Llama-3.1-8B-Instruct-GGUF/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf"}]}
mock_lm_studio.return_value = mock_client
yield mock_client
def test_generate_response_without_tools(mock_lm_studio_client):
config = BaseLlmConfig(model="lmstudio-community/Meta-Llama-3.1-8B-Instruct-GGUF/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf", temperature=0.7, max_tokens=100, top_p=1.0)
llm = LMStudioLLM(config)
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello, how are you?"},
]
mock_response = {"message": {"content": "I'm doing well, thank you for asking!"}}
mock_lm_studio_client.chat.return_value = mock_response
response = llm.generate_response(messages)
mock_lm_studio_client.chat.assert_called_once_with(
model="lmstudio-community/Meta-Llama-3.1-8B-Instruct-GGUF/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf", messages=messages, options={"temperature": 0.7, "num_predict": 100, "top_p": 1.0}
)
assert response == "I'm doing well, thank you for asking!"