fix: add missing text_lemmatized in AsyncMemory._create_memory (#4886)
This commit is contained in:
@@ -3008,6 +3008,7 @@ class AsyncMemory(MemoryBase):
|
||||
if "created_at" not in new_metadata:
|
||||
new_metadata["created_at"] = datetime.now(timezone.utc).isoformat()
|
||||
new_metadata["updated_at"] = new_metadata["created_at"]
|
||||
new_metadata["text_lemmatized"] = lemmatize_for_bm25(data)
|
||||
|
||||
await asyncio.to_thread(
|
||||
self.vector_store.insert,
|
||||
|
||||
@@ -854,3 +854,84 @@ def test_get_all_rejects_user_id_kwarg(mock_sqlite, mock_llm_factory, mock_vecto
|
||||
|
||||
with pytest.raises(ValueError, match=r"user_id.*filters"):
|
||||
memory.get_all(user_id="u1")
|
||||
|
||||
|
||||
# ─── Regression: AsyncMemory._create_memory must store text_lemmatized ─────────
|
||||
@patch('mem0.utils.factory.EmbedderFactory.create')
|
||||
@patch('mem0.utils.factory.VectorStoreFactory.create')
|
||||
@patch('mem0.utils.factory.LlmFactory.create')
|
||||
@patch('mem0.memory.storage.SQLiteManager')
|
||||
def test_sync_create_memory_stores_text_lemmatized(mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
|
||||
"""Sync Memory._create_memory must include text_lemmatized in payload for BM25 keyword search."""
|
||||
embedder = MagicMock()
|
||||
embedder.embed.return_value = [0.1, 0.2, 0.3]
|
||||
mock_embedder_factory.return_value = embedder
|
||||
|
||||
mock_vector_store = MagicMock()
|
||||
mock_vector_store.insert.return_value = None
|
||||
telemetry_vector_store = MagicMock()
|
||||
mock_vector_factory.side_effect = [mock_vector_store, telemetry_vector_store]
|
||||
|
||||
mock_llm_factory.return_value = MagicMock()
|
||||
mock_sqlite.return_value = MagicMock()
|
||||
|
||||
from mem0.memory.main import Memory as MemoryClass
|
||||
memory = MemoryClass(MemoryConfig())
|
||||
|
||||
data = "I love hiking in the mountains"
|
||||
embeddings = {data: [0.1, 0.2, 0.3]}
|
||||
metadata = {"user_id": "test_user"}
|
||||
|
||||
memory._create_memory(data, embeddings, metadata)
|
||||
|
||||
# Check that text_lemmatized was stored in the payload
|
||||
insert_call = mock_vector_store.insert.call_args
|
||||
payload = insert_call.kwargs.get("payloads") or insert_call[1].get("payloads")
|
||||
assert payload is not None and len(payload) == 1
|
||||
assert "text_lemmatized" in payload[0], "Sync _create_memory must store text_lemmatized for BM25"
|
||||
assert payload[0]["text_lemmatized"] != "", "text_lemmatized must not be empty"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mem0.utils.factory.EmbedderFactory.create')
|
||||
@patch('mem0.utils.factory.VectorStoreFactory.create')
|
||||
@patch('mem0.utils.factory.LlmFactory.create')
|
||||
@patch('mem0.memory.storage.SQLiteManager')
|
||||
async def test_async_create_memory_stores_text_lemmatized(mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
|
||||
"""
|
||||
Regression test: AsyncMemory._create_memory must include text_lemmatized
|
||||
in the vector store payload.
|
||||
|
||||
Without text_lemmatized, memories created via AsyncMemory with infer=False
|
||||
are invisible to BM25 keyword search, silently degrading search recall for
|
||||
all async users.
|
||||
"""
|
||||
embedder = MagicMock()
|
||||
embedder.embed.return_value = [0.1, 0.2, 0.3]
|
||||
mock_embedder_factory.return_value = embedder
|
||||
|
||||
mock_vector_store = MagicMock()
|
||||
mock_vector_store.insert.return_value = None
|
||||
mock_vector_factory.return_value = mock_vector_store
|
||||
|
||||
mock_llm_factory.return_value = MagicMock()
|
||||
mock_sqlite.return_value = MagicMock()
|
||||
|
||||
from mem0.memory.main import AsyncMemory
|
||||
memory = AsyncMemory(MemoryConfig())
|
||||
|
||||
data = "I love hiking in the mountains"
|
||||
embeddings = {data: [0.1, 0.2, 0.3]}
|
||||
metadata = {"user_id": "test_user"}
|
||||
|
||||
await memory._create_memory(data, embeddings, metadata)
|
||||
|
||||
# Check that text_lemmatized was stored in the payload
|
||||
insert_call = mock_vector_store.insert.call_args
|
||||
payload = insert_call.kwargs.get("payloads") or insert_call[1].get("payloads")
|
||||
assert payload is not None and len(payload) == 1
|
||||
assert "text_lemmatized" in payload[0], (
|
||||
"AsyncMemory._create_memory must store text_lemmatized for BM25 keyword search"
|
||||
)
|
||||
assert payload[0]["text_lemmatized"] != "", "text_lemmatized must not be empty"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user