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:
soumil-rathi
2026-04-14 18:00:58 +05:30
committed by GitHub
co-authored by Soumil Rathi Saket Aryan chaithanyak42 kartik-mem0
parent 57f944e18a
commit a488e19044
120 changed files with 10107 additions and 17135 deletions
+2
View File
@@ -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
View File
@@ -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"