diff --git a/haystack_experimental/components/agents/agent.py b/haystack_experimental/components/agents/agent.py index 680936a1..1c6edbf7 100644 --- a/haystack_experimental/components/agents/agent.py +++ b/haystack_experimental/components/agents/agent.py @@ -178,7 +178,7 @@ def __init__( # noqa: PLR0913 def _initialize_fresh_execution( self, - messages: list[ChatMessage], + messages: list[ChatMessage] | None, streaming_callback: StreamingCallbackT | None, requires_async: bool, *, @@ -226,7 +226,7 @@ def _initialize_fresh_execution( ) # NOTE: difference with parent method to add memory retrieval - if self._memory_store: + if self._memory_store and messages: retrieved_memories = self._memory_store.search_memories( query=messages[-1].text, **memory_store_kwargs if memory_store_kwargs else {} ) diff --git a/haystack_experimental/memory_stores/mem0/memory_store.py b/haystack_experimental/memory_stores/mem0/memory_store.py index 51ba6a95..765ad5ff 100644 --- a/haystack_experimental/memory_stores/mem0/memory_store.py +++ b/haystack_experimental/memory_stores/mem0/memory_store.py @@ -50,26 +50,25 @@ def add_memories( user_id: str | None = None, run_id: str | None = None, agent_id: str | None = None, - async_mode: bool = False, **kwargs: Any, ) -> list[dict[str, Any]]: """ Add ChatMessage memories to Mem0. :param messages: List of ChatMessage objects with memory metadata - :param infer: Whether to infer facts from the messages. If False, the whole message will - be added as a memory. + :param infer: Whether to infer facts from the messages. If False, the whole message will be added as a memory. + With `infer=False`, Mem0 returns the added memory ids synchronously. With `infer=True`, Mem0 may return a + pending status without any memory ids; in that case, the extracted memories become available via + `search_memories` once the server finishes indexing them. :param user_id: The user ID to to store and retrieve memories from the memory store. :param run_id: The run ID to to store and retrieve memories from the memory store. :param agent_id: The agent ID to to store and retrieve memories from the memory store. If you want Mem0 to store chat messages from the assistant, you need to set the agent_id. - :param async_mode: Whether to add memories asynchronously. - If True, the method will return immediately and the memories will be added in the background. :param kwargs: Additional keyword arguments to pass to the Mem0 client.add method. Note: ChatMessage.meta in the list of messages will be ignored because Mem0 doesn't allow passing metadata for each message in the list. You can pass metadata for the whole memory by passing the `metadata` keyword argument to the method. - :returns: List of objects with the memory_id and the memory + :returns: List of objects with the memory_id and the memory. """ added_ids = [] ids = self._get_ids(user_id, run_id, agent_id) @@ -85,11 +84,13 @@ def add_memories( # we save the role of the message in the metadata mem0_messages.append({"content": message.text, "role": message.role.value}) try: - status = self.client.add(messages=mem0_messages, infer=infer, **ids, async_mode=async_mode, **kwargs) - if status: + status = self.client.add(messages=mem0_messages, infer=infer, **ids, **kwargs) + if status and "results" in status: for result in status["results"]: - memory_id = {"memory_id": result.get("id"), "memory": result["memory"]} - added_ids.append(memory_id) + # Mem0 v3 wraps the memory text under a `data` key; older shapes exposed it directly + data = result.get("data") + memory_text = data.get("memory") if isinstance(data, dict) else result.get("memory") + added_ids.append({"memory_id": result.get("id"), "memory": memory_text}) except Exception as e: raise RuntimeError(f"Failed to add memory message: {e}") from e return added_ids diff --git a/test/memory_stores/test_mem0_memory_store.py b/test/memory_stores/test_mem0_memory_store.py index 6b762910..40b594fd 100644 --- a/test/memory_stores/test_mem0_memory_store.py +++ b/test/memory_stores/test_mem0_memory_store.py @@ -92,8 +92,7 @@ def test_add_memories(self, sample_messages, memory_store): """Test adding memories successfully.""" store, user_id = memory_store result = store.add_memories(messages=sample_messages, user_id=user_id) - # with infer=True (default), two messages are converted to a single memory - assert len(result) == 1 + assert result == [] @pytest.mark.skipif( not os.environ.get("MEM0_API_KEY", None), @@ -115,9 +114,7 @@ def test_add_memories_with_metadata(self, memory_store): """Test adding memories with metadata.""" store, user_id = memory_store messages = [ChatMessage.from_user("User likes to work with python on NLP projects")] - result = store.add_memories( - messages=messages, user_id=user_id, metadata={"key": "value"}, async_mode=False - ) + result = store.add_memories(messages=messages, infer=False, user_id=user_id, metadata={"key": "value"}) assert len(result) == 1 @pytest.mark.skipif( @@ -201,17 +198,25 @@ def test_delete_memory(self, sample_messages, memory_store): def test_role_based_memories(self, memory_store): store, user_id = memory_store unique_agent_id = _get_unique_user_id() - messages = [ + # in Mem0 v3, to keep user and assistant turns searchable by their own entity id, + # they must be added in separate calls + user_messages = [ ChatMessage.from_user("I'm planning to watch a movie tonight. Any recommendations?"), - ChatMessage.from_assistant("How about thriller movies? They can be quite engaging."), ChatMessage.from_user("I'm not a big fan of thriller movies but I love sci-fi movies."), + ] + assistant_messages = [ + ChatMessage.from_assistant("How about thriller movies? They can be quite engaging."), ChatMessage.from_assistant( "Got it! Then I would recommend Interstellar or Inception? I would also recommend watching some " "Japanese anime movies." ), ] - store.add_memories(messages=messages, infer=False, user_id=user_id, agent_id=unique_agent_id) - assistant_mem = store.search_memories(filters={"field": "agent_id", "operator": "==", "value": unique_agent_id}) + store.add_memories(messages=user_messages, infer=False, user_id=user_id) + store.add_memories(messages=assistant_messages, infer=False, agent_id=unique_agent_id) + + assistant_mem = store.search_memories( + filters={"field": "agent_id", "operator": "==", "value": unique_agent_id} + ) user_mem = store.search_memories(filters={"field": "user_id", "operator": "==", "value": user_id}) assert len(assistant_mem) == 2 assert len(user_mem) == 2