feat(oss): port v3 pipeline with hybrid search, entity extraction, and additive scoring (#4805)
Co-authored-by: Soumil Rathi <soumilrathi@gmail.com> Co-authored-by: Saket Aryan <saketaryan2002@gmail.com> Co-authored-by: chaithanyak42 <chaithanya.kumar42a@gmail.com> Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
This commit is contained in:
co-authored by
Soumil Rathi
Saket Aryan
chaithanyak42
kartik-mem0
parent
57f944e18a
commit
a488e19044
@@ -2,6 +2,8 @@ from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
pytest.importorskip("langchain", reason="langchain not installed")
|
||||
|
||||
from mem0.configs.llms.base import BaseLlmConfig
|
||||
from mem0.llms.langchain import LangchainLLM
|
||||
|
||||
|
||||
+26
-20
@@ -144,6 +144,7 @@ def create_mocked_memory():
|
||||
|
||||
mock_embedder = MagicMock()
|
||||
mock_embedder.embed.return_value = [0.1, 0.2, 0.3]
|
||||
mock_embedder.embed_batch.return_value = [[0.1, 0.2, 0.3]]
|
||||
mock_embedder_factory.return_value = mock_embedder
|
||||
|
||||
mock_vector_store = MagicMock()
|
||||
@@ -151,9 +152,12 @@ def create_mocked_memory():
|
||||
mock_vector_store.add.return_value = None
|
||||
mock_vector_factory.return_value = mock_vector_store
|
||||
|
||||
mock_sqlite.return_value = MagicMock()
|
||||
mock_db = MagicMock()
|
||||
mock_db.get_last_messages.return_value = []
|
||||
mock_sqlite.return_value = mock_db
|
||||
|
||||
memory = Memory()
|
||||
memory.custom_instructions = None
|
||||
memory.api_version = "v1.0"
|
||||
return memory, mock_llm, mock_vector_store
|
||||
|
||||
@@ -170,6 +174,7 @@ def create_mocked_async_memory():
|
||||
|
||||
mock_embedder = MagicMock()
|
||||
mock_embedder.embed.return_value = [0.1, 0.2, 0.3]
|
||||
mock_embedder.embed_batch.return_value = [[0.1, 0.2, 0.3]]
|
||||
mock_embedder_factory.return_value = mock_embedder
|
||||
|
||||
mock_vector_store = MagicMock()
|
||||
@@ -177,9 +182,12 @@ def create_mocked_async_memory():
|
||||
mock_vector_store.add.return_value = None
|
||||
mock_vector_factory.return_value = mock_vector_store
|
||||
|
||||
mock_sqlite.return_value = MagicMock()
|
||||
mock_db = MagicMock()
|
||||
mock_db.get_last_messages.return_value = []
|
||||
mock_sqlite.return_value = mock_db
|
||||
|
||||
memory = AsyncMemory()
|
||||
memory.custom_instructions = None
|
||||
memory.api_version = "v1.0"
|
||||
return memory, mock_llm, mock_vector_store
|
||||
|
||||
@@ -187,22 +195,21 @@ def create_mocked_async_memory():
|
||||
def test_thinking_tags_sync():
|
||||
"""Test thinking tags handling in Memory._add_to_vector_store (sync)."""
|
||||
memory, mock_llm, mock_vector_store = create_mocked_memory()
|
||||
|
||||
# Mock LLM responses for both phases
|
||||
|
||||
# v3 pipeline: single LLM call returning ADD-only memories
|
||||
mock_llm.generate_response.side_effect = [
|
||||
' <think>Sync fact extraction</think> \n{"facts": ["User loves sci-fi"]}',
|
||||
' <think>Sync memory actions</think> \n{"memory": [{"text": "Loves sci-fi", "event": "ADD"}]}'
|
||||
'<think>Sync extraction</think>\n{"memory": [{"text": "Loves sci-fi", "attributed_to": "user"}]}'
|
||||
]
|
||||
|
||||
|
||||
mock_vector_store.search.return_value = []
|
||||
|
||||
|
||||
result = memory._add_to_vector_store(
|
||||
messages=[{"role": "user", "content": "I love sci-fi movies"}],
|
||||
metadata={},
|
||||
filters={},
|
||||
metadata={},
|
||||
filters={},
|
||||
infer=True
|
||||
)
|
||||
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0]["memory"] == "Loves sci-fi"
|
||||
assert result[0]["event"] == "ADD"
|
||||
@@ -213,13 +220,12 @@ def test_thinking_tags_sync():
|
||||
async def test_async_thinking_tags_async():
|
||||
"""Test thinking tags handling in AsyncMemory._add_to_vector_store."""
|
||||
memory, mock_llm, mock_vector_store = create_mocked_async_memory()
|
||||
|
||||
# Directly mock llm.generate_response instead of via asyncio.to_thread
|
||||
|
||||
# v3 pipeline: single LLM call returning ADD-only memories
|
||||
mock_llm.generate_response.side_effect = [
|
||||
' <think>Async fact extraction</think> \n{"facts": ["User loves sci-fi"]}',
|
||||
' <think>Async memory actions</think> \n{"memory": [{"text": "Loves sci-fi", "event": "ADD"}]}'
|
||||
'<think>Async extraction</think>\n{"memory": [{"text": "Loves sci-fi", "attributed_to": "user"}]}'
|
||||
]
|
||||
|
||||
|
||||
# Mock asyncio.to_thread to call the function directly (bypass threading)
|
||||
async def mock_to_thread(func, *args, **kwargs):
|
||||
if func == mock_llm.generate_response:
|
||||
@@ -230,15 +236,15 @@ async def test_async_thinking_tags_async():
|
||||
return []
|
||||
else:
|
||||
return func(*args, **kwargs)
|
||||
|
||||
|
||||
with patch('mem0.memory.main.asyncio.to_thread', side_effect=mock_to_thread):
|
||||
result = await memory._add_to_vector_store(
|
||||
messages=[{"role": "user", "content": "I love sci-fi movies"}],
|
||||
metadata={},
|
||||
effective_filters={},
|
||||
metadata={},
|
||||
effective_filters={},
|
||||
infer=True
|
||||
)
|
||||
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0]["memory"] == "Loves sci-fi"
|
||||
assert result[0]["event"] == "ADD"
|
||||
Reference in New Issue
Block a user