Skip to content

Commit ed3b250

Browse files
abrookinsclaude
andcommitted
refactor: resolve PR review comments on memory editing functionality
- Replace inefficient model_dump() with model_copy() for updates - Improve datetime parsing to handle all ISO 8601 timezone formats - Refactor verbose field setting to use dictionary-first approach - Extract hash regeneration logic into reusable helper function 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 75adb51 commit ed3b250

File tree

2 files changed

+69
-33
lines changed

2 files changed

+69
-33
lines changed

agent_memory_server/long_term_memory.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,30 @@ def generate_memory_hash(memory: MemoryRecord) -> str:
278278
return hashlib.sha256(content_json.encode()).hexdigest()
279279

280280

281+
def update_memory_hash_if_text_changed(memory: MemoryRecord, updates: dict) -> dict:
282+
"""
283+
Helper function to regenerate memory hash if text field was updated.
284+
285+
This avoids code duplication of the hash regeneration logic across
286+
different update flows (like memory creation, merging, and editing).
287+
288+
Args:
289+
memory: The original memory record
290+
updates: Dictionary of updates to apply
291+
292+
Returns:
293+
Dictionary with updated memory_hash added if text was in the updates
294+
"""
295+
result_updates = dict(updates)
296+
297+
# If text was updated, regenerate the hash
298+
if "text" in updates:
299+
temp_memory = memory.model_copy(update=updates)
300+
result_updates["memory_hash"] = generate_memory_hash(temp_memory)
301+
302+
return result_updates
303+
304+
281305
async def merge_memories_with_llm(
282306
memories: list[MemoryRecord], llm_client: Any = None
283307
) -> MemoryRecord:
@@ -1574,17 +1598,10 @@ async def update_long_term_memory(
15741598
f"Cannot update fields: {invalid_fields}. Valid fields: {updatable_fields}"
15751599
)
15761600

1577-
# Create updated memory record
1578-
updated_data = existing_memory.model_dump()
1579-
updated_data.update(updates)
1580-
updated_data["updated_at"] = datetime.now(UTC)
1581-
1582-
# If text was updated, regenerate the hash
1583-
if "text" in updates:
1584-
temp_memory = MemoryRecord(**updated_data)
1585-
updated_data["memory_hash"] = generate_memory_hash(temp_memory)
1586-
1587-
updated_memory = MemoryRecord(**updated_data)
1601+
# Create updated memory record using efficient model_copy and hash helper
1602+
base_updates = {**updates, "updated_at": datetime.now(UTC)}
1603+
update_dict = update_memory_hash_if_text_changed(existing_memory, base_updates)
1604+
updated_memory = existing_memory.model_copy(update=update_dict)
15881605

15891606
# Update in the vectorstore
15901607
adapter = await get_vectorstore_adapter()

agent_memory_server/mcp.py

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
from datetime import datetime
23
from typing import Any
34

45
import ulid
@@ -47,6 +48,29 @@
4748
logger = logging.getLogger(__name__)
4849

4950

51+
def _parse_iso8601_datetime(event_date: str) -> datetime:
52+
"""
53+
Parse ISO 8601 datetime string with robust handling of different timezone formats.
54+
55+
Args:
56+
event_date: ISO 8601 formatted datetime string
57+
58+
Returns:
59+
Parsed datetime object
60+
61+
Raises:
62+
ValueError: If the datetime format is invalid
63+
"""
64+
try:
65+
# Handle 'Z' suffix (UTC indicator)
66+
if event_date.endswith("Z"):
67+
return datetime.fromisoformat(event_date.replace("Z", "+00:00"))
68+
# Let fromisoformat handle other timezone formats like +05:00, -08:00, etc.
69+
return datetime.fromisoformat(event_date)
70+
except ValueError as e:
71+
raise ValueError(f"Invalid ISO 8601 datetime format '{event_date}': {e}") from e
72+
73+
5074
class FastMCP(_FastMCPBase):
5175
"""Extend FastMCP to support optional URL namespace and default STDIO namespace."""
5276

@@ -948,27 +972,22 @@ async def edit_long_term_memory(
948972
)
949973
```
950974
"""
951-
# Build the update request, converting event_date string to datetime if provided
952-
updates = EditMemoryRecordRequest()
953-
954-
if text is not None:
955-
updates.text = text
956-
if topics is not None:
957-
updates.topics = topics
958-
if entities is not None:
959-
updates.entities = entities
960-
if memory_type is not None:
961-
updates.memory_type = memory_type
962-
if namespace is not None:
963-
updates.namespace = namespace
964-
if user_id is not None:
965-
updates.user_id = user_id
966-
if session_id is not None:
967-
updates.session_id = session_id
968-
if event_date is not None:
969-
from datetime import datetime
970-
971-
# Parse ISO 8601 datetime string
972-
updates.event_date = datetime.fromisoformat(event_date.replace("Z", "+00:00"))
975+
# Build the update request dictionary, handling event_date parsing
976+
update_dict = {
977+
"text": text,
978+
"topics": topics,
979+
"entities": entities,
980+
"memory_type": memory_type,
981+
"namespace": namespace,
982+
"user_id": user_id,
983+
"session_id": session_id,
984+
"event_date": (
985+
_parse_iso8601_datetime(event_date) if event_date is not None else None
986+
),
987+
}
988+
989+
# Filter out None values to only include fields that should be updated
990+
update_dict = {k: v for k, v in update_dict.items() if v is not None}
991+
updates = EditMemoryRecordRequest(**update_dict)
973992

974993
return await core_update_long_term_memory(memory_id=memory_id, updates=updates)

0 commit comments

Comments
 (0)