Skip to content

Commit f858a07

Browse files
authored
Merge pull request #60 from redis/feature/make-some-changes
Update memory documentation patterns to use working memory sessions
2 parents 00e7db4 + 31c1ecc commit f858a07

File tree

14 files changed

+412
-98
lines changed

14 files changed

+412
-98
lines changed

agent-memory-client/agent_memory_client/client.py

Lines changed: 217 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
RecencyConfig,
4040
SessionListResponse,
4141
WorkingMemory,
42+
WorkingMemoryGetOrCreateResponse,
4243
WorkingMemoryResponse,
4344
)
4445

@@ -216,6 +217,11 @@ async def get_working_memory(
216217
"""
217218
Get working memory for a session, including messages and context.
218219
220+
.. deprecated:: next_version
221+
This method is deprecated because it doesn't inform you whether
222+
a session was created or found existing. Use `get_or_create_working_memory`
223+
instead, which returns a tuple of (memory, created) for better session management.
224+
219225
Args:
220226
session_id: The session ID to retrieve working memory for
221227
user_id: The user ID to retrieve working memory for
@@ -230,6 +236,15 @@ async def get_working_memory(
230236
MemoryNotFoundError: If the session is not found
231237
MemoryServerError: For other server errors
232238
"""
239+
import warnings
240+
241+
warnings.warn(
242+
"get_working_memory is deprecated and will be removed in a future version. "
243+
"Use get_or_create_working_memory instead, which returns both the memory and "
244+
"whether the session was created or found existing.",
245+
DeprecationWarning,
246+
stacklevel=2,
247+
)
233248
params = {}
234249

235250
if user_id is not None:
@@ -267,6 +282,82 @@ async def get_working_memory(
267282
self._handle_http_error(e.response)
268283
raise
269284

285+
async def get_or_create_working_memory(
286+
self,
287+
session_id: str,
288+
user_id: str | None = None,
289+
namespace: str | None = None,
290+
model_name: ModelNameLiteral | None = None,
291+
context_window_max: int | None = None,
292+
) -> WorkingMemoryGetOrCreateResponse:
293+
"""
294+
Get working memory for a session, creating it if it doesn't exist.
295+
296+
This method returns both the working memory and whether it was created or found.
297+
This is important for applications that need to know if they're working with
298+
a new session or an existing one.
299+
300+
Args:
301+
session_id: The session ID to retrieve or create working memory for
302+
user_id: The user ID to retrieve working memory for
303+
namespace: Optional namespace for the session
304+
model_name: Optional model name to determine context window size
305+
context_window_max: Optional direct specification of context window tokens
306+
307+
Returns:
308+
WorkingMemoryGetOrCreateResponse containing the memory and creation status
309+
310+
Example:
311+
```python
312+
# Get or create session memory
313+
result = await client.get_or_create_working_memory(
314+
session_id="chat_session_123",
315+
user_id="user_456"
316+
)
317+
318+
if result.created:
319+
print("Created new session")
320+
else:
321+
print("Found existing session")
322+
323+
# Access the memory
324+
memory = result.memory
325+
print(f"Session has {len(memory.messages)} messages")
326+
```
327+
"""
328+
try:
329+
# Try to get existing working memory first
330+
existing_memory = await self.get_working_memory(
331+
session_id=session_id,
332+
user_id=user_id,
333+
namespace=namespace,
334+
model_name=model_name,
335+
context_window_max=context_window_max,
336+
)
337+
return WorkingMemoryGetOrCreateResponse(
338+
memory=existing_memory, created=False
339+
)
340+
except Exception:
341+
# Session doesn't exist, create it
342+
empty_memory = WorkingMemory(
343+
session_id=session_id,
344+
namespace=namespace or self.config.default_namespace,
345+
messages=[],
346+
memories=[],
347+
data={},
348+
user_id=user_id,
349+
)
350+
351+
created_memory = await self.put_working_memory(
352+
session_id=session_id,
353+
memory=empty_memory,
354+
user_id=user_id,
355+
model_name=model_name,
356+
context_window_max=context_window_max,
357+
)
358+
359+
return WorkingMemoryGetOrCreateResponse(memory=created_memory, created=True)
360+
270361
async def put_working_memory(
271362
self,
272363
session_id: str,
@@ -392,10 +483,14 @@ async def set_working_memory_data(
392483
# Get existing memory if preserving
393484
existing_memory = None
394485
if preserve_existing:
395-
existing_memory = await self.get_working_memory(
396-
session_id=session_id,
397-
namespace=namespace,
398-
)
486+
try:
487+
result_obj = await self.get_or_create_working_memory(
488+
session_id=session_id,
489+
namespace=namespace,
490+
)
491+
existing_memory = result_obj.memory
492+
except Exception:
493+
existing_memory = None
399494

400495
# Create new working memory with the data
401496
working_memory = WorkingMemory(
@@ -449,10 +544,11 @@ async def add_memories_to_working_memory(
449544
```
450545
"""
451546
# Get existing memory
452-
existing_memory = await self.get_working_memory(
547+
result_obj = await self.get_or_create_working_memory(
453548
session_id=session_id,
454549
namespace=namespace,
455550
)
551+
existing_memory = result_obj.memory
456552

457553
# Determine final memories list
458554
if replace or not existing_memory:
@@ -1029,11 +1125,12 @@ async def get_working_memory_tool(
10291125
```
10301126
"""
10311127
try:
1032-
result = await self.get_working_memory(
1128+
result_obj = await self.get_or_create_working_memory(
10331129
session_id=session_id,
10341130
namespace=namespace or self.config.default_namespace,
10351131
user_id=user_id,
10361132
)
1133+
result = result_obj.memory
10371134

10381135
# Format for LLM consumption
10391136
message_count = len(result.messages) if result.messages else 0
@@ -1074,6 +1171,95 @@ async def get_working_memory_tool(
10741171
"summary": f"Error retrieving working memory: {str(e)}",
10751172
}
10761173

1174+
async def get_or_create_working_memory_tool(
1175+
self,
1176+
session_id: str,
1177+
namespace: str | None = None,
1178+
user_id: str | None = None,
1179+
) -> dict[str, Any]:
1180+
"""
1181+
Get or create working memory state formatted for LLM consumption.
1182+
1183+
This method provides a summary of the current working memory state
1184+
that's easy for LLMs to understand and work with. If the session
1185+
doesn't exist, it creates a new one.
1186+
1187+
Args:
1188+
session_id: The session ID to get or create memory for
1189+
namespace: Optional namespace for the session
1190+
user_id: Optional user ID for the session
1191+
1192+
Returns:
1193+
Dict with formatted working memory information and creation status
1194+
1195+
Example:
1196+
```python
1197+
# Get or create working memory state for LLM
1198+
memory_state = await client.get_or_create_working_memory_tool(
1199+
session_id="current_session"
1200+
)
1201+
1202+
if memory_state["created"]:
1203+
print("Created new session")
1204+
else:
1205+
print("Found existing session")
1206+
1207+
print(memory_state["summary"]) # Human-readable summary
1208+
print(f"Messages: {memory_state['message_count']}")
1209+
print(f"Memories: {len(memory_state['memories'])}")
1210+
```
1211+
"""
1212+
try:
1213+
result_obj = await self.get_or_create_working_memory(
1214+
session_id=session_id,
1215+
namespace=namespace or self.config.default_namespace,
1216+
user_id=user_id,
1217+
)
1218+
1219+
# Format for LLM consumption
1220+
result = result_obj.memory
1221+
message_count = len(result.messages) if result.messages else 0
1222+
memory_count = len(result.memories) if result.memories else 0
1223+
data_keys = list(result.data.keys()) if result.data else []
1224+
1225+
# Create formatted memories list
1226+
formatted_memories = []
1227+
if result.memories:
1228+
for memory in result.memories:
1229+
formatted_memories.append(
1230+
{
1231+
"text": memory.text,
1232+
"memory_type": memory.memory_type,
1233+
"topics": memory.topics or [],
1234+
"entities": memory.entities or [],
1235+
"created_at": memory.created_at.isoformat()
1236+
if memory.created_at
1237+
else None,
1238+
}
1239+
)
1240+
1241+
status_text = "new session" if result_obj.created else "existing session"
1242+
1243+
return {
1244+
"session_id": session_id,
1245+
"created": result_obj.created,
1246+
"message_count": message_count,
1247+
"memory_count": memory_count,
1248+
"memories": formatted_memories,
1249+
"data_keys": data_keys,
1250+
"data": result.data or {},
1251+
"context": result.context,
1252+
"summary": f"Retrieved {status_text} with {message_count} messages, {memory_count} stored memories, and {len(data_keys)} data entries",
1253+
}
1254+
1255+
except Exception as e:
1256+
return {
1257+
"session_id": session_id,
1258+
"created": False,
1259+
"error": str(e),
1260+
"summary": f"Error retrieving or creating working memory: {str(e)}",
1261+
}
1262+
10771263
async def add_memory_tool(
10781264
self,
10791265
session_id: str,
@@ -1227,8 +1413,8 @@ def get_working_memory_tool_schema(cls) -> dict[str, Any]:
12271413
return {
12281414
"type": "function",
12291415
"function": {
1230-
"name": "get_working_memory",
1231-
"description": "Get the current working memory state including recent messages, temporarily stored memories, and session-specific data. Use this to check what's already in the current conversation context before deciding whether to search long-term memory or add new information. Examples: Check if user preferences are already loaded in this session, review recent conversation context, see what structured data has been stored for this session.",
1416+
"name": "get_or_create_working_memory",
1417+
"description": "Get the current working memory state including recent messages, temporarily stored memories, and session-specific data. Creates a new session if one doesn't exist. Returns information about whether the session was created or found existing. Use this to check what's already in the current conversation context before deciding whether to search long-term memory or add new information. Examples: Check if user preferences are already loaded in this session, review recent conversation context, see what structured data has been stored for this session.",
12321418
"parameters": {
12331419
"type": "object",
12341420
"properties": {},
@@ -1907,10 +2093,16 @@ async def resolve_function_call(
19072093
result = await self._resolve_search_memory(args)
19082094

19092095
elif function_name == "get_working_memory":
2096+
# Keep backward compatibility for deprecated method
19102097
result = await self._resolve_get_working_memory(
19112098
session_id, effective_namespace, user_id
19122099
)
19132100

2101+
elif function_name == "get_or_create_working_memory":
2102+
result = await self._resolve_get_or_create_working_memory(
2103+
session_id, effective_namespace, user_id
2104+
)
2105+
19142106
elif function_name == "add_memory_to_working_memory":
19152107
result = await self._resolve_add_memory(
19162108
args, session_id, effective_namespace, user_id
@@ -1994,6 +2186,17 @@ async def _resolve_get_working_memory(
19942186
user_id=user_id,
19952187
)
19962188

2189+
async def _resolve_get_or_create_working_memory(
2190+
self, session_id: str, namespace: str | None, user_id: str | None = None
2191+
) -> dict[str, Any]:
2192+
"""Resolve get_or_create_working_memory function call."""
2193+
result = await self.get_or_create_working_memory_tool(
2194+
session_id=session_id,
2195+
namespace=namespace,
2196+
user_id=user_id,
2197+
)
2198+
return result
2199+
19972200
async def _resolve_add_memory(
19982201
self,
19992202
args: dict[str, Any],
@@ -2192,9 +2395,10 @@ async def promote_working_memories_to_long_term(
21922395
Acknowledgement of promotion operation
21932396
"""
21942397
# Get current working memory
2195-
working_memory = await self.get_working_memory(
2398+
result_obj = await self.get_or_create_working_memory(
21962399
session_id=session_id, namespace=namespace
21972400
)
2401+
working_memory = result_obj.memory
21982402

21992403
# Filter memories if specific IDs are requested
22002404
memories_to_promote = working_memory.memories
@@ -2407,9 +2611,10 @@ async def update_working_memory_data(
24072611
WorkingMemoryResponse with updated memory
24082612
"""
24092613
# Get existing memory
2410-
existing_memory = await self.get_working_memory(
2614+
result_obj = await self.get_or_create_working_memory(
24112615
session_id=session_id, namespace=namespace, user_id=user_id
24122616
)
2617+
existing_memory = result_obj.memory
24132618

24142619
# Determine final data based on merge strategy
24152620
if existing_memory and existing_memory.data:
@@ -2462,9 +2667,10 @@ async def append_messages_to_working_memory(
24622667
WorkingMemoryResponse with updated memory (potentially summarized if token limit exceeded)
24632668
"""
24642669
# Get existing memory
2465-
existing_memory = await self.get_working_memory(
2670+
result_obj = await self.get_or_create_working_memory(
24662671
session_id=session_id, namespace=namespace, user_id=user_id
24672672
)
2673+
existing_memory = result_obj.memory
24682674

24692675
# Convert messages to MemoryMessage objects
24702676
converted_messages = []

agent-memory-client/agent_memory_client/models.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,15 @@ class MemoryRecordResults(BaseModel):
283283
next_offset: int | None = None
284284

285285

286+
class WorkingMemoryGetOrCreateResponse(BaseModel):
287+
"""Response from get_or_create_working_memory operations"""
288+
289+
memory: WorkingMemoryResponse
290+
created: bool = Field(
291+
description="True if the session was created, False if it already existed"
292+
)
293+
294+
286295
class MemoryPromptResponse(BaseModel):
287296
"""Response from memory prompt endpoint"""
288297

agent-memory-client/tests/test_client.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,13 @@ async def test_promote_working_memories_to_long_term(self, enhanced_test_client)
8888
)
8989

9090
assert result.status == "ok"
91-
mock_get.assert_called_once_with(session_id=session_id, namespace=None)
91+
mock_get.assert_called_once_with(
92+
session_id=session_id,
93+
user_id=None,
94+
namespace=None,
95+
model_name=None,
96+
context_window_max=None,
97+
)
9298
mock_create.assert_called_once_with(memories)
9399

94100
@pytest.mark.asyncio

docs/agent-examples.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ A comprehensive travel assistant that demonstrates the most complete integration
2121
The travel agent automatically discovers and uses all memory tools:
2222

2323
1. **search_memory** - Search through previous conversations and stored information
24-
2. **get_working_memory** - Check current session state, stored memories, and data
25-
3. **add_memory_to_working_memory** - Store important information as structured memories
24+
2. **get_or_create_working_memory** - Check current working memory session
25+
3. **lazily_create_long_term_memory** - Store important information as structured memories lazily
2626
4. **update_working_memory_data** - Store/update session-specific data like trip plans
2727
5. **web_search** (optional) - Search the internet for current travel information
2828

@@ -132,10 +132,10 @@ Demonstrates comprehensive memory editing capabilities through natural conversat
132132

133133
1. **search_memory** - Find existing memories using natural language
134134
2. **get_long_term_memory** - Retrieve specific memories by ID
135-
3. **add_memory_to_working_memory** - Store new information
135+
3. **lazily_create_long_term_memory** - Store new information lazily
136136
4. **edit_long_term_memory** - Update existing memories
137137
5. **delete_long_term_memories** - Remove outdated information
138-
6. **get_working_memory** - Check current session context
138+
6. **get_or_create_working_memory** - Check current working memory session
139139

140140
### Common Editing Scenarios
141141

0 commit comments

Comments
 (0)