atomic-agents
atomic-agents copied to clipboard
Getting TypeError: Object of type PosixPath is not JSON serializable with multi modal images in AgentMemory
See below stack trace
It looks like when images created from instructor.Image.from_path(image_path) and used as input they get saved into AgentMemory.
Then once they get reused on subsequent BaseAgent run() calls, the json dumps call fails cause PosixPath is not serializable (dumps gets called when the BaseAgent calls self.memory.copy())
File "./test/.venv/lib/python3.10/site-packages/atomic_agents/lib/components/agent_memory.py", line 208, in dump
return json.dumps(memory_data)
File "./test/.pyenv/versions/3.10.15/lib/python3.10/json/__init__.py", line 231, in dumps
return _default_encoder.encode(obj)
File "./test/.pyenv/versions/3.10.15/lib/python3.10/json/encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "./test/.pyenv/versions/3.10.15/lib/python3.10/json/encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "./test/.pyenv/versions/3.10.15/lib/python3.10/json/encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type PosixPath is not JSON serializable
def dump(self) -> str:
....
serialized_history = []
for message in self.history:
content_class = message.content.__class__
serialized_message = {
"role": message.role,
"content": {
"class_name": f"{content_class.__module__}.{content_class.__name__}",
"data": message.content.model_dump(),
},
"turn_id": message.turn_id,
}
....
return json.dumps(memory_data)
I fixed it in my overridden AgentMemory class with some ugly hacks (I'm sure there is a better way so figured I'd post this issue)
from pathlib import PosixPath
import instructor
....
def replace_posixpath(self, obj):
if isinstance(obj, dict):
return {k: self.replace_posixpath(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [self.replace_posixpath(item) for item in obj]
elif isinstance(obj, PosixPath):
return {"__class__": "PosixPath", "path": str(obj)}
else:
return obj
def reconstruct_image_from_posix_path(self, content_info):
# Check if the image data contains the PosixPath marker and reconstruct the Image object
if "image" in content_info["data"] and isinstance(content_info["data"]["image"], dict):
if "source" in content_info["data"]["image"]:
if "__class__" in content_info["data"]["image"]["source"]:
if content_info["data"]["image"]["source"]["__class__"] == "PosixPath":
image_path = content_info["data"]["image"]["source"]["path"]
content_info["data"]["image"] = instructor.Image.from_path(image_path)
....
def dump(self) -> str:
...
for message in self.history:
content_class = message.content.__class__
dumped_data = message.content.model_dump()
dumped_data = self.replace_posixpath(dumped_data)
serialized_message = {
"role": message.role,
"content": {
"class_name": f"{content_class.__module__}.{content_class.__name__}",
"data": dumped_data,
},
"turn_id": message.turn_id,
}
....
def load(self, serialized_data: str) -> None:
...
for message_data in memory_data["history"]:
content_info = message_data["content"]
content_class = self._get_class_from_string(content_info["class_name"])
self.reconstruct_image_from_posix_path(content_info)
content_instance = content_class(**content_info["data"])