diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index f3af7a3907..42a30dee1e 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -1588,6 +1588,7 @@ async def chat_completion( """处理完整的聊天流程:Payload 处理 → LLM 调用 → 响应处理""" try: # 8.1 Payload 预处理:执行 Pipeline Filters、工具注入、RAG 检索等 + # remark:并不涉及消息的持久化,只涉及发送给 LLM 前,上下文的封装 form_data, metadata, events = await process_chat_payload( request, form_data, user, metadata, model ) diff --git a/backend/open_webui/memory/mem0.py b/backend/open_webui/memory/mem0.py new file mode 100644 index 0000000000..bab715afb0 --- /dev/null +++ b/backend/open_webui/memory/mem0.py @@ -0,0 +1,33 @@ + +async def mem0_search(user_id: str, chat_id: str, last_message: str) -> list[str]: + """ + 预留的 Mem0 检索接口:当前为占位实现。 + 未来可替换为实际检索逻辑,返回若干相关记忆条目(字符串)。 + 增加 chat_id 便于按会话窗口区分/隔离记忆。 + """ + try: + # TODO: 接入真实 Mem0 检索 + print("mem0_search") + print("user_id:", user_id) + print("chat_id:", chat_id) + print("last_message:", last_message) + return [] + except Exception as e: + log.debug(f"Mem0 search failed: {e}") + return [] + + +async def mem0_delete(user_id: str, chat_id: str) -> bool: + """ + 删除指定用户在指定 chat 窗口下的所有 Mem0 相关记忆(占位实现)。 + 未来可替换为实际删除逻辑。 + """ + try: + # TODO: 接入真实删除逻辑(如按 chat_id 过滤) + print("mem0_delete") + print("user_id:", user_id) + print("chat_id:", chat_id) + return True + except Exception as e: + log.debug(f"Mem0 delete failed: {e}") + return False diff --git a/backend/open_webui/routers/chats.py b/backend/open_webui/routers/chats.py index dd6900fd90..185d70c09d 100644 --- a/backend/open_webui/routers/chats.py +++ b/backend/open_webui/routers/chats.py @@ -19,6 +19,7 @@ from open_webui.constants import ERROR_MESSAGES from open_webui.env import SRC_LOG_LEVELS from fastapi import APIRouter, Depends, HTTPException, Request, status from pydantic import BaseModel +from open_webui.memory.mem0 import mem0_delete from open_webui.utils.auth import get_admin_user, get_verified_user @@ -654,6 +655,9 @@ async def delete_chat_by_id(request: Request, id: str, user=Depends(get_verified # === 管理员分支 === if user.role == "admin": chat = Chats.get_chat_by_id(id) + + # 清理该聊天的 Mem0 记忆条目 + await mem0_delete(chat.user_id, id) # 清理孤立标签(仅被该聊天使用的标签) for tag in chat.meta.get("tags", []): @@ -676,6 +680,9 @@ async def delete_chat_by_id(request: Request, id: str, user=Depends(get_verified chat = Chats.get_chat_by_id(id) + # 清理该聊天的 Mem0 记忆条目 + await mem0_delete(user.id, id) + # 清理孤立标签 for tag in chat.meta.get("tags", []): if Chats.count_chats_by_tag_name_and_user_id(tag, user.id) == 1: diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index d16006e618..44e5313bbf 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -54,7 +54,8 @@ from open_webui.routers.pipelines import ( process_pipeline_inlet_filter, process_pipeline_outlet_filter, ) -from open_webui.routers.memories import query_memory, QueryMemoryForm +from open_webui.models.memories import Memories +from open_webui.memory.mem0 import mem0_search from open_webui.utils.webhook import post_webhook from open_webui.utils.files import ( @@ -517,37 +518,44 @@ async def chat_completion_tools_handler( async def chat_memory_handler( - request: Request, form_data: dict, extra_params: dict, user + request: Request, form_data: dict, extra_params: dict, user, metadata ): - try: - results = await query_memory( - request, - QueryMemoryForm( - **{ - "content": get_last_user_message(form_data["messages"]) or "", - "k": 3, - } - ), - user, - ) - except Exception as e: - log.debug(e) - results = None + """ + 聊天记忆处理器 - 注入用户手动保存的记忆 + Mem0 检索结果到当前对话上下文 + + 新增行为: + 1. memory 特性开启时,直接注入用户所有记忆条目(不再做 RAG Top-K) + 2. 预留 Mem0 检索:使用最后一条用户消息查询 Mem0,返回的条目也一并注入 + 3. 上下文注入方式保持不变:统一写入 System Message + """ + user_message = get_last_user_message(form_data.get("messages", [])) or "" + + # === 1. 获取用户全部记忆(不再截断 Top-K) === + memories = Memories.get_memories_by_user_id(user.id) or [] + + # === 2. 预留的 Mem0 检索结果 === + mem0_results = await mem0_search(user.id, metadata.get("chat_id"), user_message) + + # === 3. 格式化记忆条目 === + entries = [] + + # 3.1 用户记忆库全量 + for mem in memories: + created_at_date = time.strftime("%Y-%m-%d", time.localtime(mem.created_at)) if mem.created_at else "Unknown Date" + entries.append(f"[{created_at_date}] {mem.content}") + + # 3.2 Mem0 检索结果 + for item in mem0_results: + entries.append(f"[Mem0] {item}") + + if not entries: + return form_data user_context = "" - if results and hasattr(results, "documents"): - if results.documents and len(results.documents) > 0: - for doc_idx, doc in enumerate(results.documents[0]): - created_at_date = "Unknown Date" - - if results.metadatas[0][doc_idx].get("created_at"): - created_at_timestamp = results.metadatas[0][doc_idx]["created_at"] - created_at_date = time.strftime( - "%Y-%m-%d", time.localtime(created_at_timestamp) - ) - - user_context += f"{doc_idx + 1}. [{created_at_date}] {doc}\n" + for idx, entry in enumerate(entries): + user_context += f"{idx + 1}. {entry}\n" + # === 4. 将记忆注入到系统消息中 === form_data["messages"] = add_or_update_system_message( f"User Context:\n{user_context}\n", form_data["messages"], append=True ) @@ -1189,7 +1197,7 @@ async def process_chat_payload(request, form_data, user, metadata, model): # 12.1 记忆功能 - 注入历史对话记忆 if "memory" in features and features["memory"]: form_data = await chat_memory_handler( - request, form_data, extra_params, user + request, form_data, extra_params, user, metadata ) # 12.2 网页搜索功能 - 执行网络搜索