Merge branch 'develop' into fix/workflow

This commit is contained in:
Eternity
2026-01-14 10:58:13 +08:00
committed by GitHub
77 changed files with 3193 additions and 1889 deletions

View File

@@ -20,6 +20,8 @@ from . import (
knowledgeshare_controller,
memory_agent_controller,
memory_dashboard_controller,
memory_episodic_controller,
memory_explicit_controller,
memory_forget_controller,
memory_reflection_controller,
memory_short_term_controller,
@@ -67,6 +69,8 @@ manager_router.include_router(memory_agent_controller.router)
manager_router.include_router(memory_dashboard_controller.router)
manager_router.include_router(memory_storage_controller.router)
manager_router.include_router(user_memory_controllers.router)
manager_router.include_router(memory_episodic_controller.router)
manager_router.include_router(memory_explicit_controller.router)
manager_router.include_router(api_key_controller.router)
manager_router.include_router(release_share_controller.router)
manager_router.include_router(public_share_controller.router) # 公开路由(无需认证)

View File

@@ -30,7 +30,7 @@ from sqlalchemy.orm import Session
api_logger = get_api_logger()
router = APIRouter(
prefix="/memory/emotion",
prefix="/memory/emotion-memory",
tags=["Emotion Analysis"],
dependencies=[Depends(get_current_user)] # 所有路由都需要认证
)

View File

@@ -32,7 +32,6 @@ def get_workspace_list(
@router.get("/version", response_model=ApiResponse)
def get_system_version():
"""获取系统版本号+说明"""
return success(data={
"version": settings.SYSTEM_VERSION,
"introduction": settings.SYSTEM_INTRODUCTION
}, msg="系统版本获取成功")
current_version = settings.SYSTEM_VERSION
version_introduction = HomePageService.load_version_introduction(current_version)
return success(data={"version": current_version, "introduction": version_introduction}, msg="系统版本获取成功")

View File

@@ -1,18 +1,15 @@
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from typing import List, Optional
import uuid
from app.repositories.end_user_repository import update_end_user_other_name
import uuid
from typing import Optional
from app.core.response_utils import success
from app.db import get_db
from app.dependencies import get_current_user
from app.models.user_model import User
from app.schemas.memory_agent_schema import End_User_Information
from app.schemas.response_schema import ApiResponse
from app.schemas.app_schema import App as AppSchema
from app.services import memory_dashboard_service, memory_storage_service, workspace_service
from app.services.memory_agent_service import get_end_users_connected_configs_batch
from app.core.logging_config import get_api_logger
# 获取API专用日志器
@@ -102,7 +99,8 @@ async def get_workspace_end_users(
"""
获取工作空间的宿主列表
返回格式与原 memory_list 接口中的 end_users 字段相同
返回格式与原 memory_list 接口中的 end_users 字段相同
并包含每个用户的记忆配置信息memory_config_id 和 memory_config_name
"""
workspace_id = current_user.current_workspace_id
# 获取当前空间类型
@@ -113,6 +111,17 @@ async def get_workspace_end_users(
workspace_id=workspace_id,
current_user=current_user
)
# 批量获取所有用户的记忆配置信息(优化:一次查询而非 N 次)
end_user_ids = [str(user.id) for user in end_users]
memory_configs_map = {}
if end_user_ids:
try:
memory_configs_map = get_end_users_connected_configs_batch(end_user_ids, db)
except Exception as e:
api_logger.error(f"批量获取记忆配置失败: {str(e)}")
# 失败时使用空字典,不影响其他数据返回
result = []
for end_user in end_users:
memory_num = {}
@@ -123,10 +132,25 @@ async def get_workspace_end_users(
memory_num = {
"total":memory_dashboard_service.get_current_user_total_chunk(str(end_user.id), db, current_user)
}
# 从批量查询结果中获取配置信息
user_id = str(end_user.id)
memory_config_info = memory_configs_map.get(user_id, {
"memory_config_id": None,
"memory_config_name": None
})
# 只保留需要的字段,移除 error 字段(如果有)
memory_config = {
"memory_config_id": memory_config_info.get("memory_config_id"),
"memory_config_name": memory_config_info.get("memory_config_name")
}
result.append(
{
'end_user':end_user,
'memory_num':memory_num
'end_user': end_user,
'memory_num': memory_num,
'memory_config': memory_config
}
)
@@ -465,7 +489,6 @@ async def dashboard_data(
if storage_type is None:
storage_type = 'neo4j'
user_rag_memory_id = None
# 根据 storage_type 决定返回哪个数据对象
# 如果是 'rag'neo4j_data 为 null否则 rag_data 为 null

View File

@@ -0,0 +1,125 @@
"""
情景记忆相关的控制器
包含情景记忆总览和详情查询接口
"""
from fastapi import APIRouter, Depends
from app.core.error_codes import BizCode
from app.core.logging_config import get_api_logger
from app.core.response_utils import fail, success
from app.dependencies import get_current_user
from app.models.user_model import User
from app.schemas.response_schema import ApiResponse
from app.schemas.memory_episodic_schema import (
EpisodicMemoryOverviewRequest,
EpisodicMemoryDetailsRequest,
)
from app.services.memory_episodic_service import memory_episodic_service
# Get API logger
api_logger = get_api_logger()
router = APIRouter(
prefix="/memory/episodic-memory",
tags=["Episodic Memory"],
)
@router.post("/overview", response_model=ApiResponse)
async def get_episodic_memory_overview_api(
request: EpisodicMemoryOverviewRequest,
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")
# 验证参数
valid_time_ranges = ["all", "today", "this_week", "this_month"]
valid_episodic_types = ["all", "conversation", "project_work", "learning", "decision", "important_event"]
if request.time_range not in valid_time_ranges:
return fail(BizCode.INVALID_PARAMETER, f"无效的时间范围参数,可选值:{', '.join(valid_time_ranges)}")
if request.episodic_type not in valid_episodic_types:
return fail(BizCode.INVALID_PARAMETER, f"无效的情景类型参数,可选值:{', '.join(valid_episodic_types)}")
# 处理 title_keyword去除首尾空格
title_keyword = request.title_keyword.strip() if request.title_keyword else None
api_logger.info(
f"情景记忆总览查询请求: end_user_id={request.end_user_id}, user={current_user.username}, "
f"workspace={workspace_id}, time_range={request.time_range}, episodic_type={request.episodic_type}, "
f"title_keyword={title_keyword}"
)
try:
# 调用Service层方法
result = await memory_episodic_service.get_episodic_memory_overview(
request.end_user_id, request.time_range, request.episodic_type, title_keyword
)
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.post("/details", response_model=ApiResponse)
async def get_episodic_memory_details_api(
request: EpisodicMemoryDetailsRequest,
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}, summary_id={request.summary_id}, "
f"user={current_user.username}, workspace={workspace_id}"
)
try:
# 调用Service层方法
result = await memory_episodic_service.get_episodic_memory_details(
end_user_id=request.end_user_id,
summary_id=request.summary_id
)
api_logger.info(
f"成功获取情景记忆详情: end_user_id={request.end_user_id}, summary_id={request.summary_id}"
)
return success(data=result, msg="查询成功")
except ValueError as e:
# 处理情景记忆不存在的情况
api_logger.warning(f"情景记忆不存在: end_user_id={request.end_user_id}, summary_id={request.summary_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}, summary_id={request.summary_id}, error={str(e)}")
return fail(BizCode.INTERNAL_ERROR, "情景记忆详情查询失败", str(e))

View File

@@ -0,0 +1,115 @@
"""
显性记忆控制器
处理显性记忆相关的API接口包括情景记忆和语义记忆的查询。
"""
from fastapi import APIRouter, Depends
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.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))

View File

@@ -39,7 +39,7 @@ from app.services.memory_forget_service import MemoryForgetService
api_logger = get_api_logger()
router = APIRouter(
prefix="/memory/forget",
prefix="/memory/forget-memory",
tags=["Memory Forgetting Engine"],
dependencies=[Depends(get_current_user)] # 所有路由都需要认证
)

View File

@@ -20,12 +20,6 @@ from app.services.user_memory_service import (
from app.services.memory_entity_relationship_service import MemoryEntityService,MemoryEmotion,MemoryInteraction
from app.schemas.response_schema import ApiResponse
from app.schemas.memory_storage_schema import GenerateCacheRequest
from app.schemas.user_memory_schema import (
EpisodicMemoryOverviewRequest,
EpisodicMemoryDetailsRequest,
ExplicitMemoryOverviewRequest,
ExplicitMemoryDetailsRequest,
)
from app.schemas.end_user_schema import (
EndUserProfileResponse,
@@ -440,195 +434,3 @@ async def memory_space_relationship_evolution(id: str, label: str,
except Exception as e:
api_logger.error(f"关系演变查询失败: id={id}, table={label}, error={str(e)}", exc_info=True)
return fail(BizCode.INTERNAL_ERROR, "关系演变查询失败", str(e))
@router.post("/classifications/episodic-memory", response_model=ApiResponse)
async def get_episodic_memory_overview_api(
request: EpisodicMemoryOverviewRequest,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
) -> 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")
# 验证参数
valid_time_ranges = ["all", "today", "this_week", "this_month"]
valid_episodic_types = ["all", "conversation", "project_work", "learning", "decision", "important_event"]
if request.time_range not in valid_time_ranges:
return fail(BizCode.INVALID_PARAMETER, f"无效的时间范围参数,可选值:{', '.join(valid_time_ranges)}")
if request.episodic_type not in valid_episodic_types:
return fail(BizCode.INVALID_PARAMETER, f"无效的情景类型参数,可选值:{', '.join(valid_episodic_types)}")
# 处理 title_keyword去除首尾空格
title_keyword = request.title_keyword.strip() if request.title_keyword else None
api_logger.info(
f"情景记忆总览查询请求: end_user_id={request.end_user_id}, user={current_user.username}, "
f"workspace={workspace_id}, time_range={request.time_range}, episodic_type={request.episodic_type}, "
f"title_keyword={title_keyword}"
)
try:
# 调用Service层方法
result = await user_memory_service.get_episodic_memory_overview(
db, request.end_user_id, request.time_range, request.episodic_type, title_keyword
)
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.post("/classifications/episodic-memory-details", response_model=ApiResponse)
async def get_episodic_memory_details_api(
request: EpisodicMemoryDetailsRequest,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
) -> 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}, summary_id={request.summary_id}, "
f"user={current_user.username}, workspace={workspace_id}"
)
try:
# 调用Service层方法
result = await user_memory_service.get_episodic_memory_details(
db=db,
end_user_id=request.end_user_id,
summary_id=request.summary_id
)
api_logger.info(
f"成功获取情景记忆详情: end_user_id={request.end_user_id}, summary_id={request.summary_id}"
)
return success(data=result, msg="查询成功")
except ValueError as e:
# 处理情景记忆不存在的情况
api_logger.warning(f"情景记忆不存在: end_user_id={request.end_user_id}, summary_id={request.summary_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}, summary_id={request.summary_id}, error={str(e)}")
return fail(BizCode.INTERNAL_ERROR, "情景记忆详情查询失败", str(e))
@router.post("/classifications/explicit-memory", response_model=ApiResponse)
async def get_explicit_memory_overview_api(
request: ExplicitMemoryOverviewRequest,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
) -> 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 user_memory_service.get_explicit_memory_overview(
db, 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.post("/classifications/explicit-memory-details", response_model=ApiResponse)
async def get_explicit_memory_details_api(
request: ExplicitMemoryDetailsRequest,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
) -> 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 user_memory_service.get_explicit_memory_details(
db=db,
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))