Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,15 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.12] # Not testing with 3.13 at the moment
redis-version: ['6.2.6-v9', 'latest'] # 8.0-M03 is not working atm
python-version: [3.12]
redis-version: ['redis/redis-stack:6.2.6-v9', 'redis:8.0.3', 'redis:latest']

steps:
- uses: actions/checkout@v3

- name: Set Redis image name
run: |
if [[ "${{ matrix.redis-version }}" == "8.0-M03" ]]; then
echo "REDIS_IMAGE=redis:${{ matrix.redis-version }}" >> $GITHUB_ENV
else
echo "REDIS_IMAGE=redis/redis-stack-server:${{ matrix.redis-version }}" >> $GITHUB_ENV
fi
echo "REDIS_IMAGE=${{ matrix.redis-version }}" >> $GITHUB_ENV

- name: Set up Python
uses: actions/setup-python@v4
Expand Down Expand Up @@ -81,6 +77,12 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
Expand All @@ -103,6 +105,8 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
tags: |
andrewbrookins510/agent-memory-server:latest
andrewbrookins510/agent-memory-server:${{ steps.version.outputs.version }}
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version }}
cache-from: type=gha
Expand Down
2 changes: 1 addition & 1 deletion agent-memory-client/agent_memory_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
memory management capabilities for AI agents and applications.
"""

__version__ = "0.9.0"
__version__ = "0.9.1"

from .client import MemoryAPIClient, MemoryClientConfig, create_memory_client
from .exceptions import (
Expand Down
4 changes: 2 additions & 2 deletions agent-memory-client/agent_memory_client/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ class WorkingMemory(BaseModel):
)

# TTL and timestamps
ttl_seconds: int = Field(
default=3600, # 1 hour default
ttl_seconds: int | None = Field(
default=None, # Persistent by default
description="TTL for the working memory in seconds",
)
last_accessed: datetime = Field(
Expand Down
2 changes: 1 addition & 1 deletion agent_memory_server/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Redis Agent Memory Server - A memory system for conversational AI."""

__version__ = "0.9.0"
__version__ = "0.9.1"
9 changes: 8 additions & 1 deletion agent_memory_server/mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,14 @@ async def run_sse_async(self):

redis = await get_redis_conn()
await ensure_search_index_exists(redis)
return await super().run_sse_async()

# Run the SSE server using our custom implementation
import uvicorn

app = self.sse_app()
await uvicorn.Server(
uvicorn.Config(app, host="0.0.0.0", port=int(self.settings.port))
).serve()

async def run_stdio_async(self):
"""Ensure Redis search index exists before starting STDIO MCP server."""
Expand Down
4 changes: 2 additions & 2 deletions agent_memory_server/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ class WorkingMemory(BaseModel):
)

# TTL and timestamps
ttl_seconds: int = Field(
default=3600, # 1 hour default
ttl_seconds: int | None = Field(
default=None, # Persistent by default
description="TTL for the working memory in seconds",
)
last_accessed: datetime = Field(
Expand Down
32 changes: 19 additions & 13 deletions agent_memory_server/working_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ async def get_working_memory(
tokens=working_memory_data.get("tokens", 0),
session_id=session_id,
namespace=namespace,
ttl_seconds=working_memory_data.get("ttl_seconds", 3600),
ttl_seconds=working_memory_data.get("ttl_seconds", None),
data=working_memory_data.get("data") or {},
last_accessed=datetime.fromtimestamp(
working_memory_data.get("last_accessed", int(time.time())), UTC
Expand Down Expand Up @@ -188,18 +188,24 @@ async def set_working_memory(
}

try:
# Store with TTL
await redis_client.setex(
key,
working_memory.ttl_seconds,
json.dumps(
data, default=json_datetime_handler
), # Add custom handler for any remaining datetime objects
)
logger.info(
f"Set working memory for session {working_memory.session_id} with TTL {working_memory.ttl_seconds}s"
)

if working_memory.ttl_seconds is not None:
# Store with TTL
await redis_client.setex(
key,
working_memory.ttl_seconds,
json.dumps(data, default=json_datetime_handler),
)
logger.info(
f"Set working memory for session {working_memory.session_id} with TTL {working_memory.ttl_seconds}s"
)
else:
await redis_client.set(
key,
json.dumps(data, default=json_datetime_handler),
)
logger.info(
f"Set working memory for session {working_memory.session_id} with no TTL"
)
except Exception as e:
logger.error(
f"Error setting working memory for session {working_memory.session_id}: {e}"
Expand Down
34 changes: 28 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ services:
dockerfile: Dockerfile
environment:
- REDIS_URL=redis://redis:6379
- PORT=9000
- PORT=9050
# Add your API keys here or use a .env file
- OPENAI_API_KEY=${OPENAI_API_KEY}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
Expand All @@ -46,19 +46,41 @@ services:
- ENABLE_TOPIC_EXTRACTION=True
- ENABLE_NER=True
ports:
- "9000:9000"
- "9050:9000"
depends_on:
- redis
command: ["uv", "run", "agent-memory", "mcp", "--mode", "sse"]

task-worker:
build:
context: .
dockerfile: Dockerfile
environment:
- REDIS_URL=redis://redis:6379
# Add your API keys here or use a .env file
- OPENAI_API_KEY=${OPENAI_API_KEY}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
# Optional configurations with defaults
- LONG_TERM_MEMORY=True
- WINDOW_SIZE=20
- GENERATION_MODEL=gpt-4o-mini
- EMBEDDING_MODEL=text-embedding-3-small
- ENABLE_TOPIC_EXTRACTION=True
- ENABLE_NER=True
depends_on:
- redis
command: ["uv", "run", "agent-memory", "task-worker"]
volumes:
- ./agent_memory_server:/app/agent_memory_server
restart: unless-stopped

redis:
image: redis/redis-stack:latest
image: redis:8
ports:
- "16379:6379" # Redis port
- "18001:8001" # RedisInsight port
- "16380:6379" # Redis port
volumes:
- redis_data:/data
command: redis-stack-server --save 60 1 --loglevel warning
command: redis-server --save "" --loglevel warning --appendonly no --stop-writes-on-bgsave-error no
healthcheck:
test: [ "CMD", "redis-cli", "ping" ]
interval: 30s
Expand Down
9 changes: 5 additions & 4 deletions examples/memory_prompt_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@


# Configure logging
logging.basicConfig(level=logging.INFO)
logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger(__name__)

# Reduce third-party logging
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("openai").setLevel(logging.WARNING)

# Environment setup
MEMORY_SERVER_URL = os.getenv("MEMORY_SERVER_URL", "http://localhost:8000")
DEFAULT_USER = "demo_user"
Expand Down Expand Up @@ -96,7 +100,6 @@ async def cleanup(self):
"""Clean up resources."""
if self._memory_client:
await self._memory_client.close()
logger.info("Memory client closed")

async def _add_message_to_working_memory(
self, session_id: str, user_id: str, role: str, content: str
Expand Down Expand Up @@ -145,8 +148,6 @@ async def _generate_response(
content = content["text"]
messages.append({"role": msg["role"], "content": str(content)})

logger.info(f"Total messages for LLM: {len(messages)}")

# Generate response
response = self.llm.invoke(messages)
return str(response.content)
Expand Down
77 changes: 43 additions & 34 deletions examples/travel_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import json
import logging
import os
import textwrap

from agent_memory_client import (
MemoryAPIClient,
Expand All @@ -37,16 +36,26 @@
from agent_memory_client.models import (
WorkingMemory,
)
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.callbacks.manager import CallbackManagerForToolRun
from langchain_openai import ChatOpenAI
from redis import Redis


try:
from langchain_community.tools.tavily_search import TavilySearchResults
except ImportError as e:
raise ImportError("Please install langchain-community for this demo.") from e


# Configure logging
logging.basicConfig(level=logging.INFO)
logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger(__name__)

# Reduce third-party logging
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("openai").setLevel(logging.WARNING)


# Environment setup
MEMORY_SERVER_URL = os.getenv("MEMORY_SERVER_URL", "http://localhost:8000")
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")
Expand All @@ -56,35 +65,35 @@

SYSTEM_PROMPT = {
"role": "system",
"content": textwrap.dedent("""
You are a helpful travel assistant. You can help with travel-related questions.
You have access to conversation history and memory management tools to provide
personalized responses.

Available tools:

1. **web_search** (if available): Search for current travel information, weather,
events, or other up-to-date data when specifically needed.

2. **Memory Management Tools** (always available):
- **search_memory**: Look up previous conversations and stored information
- **get_working_memory**: Check current session context
- **add_memory_to_working_memory**: Store important preferences or information
- **update_working_memory_data**: Save session-specific data

**Guidelines**:
- Answer the user's actual question first and directly
- When someone shares information (like "I like X"), simply acknowledge it naturally - don't immediately give advice or suggestions unless they ask
- Search memory or web when it would be helpful for the current conversation
- Don't assume the user is actively planning a trip unless they explicitly say so
- Be conversational and natural - respond to what the user actually says
- When sharing memories, simply state what you remember rather than turning it into advice
- Only offer suggestions, recommendations, or tips if the user explicitly asks for them
- Store preferences and important details, but don't be overly eager about it
- If someone shares a preference, respond like a friend would - acknowledge it, maybe ask a follow-up question, but don't launch into advice

Be helpful, friendly, and responsive. Mirror their conversational style - if they're just chatting, chat back. If they ask for help, then help.
"""),
"content": """
You are a helpful travel assistant. You can help with travel-related questions.
You have access to conversation history and memory management tools to provide
personalized responses.

Available tools:

1. **web_search** (if available): Search for current travel information, weather,
events, or other up-to-date data when specifically needed.

2. **Memory Management Tools** (always available):
- **search_memory**: Look up previous conversations and stored information
- **get_working_memory**: Check current session context
- **add_memory_to_working_memory**: Store important preferences or information
- **update_working_memory_data**: Save session-specific data

**Guidelines**:
- Answer the user's actual question first and directly
- When someone shares information (like "I like X"), simply acknowledge it naturally - don't immediately give advice or suggestions unless they ask
- Search memory or web when it would be helpful for the current conversation
- Don't assume the user is actively planning a trip unless they explicitly say so
- Be conversational and natural - respond to what the user actually says
- When sharing memories, simply state what you remember rather than turning it into advice
- Only offer suggestions, recommendations, or tips if the user explicitly asks for them
- Store preferences and important details, but don't be overly eager about it
- If someone shares a preference, respond like a friend would - acknowledge it, maybe ask a follow-up question, but don't launch into advice

Be helpful, friendly, and responsive. Mirror their conversational style - if they're just chatting, chat back. If they ask for help, then help.
""",
}


Expand Down Expand Up @@ -151,12 +160,12 @@ def _setup_llms(self):
# Define the web search tool function
web_search_function = {
"name": "web_search",
"description": textwrap.dedent("""
"description": """
Search the web for current information about travel destinations,
requirements, weather, events, or any other travel-related
queries. Use this when you need up-to-date information that may
not be in your training data.
"""),
""",
"parameters": {
"type": "object",
"properties": {
Expand Down
2 changes: 1 addition & 1 deletion manual_oauth_qa/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ ANTHROPIC_API_KEY=your-actual-anthropic-key
docker-compose up redis

# Or using Docker directly
docker run -d -p 6379:6379 redis/redis-stack-server:latest
docker run -d -p 6379:6379 redis:8.0.3
```

#### 3.2 Start Memory Server
Expand Down
12 changes: 6 additions & 6 deletions manual_oauth_qa/quick_auth0_setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,19 @@ fi

# Check if Redis is running
echo "🔍 Checking Redis connection..."
if redis-cli ping > /dev/null 2>&1; then
if redis-cli ping >/dev/null 2>&1; then
echo "✅ Redis is running"
else
echo "❌ Redis is not running. Starting Redis with Docker..."
if command -v docker > /dev/null 2>&1; then
docker run -d -p 6379:6379 --name redis-memory-test redis/redis-stack-server:latest
if command -v docker >/dev/null 2>&1; then
docker run -d -p 6379:6379 --name redis-memory-test redis:8.0.3
echo "✅ Started Redis container"
sleep 2
else
echo "❌ Docker not found. Please start Redis manually:"
echo " brew install redis && brew services start redis"
echo " OR"
echo " docker run -d -p 6379:6379 redis/redis-stack-server:latest"
echo " docker run -d -p 6379:6379 redis:8.0.3"
exit 1
fi
fi
Expand Down Expand Up @@ -77,8 +77,8 @@ echo "🔍 Testing Auth0 token endpoint..."
AUTH0_DOMAIN=$(echo $OAUTH2_ISSUER_URL | sed 's|https://||' | sed 's|/||')

TOKEN_RESPONSE=$(curl -s -X POST "https://$AUTH0_DOMAIN/oauth/token" \
-H "Content-Type: application/json" \
-d "{
-H "Content-Type: application/json" \
-d "{
\"client_id\": \"$AUTH0_CLIENT_ID\",
\"client_secret\": \"$AUTH0_CLIENT_SECRET\",
\"audience\": \"$OAUTH2_AUDIENCE\",
Expand Down
Loading