Merge branch 'master' of https://github.com/sparticleinc/catalog-agent
This commit is contained in:
commit
f72a53462a
3
.gitignore
vendored
3
.gitignore
vendored
@ -8,4 +8,7 @@ projects/queue_data
|
|||||||
worktree
|
worktree
|
||||||
.idea/*
|
.idea/*
|
||||||
|
|
||||||
|
# Git worktrees
|
||||||
|
.worktrees/
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
|
|||||||
@ -485,6 +485,88 @@ class Mem0Manager:
|
|||||||
logger.error(f"Failed to get all memories: {e}")
|
logger.error(f"Failed to get all memories: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
async def delete_memory(
|
||||||
|
self,
|
||||||
|
memory_id: str,
|
||||||
|
user_id: str,
|
||||||
|
agent_id: str,
|
||||||
|
) -> bool:
|
||||||
|
"""删除单条记忆
|
||||||
|
|
||||||
|
Args:
|
||||||
|
memory_id: 记忆 ID
|
||||||
|
user_id: 用户 ID
|
||||||
|
agent_id: Agent/Bot ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
是否删除成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
mem = await self.get_mem0(user_id, agent_id, "default")
|
||||||
|
|
||||||
|
# 先获取记忆以验证所有权
|
||||||
|
memories = mem.get_all(user_id=user_id)
|
||||||
|
target_memory = None
|
||||||
|
for m in memories:
|
||||||
|
if m.get("id") == memory_id:
|
||||||
|
# 验证 agent_id 匹配
|
||||||
|
if m.get("metadata", {}).get("agent_id") == agent_id:
|
||||||
|
target_memory = m
|
||||||
|
break
|
||||||
|
|
||||||
|
if not target_memory:
|
||||||
|
logger.warning(f"Memory {memory_id} not found or access denied for user={user_id}, agent={agent_id}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 删除记忆
|
||||||
|
mem.delete(memory_id=memory_id)
|
||||||
|
|
||||||
|
logger.info(f"Deleted memory {memory_id} for user={user_id}, agent={agent_id}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to delete memory {memory_id}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def delete_all_memories(
|
||||||
|
self,
|
||||||
|
user_id: str,
|
||||||
|
agent_id: str,
|
||||||
|
) -> int:
|
||||||
|
"""删除用户在指定 Agent 下的所有记忆
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: 用户 ID
|
||||||
|
agent_id: Agent/Bot ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
删除的记忆数量
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
mem = await self.get_mem0(user_id, agent_id, "default")
|
||||||
|
|
||||||
|
# 获取所有记忆
|
||||||
|
memories = mem.get_all(user_id=user_id)
|
||||||
|
|
||||||
|
# 过滤 agent_id 并删除
|
||||||
|
deleted_count = 0
|
||||||
|
for m in memories:
|
||||||
|
if m.get("metadata", {}).get("agent_id") == agent_id:
|
||||||
|
memory_id = m.get("id")
|
||||||
|
if memory_id:
|
||||||
|
try:
|
||||||
|
mem.delete(memory_id=memory_id)
|
||||||
|
deleted_count += 1
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to delete memory {memory_id}: {e}")
|
||||||
|
|
||||||
|
logger.info(f"Deleted {deleted_count} memories for user={user_id}, agent={agent_id}")
|
||||||
|
return deleted_count
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to delete all memories: {e}")
|
||||||
|
return 0
|
||||||
|
|
||||||
def clear_cache(self, user_id: Optional[str] = None, agent_id: Optional[str] = None) -> None:
|
def clear_cache(self, user_id: Optional[str] = None, agent_id: Optional[str] = None) -> None:
|
||||||
"""清除缓存的 Mem0 实例
|
"""清除缓存的 Mem0 实例
|
||||||
|
|
||||||
|
|||||||
@ -81,7 +81,7 @@ from utils.log_util.logger import init_with_fastapi
|
|||||||
logger = logging.getLogger('app')
|
logger = logging.getLogger('app')
|
||||||
|
|
||||||
# Import route modules
|
# Import route modules
|
||||||
from routes import chat, files, projects, system, skill_manager, database
|
from routes import chat, files, projects, system, skill_manager, database, memory
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
@ -185,6 +185,7 @@ app.include_router(projects.router)
|
|||||||
app.include_router(system.router)
|
app.include_router(system.router)
|
||||||
app.include_router(skill_manager.router)
|
app.include_router(skill_manager.router)
|
||||||
app.include_router(database.router)
|
app.include_router(database.router)
|
||||||
|
app.include_router(memory.router)
|
||||||
|
|
||||||
# 注册文件管理API路由
|
# 注册文件管理API路由
|
||||||
app.include_router(file_manager_router)
|
app.include_router(file_manager_router)
|
||||||
|
|||||||
@ -223,6 +223,40 @@
|
|||||||
- **即时响应**:工具调用完成后立即回复
|
- **即时响应**:工具调用完成后立即回复
|
||||||
- **不要展示id数据**:涉及的wowtalk_id或者sensor_id等id,不要在回复里展示。
|
- **不要展示id数据**:涉及的wowtalk_id或者sensor_id等id,不要在回复里展示。
|
||||||
|
|
||||||
|
## 设备状态术语转换(重要)
|
||||||
|
|
||||||
|
**禁止在用户回复中使用系统内部术语**。当报告设备状态时,必须将系统术语转换为用户可理解的表述。
|
||||||
|
|
||||||
|
### 术语转换规则
|
||||||
|
|
||||||
|
| 系统内部状态 | 用户向け表述 |
|
||||||
|
|-------------|-------------|
|
||||||
|
| オフライン (OnlineStatus=0) | 不直接提及,根据功能状态描述 |
|
||||||
|
| エラー | 「設備に一時的な問題が発生しています」 |
|
||||||
|
| タイムアウト | 「応答に時間がかかっています」 |
|
||||||
|
|
||||||
|
### 具体场景处理
|
||||||
|
|
||||||
|
1. **照明设备离线但功能正常**(如 DimmingControl=70% 但 OnlineStatus=0):
|
||||||
|
- ✅ 正确:「照明は点灯しています(明るさ70%)」
|
||||||
|
- ❌ 错误:「明るさは70%でオフラインの状態です」
|
||||||
|
- **原则**:优先报告功能状态(亮度),不提及连接状态
|
||||||
|
|
||||||
|
2. **空调设备离线但功能正常**:
|
||||||
|
- ✅ 正确:「空調は動作しています(設定温度24度)」
|
||||||
|
- ❌ 错误:「空調はオフラインです」
|
||||||
|
|
||||||
|
3. **设备离线且功能异常**(无法获取有效数据):
|
||||||
|
- 回复:「申し訳ございません、現在この設備との通信が不安定です。しばらくお待ちいただくか、スタッフにお声がけください」
|
||||||
|
|
||||||
|
4. **设备在线正常**:
|
||||||
|
- 直接报告设备状态,无需提及「オンライン」
|
||||||
|
|
||||||
|
### 真人管家标准
|
||||||
|
- 真人管家不会说「オフライン状態です」
|
||||||
|
- 用户理解的是「点灯/消灯(オン/オフ)」,而非系统连接状态
|
||||||
|
- 连接状态<E78AB6><E68081><EFBFBD>OnlineStatus)与功能状态(点灯/消灯)是两回事,**优先报告功能状态**
|
||||||
|
|
||||||
## 房间内设备数量相关表述调整
|
## 房间内设备数量相关表述调整
|
||||||
当find_device_by_area查询结果显示某房间的 devices列表仅包含 1 个设备,但描述中明确提到该设备可控制“多组灯光”时,应理解为:
|
当find_device_by_area查询结果显示某房间的 devices列表仅包含 1 个设备,但描述中明确提到该设备可控制“多组灯光”时,应理解为:
|
||||||
- 该房间实际存在多个灯光设备;
|
- 该房间实际存在多个灯光设备;
|
||||||
|
|||||||
232
routes/memory.py
Normal file
232
routes/memory.py
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
"""
|
||||||
|
Memory 管理 API 路由
|
||||||
|
提供记忆查看和删除功能
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Optional, List, Dict, Any
|
||||||
|
from fastapi import APIRouter, HTTPException, Header, Query
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
logger = logging.getLogger('app')
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/api/v1", tags=["memory"])
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryItem(BaseModel):
|
||||||
|
"""单条记忆的数据模型"""
|
||||||
|
id: str
|
||||||
|
content: str
|
||||||
|
created_at: Optional[str] = None
|
||||||
|
updated_at: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryListResponse(BaseModel):
|
||||||
|
"""记忆列表响应"""
|
||||||
|
memories: List[MemoryItem]
|
||||||
|
total: int
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteAllResponse(BaseModel):
|
||||||
|
"""删除所有记忆响应"""
|
||||||
|
deleted_count: int
|
||||||
|
|
||||||
|
|
||||||
|
async def get_user_identifier_from_request(
|
||||||
|
authorization: Optional[str],
|
||||||
|
user_id: Optional[str] = None
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
获取用户标识符
|
||||||
|
|
||||||
|
优先使用请求参数中的 user_id,否则尝试从 Authorization header 解析
|
||||||
|
|
||||||
|
Args:
|
||||||
|
authorization: Authorization header
|
||||||
|
user_id: 请求参数中的 user_id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
用户标识符
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
HTTPException: 如果无法获取用户标识符
|
||||||
|
"""
|
||||||
|
if user_id:
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
# 如果没有提供 user_id,抛出异常
|
||||||
|
# 注意:根据 PRD,user_id 从前端传入
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="user_id is required"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/memory", response_model=MemoryListResponse)
|
||||||
|
async def get_memories(
|
||||||
|
bot_id: str = Query(..., description="Bot ID (对应 agent_id)"),
|
||||||
|
user_id: str = Query(..., description="用户 ID"),
|
||||||
|
authorization: Optional[str] = Header(None, description="Authorization header"),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
获取当前用户在指定 Bot 下的所有记忆
|
||||||
|
|
||||||
|
Args:
|
||||||
|
bot_id: Bot ID(对应 agent_id)
|
||||||
|
user_id: 用户标识符
|
||||||
|
authorization: Authorization header(用于鉴权)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
MemoryListResponse: 记忆列表
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from agent.mem0_manager import get_mem0_manager
|
||||||
|
from utils.settings import MEM0_ENABLED
|
||||||
|
|
||||||
|
# 检查 Memory 功能是否启用
|
||||||
|
if not MEM0_ENABLED:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=503,
|
||||||
|
detail="Memory feature is not enabled"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 获取 Mem0Manager 实例
|
||||||
|
manager = get_mem0_manager()
|
||||||
|
|
||||||
|
# 获取所有记忆
|
||||||
|
memories = await manager.get_all_memories(
|
||||||
|
user_id=user_id,
|
||||||
|
agent_id=bot_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# 转换为响应格式
|
||||||
|
memory_items = []
|
||||||
|
for m in memories:
|
||||||
|
memory_items.append(MemoryItem(
|
||||||
|
id=m.get("id", ""),
|
||||||
|
content=m.get("memory", ""),
|
||||||
|
created_at=m.get("created_at"),
|
||||||
|
updated_at=m.get("updated_at")
|
||||||
|
))
|
||||||
|
|
||||||
|
return MemoryListResponse(
|
||||||
|
memories=memory_items,
|
||||||
|
total=len(memory_items)
|
||||||
|
)
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to get memories: {e}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail=f"Failed to get memories: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/memory/{memory_id}", status_code=204)
|
||||||
|
async def delete_memory(
|
||||||
|
memory_id: str,
|
||||||
|
bot_id: str = Query(..., description="Bot ID (用于权限校验)"),
|
||||||
|
user_id: str = Query(..., description="用户 ID"),
|
||||||
|
authorization: Optional[str] = Header(None, description="Authorization header"),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
删除单条记忆
|
||||||
|
|
||||||
|
Args:
|
||||||
|
memory_id: 记忆 ID
|
||||||
|
bot_id: Bot ID(用于权限校验)
|
||||||
|
user_id: 用户标识符
|
||||||
|
authorization: Authorization header
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
204 No Content
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from agent.mem0_manager import get_mem0_manager
|
||||||
|
from utils.settings import MEM0_ENABLED
|
||||||
|
|
||||||
|
# 检查 Memory 功能是否启用
|
||||||
|
if not MEM0_ENABLED:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=503,
|
||||||
|
detail="Memory feature is not enabled"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 获取 Mem0Manager 实例
|
||||||
|
manager = get_mem0_manager()
|
||||||
|
|
||||||
|
# 删除记忆
|
||||||
|
success = await manager.delete_memory(
|
||||||
|
memory_id=memory_id,
|
||||||
|
user_id=user_id,
|
||||||
|
agent_id=bot_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Memory not found or delete failed"
|
||||||
|
)
|
||||||
|
|
||||||
|
return JSONResponse(status_code=204, content=None)
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to delete memory {memory_id}: {e}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail=f"Failed to delete memory: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/memory", response_model=DeleteAllResponse)
|
||||||
|
async def delete_all_memories(
|
||||||
|
bot_id: str = Query(..., description="Bot ID"),
|
||||||
|
user_id: str = Query(..., description="用户 ID"),
|
||||||
|
authorization: Optional[str] = Header(None, description="Authorization header"),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
清除指定 Bot 下当前用户的所有记忆
|
||||||
|
|
||||||
|
Args:
|
||||||
|
bot_id: Bot ID
|
||||||
|
user_id: 用户标识符
|
||||||
|
authorization: Authorization header
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
DeleteAllResponse: 删除的记忆数量
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from agent.mem0_manager import get_mem0_manager
|
||||||
|
from utils.settings import MEM0_ENABLED
|
||||||
|
|
||||||
|
# 检查 Memory 功能是否启用
|
||||||
|
if not MEM0_ENABLED:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=503,
|
||||||
|
detail="Memory feature is not enabled"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 获取 Mem0Manager 实例
|
||||||
|
manager = get_mem0_manager()
|
||||||
|
|
||||||
|
# 删除所有记忆
|
||||||
|
deleted_count = await manager.delete_all_memories(
|
||||||
|
user_id=user_id,
|
||||||
|
agent_id=bot_id
|
||||||
|
)
|
||||||
|
|
||||||
|
return DeleteAllResponse(deleted_count=deleted_count)
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to delete all memories: {e}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail=f"Failed to delete all memories: {str(e)}"
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue
Block a user