Compare commits

...

2 Commits

Author SHA1 Message Date
Devin AI
bbf012e800 Fix lint issues: remove unused imports
- Remove unused 'os' import from reproduce_issue.py
- Remove unused imports (os, Agent, Crew, Task) from test_mem0_storage.py
- Addresses lint check failure in CI

Co-Authored-By: Jo\u00E3o <joao@crewai.com>
2025-07-14 05:13:00 +00:00
Devin AI
46ad20b9f6 Fix mem0 external memory format issue #3152
- Convert string values to proper message format for mem0 API
- mem0 API expects messages as list of objects with role/content fields
- Add comprehensive tests for external memory type and message formatting
- Add reproduction script to verify the fix works
- Resolves 'Expected a list of items but got type str' error

Co-Authored-By: Jo\u00E3o <joao@crewai.com>
2025-07-14 05:09:59 +00:00
4 changed files with 152 additions and 7 deletions

54
reproduce_issue.py Normal file
View File

@@ -0,0 +1,54 @@
"""
Reproduction script for issue #3152 - mem0 external memory format error
Based on the code provided in the GitHub issue
"""
from crewai import Agent, Task, Crew
from crewai.memory.external.external_memory import ExternalMemory
def test_mem0_external_memory():
"""Test that reproduces the mem0 external memory format error"""
embedder_config = {
"provider": "mem0",
"config": {
"user_id": "test_user_123",
}
}
external_memory = ExternalMemory(embedder_config=embedder_config)
agent = Agent(
role="Test Agent",
goal="Test external memory functionality",
backstory="A test agent for reproducing the mem0 issue",
verbose=True
)
task = Task(
description="Test task for external memory",
expected_output="Test output",
agent=agent
)
crew = Crew(
agents=[agent],
tasks=[task],
external_memory=external_memory,
verbose=True
)
print("Testing mem0 external memory integration...")
try:
result = crew.kickoff()
print("SUCCESS: External memory integration worked!")
print(f"Result: {result}")
except Exception as e:
print(f"ERROR: {e}")
if "Expected a list of items but got type" in str(e):
print("CONFIRMED: This is the mem0 format error from issue #3152")
raise
if __name__ == "__main__":
test_mem0_external_memory()

View File

@@ -93,7 +93,13 @@ class Mem0Storage(Storage):
if params:
if isinstance(self.memory, MemoryClient):
params["output_format"] = "v1.1"
self.memory.add(value, **params)
if isinstance(value, str):
messages = [{"role": "assistant", "content": value}]
else:
messages = value
self.memory.add(messages, **params)
def search(
self,

View File

@@ -329,3 +329,29 @@ def test_external_memory_save_events(custom_storage, external_memory_with_mocked
'agent_role': "test_agent",
'save_time_ms': ANY
}
def test_external_memory_with_mem0_storage_integration():
"""Test external memory integration with mem0 storage specifically"""
from crewai.memory.storage.mem0_storage import Mem0Storage
with patch('crewai.memory.external.external_memory.ExternalMemory._configure_mem0') as mock_configure:
mock_storage = MagicMock(spec=Mem0Storage)
mock_configure.return_value = mock_storage
embedder_config = {"provider": "mem0", "config": {"user_id": "test_user"}}
external_memory = ExternalMemory(embedder_config=embedder_config)
mock_crew = MagicMock()
external_memory.set_crew(mock_crew)
test_value = "Test external memory content"
test_metadata = {"task": "test_task"}
test_agent = "test_agent"
external_memory.save(value=test_value, metadata=test_metadata, agent=test_agent)
mock_storage.save.assert_called_once_with(
test_value,
{'task': 'test_task', 'agent': 'test_agent'}
)

View File

@@ -1,14 +1,10 @@
import os
from unittest.mock import MagicMock, patch
import pytest
from mem0.client.main import MemoryClient
from mem0.memory.main import Memory
from crewai.agent import Agent
from crewai.crew import Crew
from crewai.memory.storage.mem0_storage import Mem0Storage
from crewai.task import Task
# Define the class (if not already defined)
@@ -171,8 +167,9 @@ def test_save_method_with_memory_oss(mem0_storage_with_mocked_config):
mem0_storage.save(test_value, test_metadata)
expected_messages = [{"role": "assistant", "content": test_value}]
mem0_storage.memory.add.assert_called_once_with(
test_value,
expected_messages,
agent_id="Test_Agent",
infer=False,
metadata={"type": "short_term", "key": "value"},
@@ -190,8 +187,9 @@ def test_save_method_with_memory_client(mem0_storage_with_memory_client_using_co
mem0_storage.save(test_value, test_metadata)
expected_messages = [{"role": "assistant", "content": test_value}]
mem0_storage.memory.add.assert_called_once_with(
test_value,
expected_messages,
agent_id="Test_Agent",
infer=False,
metadata={"type": "short_term", "key": "value"},
@@ -218,6 +216,67 @@ def test_search_method_with_memory_oss(mem0_storage_with_mocked_config):
assert results[0]["content"] == "Result 1"
def test_save_method_external_memory_type():
"""Test save method specifically for external memory type"""
crew = MockCrew(
memory_config={
"provider": "mem0",
"config": {"user_id": "test_user", "api_key": "test-key"},
}
)
with patch.object(MemoryClient, "__new__") as mock_client:
mock_memory_instance = MagicMock(spec=MemoryClient)
mock_client.return_value = mock_memory_instance
mem0_storage = Mem0Storage(type="external", crew=crew)
mem0_storage.memory.add = MagicMock()
test_value = "External memory test content"
test_metadata = {"task": "test_task", "agent": "test_agent"}
mem0_storage.save(test_value, test_metadata)
expected_messages = [{"role": "assistant", "content": test_value}]
mem0_storage.memory.add.assert_called_once_with(
expected_messages,
user_id="test_user",
agent_id="Test_Agent",
metadata={"type": "external", "task": "test_task", "agent": "test_agent"},
output_format="v1.1"
)
def test_save_method_with_non_string_value():
"""Test save method when value is already in message format"""
crew = MockCrew(
memory_config={
"provider": "mem0",
"config": {"user_id": "test_user", "api_key": "test-key"},
}
)
with patch.object(MemoryClient, "__new__") as mock_client:
mock_memory_instance = MagicMock(spec=MemoryClient)
mock_client.return_value = mock_memory_instance
mem0_storage = Mem0Storage(type="external", crew=crew)
mem0_storage.memory.add = MagicMock()
test_messages = [{"role": "user", "content": "Test message"}]
test_metadata = {"task": "test_task"}
mem0_storage.save(test_messages, test_metadata)
mem0_storage.memory.add.assert_called_once_with(
test_messages,
user_id="test_user",
agent_id="Test_Agent",
metadata={"type": "external", "task": "test_task"},
output_format="v1.1"
)
def test_search_method_with_memory_client(mem0_storage_with_memory_client_using_config_from_crew):
"""Test search method for different memory types"""
mem0_storage = mem0_storage_with_memory_client_using_config_from_crew