diff --git a/routes/memory.py b/routes/memory.py index a0b6a95..c29ae53 100644 --- a/routes/memory.py +++ b/routes/memory.py @@ -1,13 +1,13 @@ """ Memory 管理 API 路由 -提供记忆查看和删除功能 +提供记忆查看、添加和删除功能 """ import logging -from typing import Optional, List, Dict, Any +from typing import Literal, Optional, List, Dict, Any from fastapi import APIRouter, HTTPException, Header, Query from fastapi.responses import JSONResponse -from pydantic import BaseModel +from pydantic import BaseModel, Field logger = logging.getLogger('app') @@ -33,6 +33,26 @@ class DeleteAllResponse(BaseModel): deleted_count: int +class ConversationMessage(BaseModel): + """对话消息""" + role: Literal["user", "assistant"] + content: str = Field(..., min_length=1) + + +class AddMemoryRequest(BaseModel): + """添加记忆的请求体""" + bot_id: str = Field(..., min_length=1) + user_id: str = Field(..., min_length=1) + messages: List[ConversationMessage] = Field(..., max_length=200) + + +class AddMemoryResponse(BaseModel): + """添加记忆的响应""" + success: bool + pairs_processed: int + pairs_failed: int = 0 + + async def get_user_identifier_from_request( authorization: Optional[str], user_id: Optional[str] = None @@ -63,6 +83,92 @@ async def get_user_identifier_from_request( ) +@router.post("/memory", response_model=AddMemoryResponse) +async def add_memory_from_conversation(data: AddMemoryRequest): + """ + 从对话消息中提取并保存记忆 + + 将用户和助手的对话配对,通过 Mem0 提取关键事实并存储。 + 用于 realtime 语音对话等不经过 Agent 中间件的场景。 + 此端点供内部服务调用(如 felo-mygpt),不暴露给外部用户。 + """ + try: + from agent.mem0_manager import get_mem0_manager + from utils.settings import MEM0_ENABLED + + if not MEM0_ENABLED: + raise HTTPException( + status_code=503, + detail="Memory feature is not enabled" + ) + + if not data.messages: + return AddMemoryResponse(success=True, pairs_processed=0) + + manager = get_mem0_manager() + + # 将消息配对为 user-assistant 对,然后调用 add_memory + pairs_processed = 0 + pairs_failed = 0 + i = 0 + while i < len(data.messages): + msg = data.messages[i] + if msg.role == 'user': + # 收集连续的 user 消息 + user_contents = [msg.content] + j = i + 1 + while j < len(data.messages) and data.messages[j].role == 'user': + user_contents.append(data.messages[j].content) + j += 1 + + user_text = '\n'.join(user_contents) + + # 检查是否有对应的 assistant 回复 + assistant_text = "" + if j < len(data.messages) and data.messages[j].role == 'assistant': + assistant_text = data.messages[j].content or "" + j += 1 + + if user_text and assistant_text: + conversation_text = f"User: {user_text}\nAssistant: {assistant_text}" + try: + await manager.add_memory( + text=conversation_text, + user_id=data.user_id, + agent_id=data.bot_id, + metadata={"type": "realtime_conversation"}, + ) + pairs_processed += 1 + except Exception as pair_error: + pairs_failed += 1 + logger.error( + f"Failed to add memory for pair: {pair_error}" + ) + + i = j + else: + i += 1 + + logger.info( + f"Added {pairs_processed} memory pairs (failed={pairs_failed}) " + f"for user={data.user_id}, bot={data.bot_id}" + ) + return AddMemoryResponse( + success=pairs_failed == 0, + pairs_processed=pairs_processed, + pairs_failed=pairs_failed, + ) + + except HTTPException: + raise + except Exception as e: + logger.error(f"Failed to add memory from conversation: {e}") + raise HTTPException( + status_code=500, + detail="Failed to add memory from conversation" + ) + + @router.get("/memory", response_model=MemoryListResponse) async def get_memories( bot_id: str = Query(..., description="Bot ID (对应 agent_id)"),