import json
|
import asyncio
|
import traceback
|
|
from ..base import MemoryProviderBase, logger
|
from mem0 import MemoryClient
|
from core.utils.util import check_model_key
|
|
TAG = __name__
|
|
|
class MemoryProvider(MemoryProviderBase):
|
def __init__(self, config, summary_memory=None):
|
super().__init__(config)
|
self.api_key = config.get("api_key", "")
|
self.api_version = config.get("api_version", "v1.1")
|
model_key_msg = check_model_key("Mem0ai", self.api_key)
|
if model_key_msg:
|
logger.bind(tag=TAG).error(model_key_msg)
|
self.use_mem0 = False
|
return
|
else:
|
self.use_mem0 = True
|
|
try:
|
self.client = MemoryClient(api_key=self.api_key)
|
logger.bind(tag=TAG).info("成功连接到 Mem0ai 服务")
|
except Exception as e:
|
logger.bind(tag=TAG).error(f"连接到 Mem0ai 服务时发生错误: {str(e)}")
|
logger.bind(tag=TAG).error(f"详细错误: {traceback.format_exc()}")
|
self.use_mem0 = False
|
|
async def save_memory(self, msgs, session_id=None):
|
try:
|
if self.use_mem0 and len(msgs) >= 2:
|
# Format the content as a message list for mem0
|
messages = []
|
for message in msgs:
|
if message.role == "system":
|
continue
|
|
if message.role == "tool":
|
continue
|
|
content = message.content
|
|
if content is None:
|
continue
|
|
# Extract content from JSON format if present (for ASR with emotion/language tags)
|
# Same logic as in query_memory method
|
try:
|
if content and content.strip().startswith("{") and content.strip().endswith("}"):
|
data = json.loads(content)
|
if "content" in data:
|
content = data["content"]
|
except (json.JSONDecodeError, KeyError, TypeError):
|
# If parsing fails, use original content
|
pass
|
|
messages.append({"role": message.role, "content": content})
|
|
result = await asyncio.to_thread(
|
self.client.add, messages, user_id=self.role_id
|
)
|
logger.bind(tag=TAG).debug(f"Save memory result: {result}")
|
except Exception as e:
|
logger.bind(tag=TAG).error(f"保存记忆失败: {str(e)}")
|
|
return None
|
|
async def query_memory(self, query: str) -> str:
|
if not self.use_mem0:
|
return ""
|
try:
|
if not getattr(self, "role_id", None):
|
return ""
|
|
filters = {"user_id": self.role_id}
|
|
search_query = query
|
try:
|
if query.strip().startswith("{") and query.strip().endswith("}"):
|
data = json.loads(query)
|
if "content" in data:
|
search_query = data["content"]
|
except (json.JSONDecodeError, KeyError):
|
pass
|
|
results = await asyncio.to_thread(
|
self.client.search, search_query, filters=filters
|
)
|
if not results or "results" not in results:
|
return ""
|
|
# Format each memory entry with its update time up to minutes
|
memories = []
|
for entry in results["results"]:
|
timestamp = entry.get("updated_at", "")
|
if timestamp:
|
try:
|
# Parse and reformat the timestamp
|
dt = timestamp.split(".")[0] # Remove milliseconds
|
formatted_time = dt.replace("T", " ")
|
except:
|
formatted_time = timestamp
|
memory = entry.get("memory", "")
|
if timestamp and memory:
|
# Store tuple of (timestamp, formatted_string) for sorting
|
memories.append((timestamp, f"[{formatted_time}] {memory}"))
|
|
# Sort by timestamp in descending order (newest first)
|
memories.sort(key=lambda x: x[0], reverse=True)
|
|
# Extract only the formatted strings
|
memories_str = "\n".join(f"- {memory[1]}" for memory in memories)
|
logger.bind(tag=TAG).debug(f"Query results: {memories_str}")
|
return memories_str
|
except Exception as e:
|
logger.bind(tag=TAG).error(f"查询记忆失败: {str(e)}")
|
return ""
|