Remove unused rom datetime import date in controller and service Fix Examples route paths from /episodic-list to /episodics to match actual router
252 lines
10 KiB
Python
252 lines
10 KiB
Python
"""
|
||
显性记忆控制器
|
||
|
||
处理显性记忆相关的API接口,包括情景记忆和语义记忆的查询。
|
||
"""
|
||
|
||
from typing import Optional
|
||
|
||
from fastapi import APIRouter, Depends, Query
|
||
|
||
from app.core.logging_config import get_api_logger
|
||
from app.core.response_utils import success, fail
|
||
from app.core.error_codes import BizCode
|
||
from app.services.memory_explicit_service import MemoryExplicitService
|
||
from app.schemas.response_schema import ApiResponse
|
||
from app.schemas.memory_explicit_schema import (
|
||
ExplicitMemoryOverviewRequest,
|
||
ExplicitMemoryDetailsRequest,
|
||
)
|
||
from app.dependencies import get_current_user
|
||
from app.models.user_model import User
|
||
|
||
# Get API logger
|
||
api_logger = get_api_logger()
|
||
|
||
# Initialize service
|
||
memory_explicit_service = MemoryExplicitService()
|
||
|
||
router = APIRouter(
|
||
prefix="/memory/explicit-memory",
|
||
tags=["Explicit Memory"],
|
||
)
|
||
|
||
|
||
@router.post("/overview", response_model=ApiResponse)
|
||
async def get_explicit_memory_overview_api(
|
||
request: ExplicitMemoryOverviewRequest,
|
||
current_user: User = Depends(get_current_user),
|
||
) -> dict:
|
||
"""
|
||
获取显性记忆总览
|
||
|
||
返回指定用户的所有显性记忆列表,包括标题、完整内容、创建时间和情绪信息。
|
||
"""
|
||
workspace_id = current_user.current_workspace_id
|
||
|
||
# 检查用户是否已选择工作空间
|
||
if workspace_id is None:
|
||
api_logger.warning(f"用户 {current_user.username} 尝试查询显性记忆总览但未选择工作空间")
|
||
return fail(BizCode.INVALID_PARAMETER, "请先切换到一个工作空间", "current_workspace_id is None")
|
||
|
||
api_logger.info(
|
||
f"显性记忆总览查询请求: end_user_id={request.end_user_id}, user={current_user.username}, "
|
||
f"workspace={workspace_id}"
|
||
)
|
||
|
||
try:
|
||
# 调用Service层方法
|
||
result = await memory_explicit_service.get_explicit_memory_overview(
|
||
request.end_user_id
|
||
)
|
||
|
||
api_logger.info(
|
||
f"成功获取显性记忆总览: end_user_id={request.end_user_id}, "
|
||
f"total={result['total']}"
|
||
)
|
||
return success(data=result, msg="查询成功")
|
||
|
||
except Exception as e:
|
||
api_logger.error(f"显性记忆总览查询失败: end_user_id={request.end_user_id}, error={str(e)}")
|
||
return fail(BizCode.INTERNAL_ERROR, "显性记忆总览查询失败", str(e))
|
||
|
||
|
||
@router.get("/episodics", response_model=ApiResponse)
|
||
async def get_episodic_memory_list_api(
|
||
end_user_id: str = Query(..., description="end user ID"),
|
||
page: int = Query(1, gt=0, description="page number, starting from 1"),
|
||
pagesize: int = Query(10, gt=0, le=100, description="number of items per page, max 100"),
|
||
start_date: Optional[int] = Query(None, description="start timestamp (ms)"),
|
||
end_date: Optional[int] = Query(None, description="end timestamp (ms)"),
|
||
episodic_type: str = Query("all", description="episodic type :all/conversation/project_work/learning/decision/important_event"),
|
||
current_user: User = Depends(get_current_user),
|
||
) -> dict:
|
||
"""
|
||
获取情景记忆分页列表
|
||
|
||
返回指定用户的情景记忆列表,支持分页、时间范围筛选和情景类型筛选。
|
||
|
||
Args:
|
||
end_user_id: 终端用户ID(必填)
|
||
page: 页码(从1开始,默认1)
|
||
pagesize: 每页数量(默认10,最大100)
|
||
start_date: 开始时间戳(可选,毫秒),自动扩展到当天 00:00:00
|
||
end_date: 结束时间戳(可选,毫秒),自动扩展到当天 23:59:59
|
||
episodic_type: 情景类型筛选(可选,默认all)
|
||
current_user: 当前用户
|
||
|
||
Returns:
|
||
ApiResponse: 包含情景记忆分页列表
|
||
|
||
Examples:
|
||
- 基础分页查询:GET /episodics?end_user_id=xxx&page=1&pagesize=5
|
||
返回第1页,每页5条数据
|
||
- 按时间范围筛选:GET /episodics?end_user_id=xxx&page=1&pagesize=5&start_date=1738684800000&end_date=1738771199000
|
||
返回指定时间范围内的数据
|
||
- 按情景类型筛选:GET /episodics?end_user_id=xxx&page=1&pagesize=5&episodic_type=important_event
|
||
返回类型为"重要事件"的数据
|
||
|
||
Notes:
|
||
- start_date 和 end_date 必须同时提供或同时不提供
|
||
- start_date 不能大于 end_date
|
||
- episodic_type 可选值:all, conversation, project_work, learning, decision, important_event
|
||
- total 为该用户情景记忆总数(不受筛选条件影响)
|
||
- page.total 为筛选后的总条数
|
||
"""
|
||
workspace_id = current_user.current_workspace_id
|
||
|
||
# 检查用户是否已选择工作空间
|
||
if workspace_id is None:
|
||
api_logger.warning(f"用户 {current_user.username} 尝试查询情景记忆列表但未选择工作空间")
|
||
return fail(BizCode.INVALID_PARAMETER, "请先切换到一个工作空间", "current_workspace_id is None")
|
||
|
||
api_logger.info(
|
||
f"情景记忆分页查询: end_user_id={end_user_id}, "
|
||
f"start_date={start_date}, end_date={end_date}, episodic_type={episodic_type}, "
|
||
f"page={page}, pagesize={pagesize}, username={current_user.username}"
|
||
)
|
||
|
||
# 1. 参数校验
|
||
if page < 1 or pagesize < 1:
|
||
api_logger.warning(f"分页参数错误: page={page}, pagesize={pagesize}")
|
||
return fail(BizCode.INVALID_PARAMETER, "分页参数必须大于0")
|
||
|
||
valid_episodic_types = ["all", "conversation", "project_work", "learning", "decision", "important_event"]
|
||
if episodic_type not in valid_episodic_types:
|
||
api_logger.warning(f"无效的情景类型参数: {episodic_type}")
|
||
return fail(BizCode.INVALID_PARAMETER, f"无效的情景类型参数,可选值:{', '.join(valid_episodic_types)}")
|
||
|
||
# 时间戳参数校验
|
||
if (start_date is not None and end_date is None) or (end_date is not None and start_date is None):
|
||
return fail(BizCode.INVALID_PARAMETER, "start_date和end_date必须同时提供")
|
||
|
||
if start_date is not None and end_date is not None and start_date > end_date:
|
||
return fail(BizCode.INVALID_PARAMETER, "start_date不能大于end_date")
|
||
|
||
# 2. 执行查询
|
||
try:
|
||
result = await memory_explicit_service.get_episodic_memory_list(
|
||
end_user_id=end_user_id,
|
||
page=page,
|
||
pagesize=pagesize,
|
||
start_date=start_date,
|
||
end_date=end_date,
|
||
episodic_type=episodic_type,
|
||
)
|
||
api_logger.info(
|
||
f"情景记忆分页查询成功: end_user_id={end_user_id}, "
|
||
f"total={result['total']}, 返回={len(result['items'])}条"
|
||
)
|
||
except Exception as e:
|
||
api_logger.error(f"情景记忆分页查询失败: end_user_id={end_user_id}, error={str(e)}")
|
||
return fail(BizCode.INTERNAL_ERROR, "情景记忆分页查询失败", str(e))
|
||
|
||
# 3. 返回结构化响应
|
||
return success(data=result, msg="查询成功")
|
||
|
||
@router.get("/semantics", response_model=ApiResponse)
|
||
async def get_semantic_memory_list_api(
|
||
end_user_id: str = Query(..., description="终端用户ID"),
|
||
current_user: User = Depends(get_current_user),
|
||
) -> dict:
|
||
"""
|
||
获取语义记忆列表
|
||
|
||
返回指定用户的全量语义记忆列表。
|
||
|
||
Args:
|
||
end_user_id: 终端用户ID(必填)
|
||
current_user: 当前用户
|
||
|
||
Returns:
|
||
ApiResponse: 包含语义记忆全量列表
|
||
"""
|
||
workspace_id = current_user.current_workspace_id
|
||
|
||
if workspace_id is None:
|
||
api_logger.warning(f"用户 {current_user.username} 尝试查询语义记忆列表但未选择工作空间")
|
||
return fail(BizCode.INVALID_PARAMETER, "请先切换到一个工作空间", "current_workspace_id is None")
|
||
|
||
api_logger.info(
|
||
f"语义记忆列表查询: end_user_id={end_user_id}, username={current_user.username}"
|
||
)
|
||
|
||
try:
|
||
result = await memory_explicit_service.get_semantic_memory_list(
|
||
end_user_id=end_user_id
|
||
)
|
||
api_logger.info(
|
||
f"语义记忆列表查询成功: end_user_id={end_user_id}, total={len(result)}"
|
||
)
|
||
except Exception as e:
|
||
api_logger.error(f"语义记忆列表查询失败: end_user_id={end_user_id}, error={str(e)}")
|
||
return fail(BizCode.INTERNAL_ERROR, "语义记忆列表查询失败", str(e))
|
||
|
||
return success(data=result, msg="查询成功")
|
||
|
||
|
||
@router.post("/details", response_model=ApiResponse)
|
||
async def get_explicit_memory_details_api(
|
||
request: ExplicitMemoryDetailsRequest,
|
||
current_user: User = Depends(get_current_user),
|
||
) -> dict:
|
||
"""
|
||
获取显性记忆详情
|
||
|
||
根据 memory_id 返回情景记忆或语义记忆的详细信息。
|
||
- 情景记忆:包括标题、内容、情绪、创建时间
|
||
- 语义记忆:包括名称、核心定义、详细笔记、创建时间
|
||
"""
|
||
workspace_id = current_user.current_workspace_id
|
||
|
||
# 检查用户是否已选择工作空间
|
||
if workspace_id is None:
|
||
api_logger.warning(f"用户 {current_user.username} 尝试查询显性记忆详情但未选择工作空间")
|
||
return fail(BizCode.INVALID_PARAMETER, "请先切换到一个工作空间", "current_workspace_id is None")
|
||
|
||
api_logger.info(
|
||
f"显性记忆详情查询请求: end_user_id={request.end_user_id}, memory_id={request.memory_id}, "
|
||
f"user={current_user.username}, workspace={workspace_id}"
|
||
)
|
||
|
||
try:
|
||
# 调用Service层方法
|
||
result = await memory_explicit_service.get_explicit_memory_details(
|
||
end_user_id=request.end_user_id,
|
||
memory_id=request.memory_id
|
||
)
|
||
|
||
api_logger.info(
|
||
f"成功获取显性记忆详情: end_user_id={request.end_user_id}, memory_id={request.memory_id}, "
|
||
f"memory_type={result.get('memory_type')}"
|
||
)
|
||
return success(data=result, msg="查询成功")
|
||
|
||
except ValueError as e:
|
||
# 处理记忆不存在的情况
|
||
api_logger.warning(f"显性记忆不存在: end_user_id={request.end_user_id}, memory_id={request.memory_id}, error={str(e)}")
|
||
return fail(BizCode.INVALID_PARAMETER, "显性记忆不存在", str(e))
|
||
except Exception as e:
|
||
api_logger.error(f"显性记忆详情查询失败: end_user_id={request.end_user_id}, memory_id={request.memory_id}, error={str(e)}")
|
||
return fail(BizCode.INTERNAL_ERROR, "显性记忆详情查询失败", str(e))
|