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:
|
if "created_at" not in new_metadata:
|
||||||
new_metadata["created_at"] = datetime.now(timezone.utc).isoformat()
|
new_metadata["created_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
new_metadata["updated_at"] = new_metadata["created_at"]
|
new_metadata["updated_at"] = new_metadata["created_at"]
|
||||||
|
new_metadata["text_lemmatized"] = lemmatize_for_bm25(data)
|
||||||
|
|
||||||
await asyncio.to_thread(
|
await asyncio.to_thread(
|
||||||
self.vector_store.insert,
|
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"):
|
with pytest.raises(ValueError, match=r"user_id.*filters"):
|
||||||
memory.get_all(user_id="u1")
|
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