Skip to content

Commit 57c093d

Browse files
authored
Merge pull request #34 from redis/fix/fix-docker-compose
Fix docker-compose file
2 parents 95ca854 + a1fc7a5 commit 57c093d

File tree

15 files changed

+313
-80
lines changed

15 files changed

+313
-80
lines changed

.github/workflows/python-tests.yml

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,15 @@ jobs:
3434
strategy:
3535
fail-fast: false
3636
matrix:
37-
python-version: [3.12] # Not testing with 3.13 at the moment
38-
redis-version: ['6.2.6-v9', 'latest'] # 8.0-M03 is not working atm
37+
python-version: [3.12]
38+
redis-version: ['redis/redis-stack:6.2.6-v9', 'redis:8.0.3', 'redis:latest']
3939

4040
steps:
4141
- uses: actions/checkout@v3
4242

4343
- name: Set Redis image name
4444
run: |
45-
if [[ "${{ matrix.redis-version }}" == "8.0-M03" ]]; then
46-
echo "REDIS_IMAGE=redis:${{ matrix.redis-version }}" >> $GITHUB_ENV
47-
else
48-
echo "REDIS_IMAGE=redis/redis-stack-server:${{ matrix.redis-version }}" >> $GITHUB_ENV
49-
fi
45+
echo "REDIS_IMAGE=${{ matrix.redis-version }}" >> $GITHUB_ENV
5046
5147
- name: Set up Python
5248
uses: actions/setup-python@v4
@@ -81,6 +77,12 @@ jobs:
8177
- name: Set up Docker Buildx
8278
uses: docker/setup-buildx-action@v3
8379

80+
- name: Log in to Docker Hub
81+
uses: docker/login-action@v3
82+
with:
83+
username: ${{ secrets.DOCKER_USERNAME }}
84+
password: ${{ secrets.DOCKER_TOKEN }}
85+
8486
- name: Log in to GitHub Container Registry
8587
uses: docker/login-action@v3
8688
with:
@@ -103,6 +105,8 @@ jobs:
103105
platforms: linux/amd64,linux/arm64
104106
push: true
105107
tags: |
108+
andrewbrookins510/agent-memory-server:latest
109+
andrewbrookins510/agent-memory-server:${{ steps.version.outputs.version }}
106110
ghcr.io/${{ github.repository }}:latest
107111
ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version }}
108112
cache-from: type=gha

agent-memory-client/agent_memory_client/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
memory management capabilities for AI agents and applications.
66
"""
77

8-
__version__ = "0.9.0"
8+
__version__ = "0.9.1"
99

1010
from .client import MemoryAPIClient, MemoryClientConfig, create_memory_client
1111
from .exceptions import (

agent-memory-client/agent_memory_client/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ class WorkingMemory(BaseModel):
183183
)
184184

185185
# TTL and timestamps
186-
ttl_seconds: int = Field(
187-
default=3600, # 1 hour default
186+
ttl_seconds: int | None = Field(
187+
default=None, # Persistent by default
188188
description="TTL for the working memory in seconds",
189189
)
190190
last_accessed: datetime = Field(

agent_memory_server/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Redis Agent Memory Server - A memory system for conversational AI."""
22

3-
__version__ = "0.9.0"
3+
__version__ = "0.9.1"

agent_memory_server/mcp.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,14 @@ async def run_sse_async(self):
137137

138138
redis = await get_redis_conn()
139139
await ensure_search_index_exists(redis)
140-
return await super().run_sse_async()
140+
141+
# Run the SSE server using our custom implementation
142+
import uvicorn
143+
144+
app = self.sse_app()
145+
await uvicorn.Server(
146+
uvicorn.Config(app, host="0.0.0.0", port=int(self.settings.port))
147+
).serve()
141148

142149
async def run_stdio_async(self):
143150
"""Ensure Redis search index exists before starting STDIO MCP server."""

agent_memory_server/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,8 @@ class WorkingMemory(BaseModel):
201201
)
202202

203203
# TTL and timestamps
204-
ttl_seconds: int = Field(
205-
default=3600, # 1 hour default
204+
ttl_seconds: int | None = Field(
205+
default=None, # Persistent by default
206206
description="TTL for the working memory in seconds",
207207
)
208208
last_accessed: datetime = Field(

agent_memory_server/working_memory.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ async def get_working_memory(
121121
tokens=working_memory_data.get("tokens", 0),
122122
session_id=session_id,
123123
namespace=namespace,
124-
ttl_seconds=working_memory_data.get("ttl_seconds", 3600),
124+
ttl_seconds=working_memory_data.get("ttl_seconds", None),
125125
data=working_memory_data.get("data") or {},
126126
last_accessed=datetime.fromtimestamp(
127127
working_memory_data.get("last_accessed", int(time.time())), UTC
@@ -188,18 +188,24 @@ async def set_working_memory(
188188
}
189189

190190
try:
191-
# Store with TTL
192-
await redis_client.setex(
193-
key,
194-
working_memory.ttl_seconds,
195-
json.dumps(
196-
data, default=json_datetime_handler
197-
), # Add custom handler for any remaining datetime objects
198-
)
199-
logger.info(
200-
f"Set working memory for session {working_memory.session_id} with TTL {working_memory.ttl_seconds}s"
201-
)
202-
191+
if working_memory.ttl_seconds is not None:
192+
# Store with TTL
193+
await redis_client.setex(
194+
key,
195+
working_memory.ttl_seconds,
196+
json.dumps(data, default=json_datetime_handler),
197+
)
198+
logger.info(
199+
f"Set working memory for session {working_memory.session_id} with TTL {working_memory.ttl_seconds}s"
200+
)
201+
else:
202+
await redis_client.set(
203+
key,
204+
json.dumps(data, default=json_datetime_handler),
205+
)
206+
logger.info(
207+
f"Set working memory for session {working_memory.session_id} with no TTL"
208+
)
203209
except Exception as e:
204210
logger.error(
205211
f"Error setting working memory for session {working_memory.session_id}: {e}"

docker-compose.yml

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ services:
3434
dockerfile: Dockerfile
3535
environment:
3636
- REDIS_URL=redis://redis:6379
37-
- PORT=9000
37+
- PORT=9050
3838
# Add your API keys here or use a .env file
3939
- OPENAI_API_KEY=${OPENAI_API_KEY}
4040
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
@@ -46,19 +46,41 @@ services:
4646
- ENABLE_TOPIC_EXTRACTION=True
4747
- ENABLE_NER=True
4848
ports:
49-
- "9000:9000"
49+
- "9050:9000"
5050
depends_on:
5151
- redis
5252
command: ["uv", "run", "agent-memory", "mcp", "--mode", "sse"]
5353

54+
task-worker:
55+
build:
56+
context: .
57+
dockerfile: Dockerfile
58+
environment:
59+
- REDIS_URL=redis://redis:6379
60+
# Add your API keys here or use a .env file
61+
- OPENAI_API_KEY=${OPENAI_API_KEY}
62+
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
63+
# Optional configurations with defaults
64+
- LONG_TERM_MEMORY=True
65+
- WINDOW_SIZE=20
66+
- GENERATION_MODEL=gpt-4o-mini
67+
- EMBEDDING_MODEL=text-embedding-3-small
68+
- ENABLE_TOPIC_EXTRACTION=True
69+
- ENABLE_NER=True
70+
depends_on:
71+
- redis
72+
command: ["uv", "run", "agent-memory", "task-worker"]
73+
volumes:
74+
- ./agent_memory_server:/app/agent_memory_server
75+
restart: unless-stopped
76+
5477
redis:
55-
image: redis/redis-stack:latest
78+
image: redis:8
5679
ports:
57-
- "16379:6379" # Redis port
58-
- "18001:8001" # RedisInsight port
80+
- "16380:6379" # Redis port
5981
volumes:
6082
- redis_data:/data
61-
command: redis-stack-server --save 60 1 --loglevel warning
83+
command: redis-server --save "" --loglevel warning --appendonly no --stop-writes-on-bgsave-error no
6284
healthcheck:
6385
test: [ "CMD", "redis-cli", "ping" ]
6486
interval: 30s

examples/memory_prompt_agent.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,13 @@
3434

3535

3636
# Configure logging
37-
logging.basicConfig(level=logging.INFO)
37+
logging.basicConfig(level=logging.WARNING)
3838
logger = logging.getLogger(__name__)
3939

40+
# Reduce third-party logging
41+
logging.getLogger("httpx").setLevel(logging.WARNING)
42+
logging.getLogger("openai").setLevel(logging.WARNING)
43+
4044
# Environment setup
4145
MEMORY_SERVER_URL = os.getenv("MEMORY_SERVER_URL", "http://localhost:8000")
4246
DEFAULT_USER = "demo_user"
@@ -96,7 +100,6 @@ async def cleanup(self):
96100
"""Clean up resources."""
97101
if self._memory_client:
98102
await self._memory_client.close()
99-
logger.info("Memory client closed")
100103

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

148-
logger.info(f"Total messages for LLM: {len(messages)}")
149-
150151
# Generate response
151152
response = self.llm.invoke(messages)
152153
return str(response.content)

examples/travel_agent.py

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import json
2929
import logging
3030
import os
31-
import textwrap
3231

3332
from agent_memory_client import (
3433
MemoryAPIClient,
@@ -37,16 +36,26 @@
3736
from agent_memory_client.models import (
3837
WorkingMemory,
3938
)
40-
from langchain_community.tools.tavily_search import TavilySearchResults
4139
from langchain_core.callbacks.manager import CallbackManagerForToolRun
4240
from langchain_openai import ChatOpenAI
4341
from redis import Redis
4442

4543

44+
try:
45+
from langchain_community.tools.tavily_search import TavilySearchResults
46+
except ImportError as e:
47+
raise ImportError("Please install langchain-community for this demo.") from e
48+
49+
4650
# Configure logging
47-
logging.basicConfig(level=logging.INFO)
51+
logging.basicConfig(level=logging.WARNING)
4852
logger = logging.getLogger(__name__)
4953

54+
# Reduce third-party logging
55+
logging.getLogger("httpx").setLevel(logging.WARNING)
56+
logging.getLogger("openai").setLevel(logging.WARNING)
57+
58+
5059
# Environment setup
5160
MEMORY_SERVER_URL = os.getenv("MEMORY_SERVER_URL", "http://localhost:8000")
5261
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")
@@ -56,35 +65,35 @@
5665

5766
SYSTEM_PROMPT = {
5867
"role": "system",
59-
"content": textwrap.dedent("""
60-
You are a helpful travel assistant. You can help with travel-related questions.
61-
You have access to conversation history and memory management tools to provide
62-
personalized responses.
63-
64-
Available tools:
65-
66-
1. **web_search** (if available): Search for current travel information, weather,
67-
events, or other up-to-date data when specifically needed.
68-
69-
2. **Memory Management Tools** (always available):
70-
- **search_memory**: Look up previous conversations and stored information
71-
- **get_working_memory**: Check current session context
72-
- **add_memory_to_working_memory**: Store important preferences or information
73-
- **update_working_memory_data**: Save session-specific data
74-
75-
**Guidelines**:
76-
- Answer the user's actual question first and directly
77-
- When someone shares information (like "I like X"), simply acknowledge it naturally - don't immediately give advice or suggestions unless they ask
78-
- Search memory or web when it would be helpful for the current conversation
79-
- Don't assume the user is actively planning a trip unless they explicitly say so
80-
- Be conversational and natural - respond to what the user actually says
81-
- When sharing memories, simply state what you remember rather than turning it into advice
82-
- Only offer suggestions, recommendations, or tips if the user explicitly asks for them
83-
- Store preferences and important details, but don't be overly eager about it
84-
- If someone shares a preference, respond like a friend would - acknowledge it, maybe ask a follow-up question, but don't launch into advice
85-
86-
Be helpful, friendly, and responsive. Mirror their conversational style - if they're just chatting, chat back. If they ask for help, then help.
87-
"""),
68+
"content": """
69+
You are a helpful travel assistant. You can help with travel-related questions.
70+
You have access to conversation history and memory management tools to provide
71+
personalized responses.
72+
73+
Available tools:
74+
75+
1. **web_search** (if available): Search for current travel information, weather,
76+
events, or other up-to-date data when specifically needed.
77+
78+
2. **Memory Management Tools** (always available):
79+
- **search_memory**: Look up previous conversations and stored information
80+
- **get_working_memory**: Check current session context
81+
- **add_memory_to_working_memory**: Store important preferences or information
82+
- **update_working_memory_data**: Save session-specific data
83+
84+
**Guidelines**:
85+
- Answer the user's actual question first and directly
86+
- When someone shares information (like "I like X"), simply acknowledge it naturally - don't immediately give advice or suggestions unless they ask
87+
- Search memory or web when it would be helpful for the current conversation
88+
- Don't assume the user is actively planning a trip unless they explicitly say so
89+
- Be conversational and natural - respond to what the user actually says
90+
- When sharing memories, simply state what you remember rather than turning it into advice
91+
- Only offer suggestions, recommendations, or tips if the user explicitly asks for them
92+
- Store preferences and important details, but don't be overly eager about it
93+
- If someone shares a preference, respond like a friend would - acknowledge it, maybe ask a follow-up question, but don't launch into advice
94+
95+
Be helpful, friendly, and responsive. Mirror their conversational style - if they're just chatting, chat back. If they ask for help, then help.
96+
""",
8897
}
8998

9099

@@ -151,12 +160,12 @@ def _setup_llms(self):
151160
# Define the web search tool function
152161
web_search_function = {
153162
"name": "web_search",
154-
"description": textwrap.dedent("""
163+
"description": """
155164
Search the web for current information about travel destinations,
156165
requirements, weather, events, or any other travel-related
157166
queries. Use this when you need up-to-date information that may
158167
not be in your training data.
159-
"""),
168+
""",
160169
"parameters": {
161170
"type": "object",
162171
"properties": {

0 commit comments

Comments
 (0)