Skip to content

Commit c705c9b

Browse files
committed
Add comprehensive regression test for discrete_memory_extracted flag
- Add test_redis_adapter_preserves_discrete_memory_extracted_flag to catch the bug where Redis vector store adapter wasn't preserving the discrete_memory_extracted field during search - Test verifies that memories with discrete_memory_extracted='t' are returned correctly, not defaulted to 'f' - Includes clear error message to help diagnose similar issues in the future
1 parent dac18fc commit c705c9b

File tree

3 files changed

+74
-1
lines changed

3 files changed

+74
-1
lines changed

agent_memory_server/long_term_memory.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
get_model_client,
3131
)
3232
from agent_memory_server.models import (
33+
ExtractedMemoryRecord,
3334
MemoryMessage,
3435
MemoryRecord,
3536
MemoryRecordResults,
@@ -593,7 +594,7 @@ async def compact_long_term_memories(
593594

594595

595596
async def index_long_term_memories(
596-
memories: list[MemoryRecord],
597+
memories: list[MemoryRecord | ExtractedMemoryRecord],
597598
redis_client: Redis | None = None,
598599
deduplicate: bool = False,
599600
vector_distance_threshold: float = 0.12,

agent_memory_server/vectorstore_adapter.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,9 @@ def parse_timestamp_to_datetime(timestamp_val):
874874
topics=self._parse_list_field(doc.metadata.get("topics")),
875875
entities=self._parse_list_field(doc.metadata.get("entities")),
876876
memory_hash=doc.metadata.get("memory_hash", ""),
877+
discrete_memory_extracted=doc.metadata.get(
878+
"discrete_memory_extracted", "f"
879+
),
877880
memory_type=doc.metadata.get("memory_type", "message"),
878881
persisted_at=doc.metadata.get("persisted_at"),
879882
extracted_from=self._parse_list_field(

tests/test_vectorstore_adapter.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
"""Tests for the VectorStore adapter functionality."""
22

3+
import asyncio
34
from unittest.mock import AsyncMock, MagicMock, patch
45

56
import pytest
67

8+
from agent_memory_server.filters import Namespace
79
from agent_memory_server.models import MemoryRecord, MemoryTypeEnum
810
from agent_memory_server.vectorstore_adapter import (
911
LangChainVectorStoreAdapter,
@@ -544,3 +546,70 @@ async def asimilarity_search_with_relevance_scores(
544546
)
545547

546548
assert len(unprocessed_results_after.memories) == 0
549+
550+
def test_redis_adapter_preserves_discrete_memory_extracted_flag(self):
551+
"""Regression test: Ensure Redis adapter preserves discrete_memory_extracted='t' during search.
552+
553+
This test catches the bug where MCP-created memories with discrete_memory_extracted='t'
554+
were being returned as 'f' because the Redis vector store adapter wasn't populating
555+
the field during document-to-memory conversion.
556+
"""
557+
from datetime import UTC, datetime
558+
from unittest.mock import MagicMock
559+
560+
# Create mock vectorstore and embeddings
561+
mock_vectorstore = MagicMock()
562+
mock_embeddings = MagicMock()
563+
564+
# Create Redis adapter
565+
adapter = RedisVectorStoreAdapter(mock_vectorstore, mock_embeddings)
566+
567+
# Mock document that simulates what Redis returns for an MCP-created memory
568+
mock_doc = MagicMock()
569+
mock_doc.page_content = "User likes green tea"
570+
mock_doc.metadata = {
571+
"id_": "memory_001",
572+
"session_id": None,
573+
"user_id": None,
574+
"namespace": "user_preferences",
575+
"created_at": datetime.now(UTC).timestamp(),
576+
"updated_at": datetime.now(UTC).timestamp(),
577+
"last_accessed": datetime.now(UTC).timestamp(),
578+
"topics": "preferences,beverages",
579+
"entities": "",
580+
"memory_hash": "abc123",
581+
"discrete_memory_extracted": "t", # This should be preserved!
582+
"memory_type": "semantic",
583+
"persisted_at": None,
584+
"extracted_from": "",
585+
"event_date": None,
586+
}
587+
588+
# Mock the search to return our test document
589+
mock_vectorstore.asimilarity_search_with_relevance_scores = AsyncMock(
590+
return_value=[(mock_doc, 0.9)]
591+
)
592+
593+
# Perform search
594+
result = asyncio.run(
595+
adapter.search_memories(
596+
query="green tea",
597+
namespace=Namespace(field="namespace", eq="user_preferences"),
598+
limit=10,
599+
)
600+
)
601+
602+
# Verify we got the memory back
603+
assert len(result.memories) == 1
604+
memory = result.memories[0]
605+
606+
# REGRESSION TEST: This should be 't', not 'f'
607+
assert memory.discrete_memory_extracted == "t", (
608+
f"Regression: Expected discrete_memory_extracted='t', got '{memory.discrete_memory_extracted}'. "
609+
f"This indicates the Redis adapter is not preserving the flag during search."
610+
)
611+
612+
# Also verify other expected properties
613+
assert memory.memory_type.value == "semantic"
614+
assert memory.namespace == "user_preferences"
615+
assert memory.text == "User likes green tea"

0 commit comments

Comments
 (0)