Merge #47 into develop from fix/othername-name
[fix]Fix the bug that affects user memory. * fix/othername-name: (11 commits squashed) - [fix]Fix the issue with the display of the user's memory list - [fix]Ensure the six dimensions of emotional expression - [fix]Fix the issue with the display of the user's memory list - [fix]Ensure the six dimensions of emotional expression - Merge branch 'fix/othername-name' of codeup.aliyun.com:redbearai/python/redbear-mem-open into fix/othername-name - [fix]Restore the display of memory types - [fix]Fix the issue with the display of the user's memory list - [fix]Ensure the six dimensions of emotional expression - [fix]Restore the display of memory types - Merge branch 'fix/othername-name' of codeup.aliyun.com:redbearai/python/redbear-mem-open into fix/othername-name - [updated]Update the title of the "analytics/node_statistics" log Signed-off-by: 乐力齐 <accounts_690c7b0af9007d7e338af636@mail.teambition.com> Reviewed-by: aliyun6762716068 <accounts_68cb7c6b61f5dcc4200d6251@mail.teambition.com> Merged-by: aliyun6762716068 <accounts_68cb7c6b61f5dcc4200d6251@mail.teambition.com> CR-link: https://codeup.aliyun.com/redbearai/python/redbear-mem-open/change/47
This commit is contained in:
@@ -14,6 +14,7 @@ from app.core.error_codes import BizCode
|
||||
from app.services.user_memory_service import (
|
||||
UserMemoryService,
|
||||
analytics_node_statistics,
|
||||
analytics_memory_types,
|
||||
analytics_graph_data,
|
||||
)
|
||||
from app.schemas.response_schema import ApiResponse
|
||||
@@ -185,21 +186,17 @@ async def get_node_statistics_api(
|
||||
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}, user={current_user.username}, workspace={workspace_id}")
|
||||
api_logger.info(f"记忆类型统计请求: end_user_id={end_user_id}, user={current_user.username}, workspace={workspace_id}")
|
||||
|
||||
try:
|
||||
result = await analytics_node_statistics(db, end_user_id)
|
||||
# 调用新的记忆类型统计函数
|
||||
result = await analytics_memory_types(db, end_user_id)
|
||||
|
||||
# 检查是否有错误消息
|
||||
if "message" in result and result["total"] == 0:
|
||||
api_logger.warning(f"节点统计查询返回空结果: {result.get('message')}")
|
||||
return success(data=result, msg=result.get("message", "查询成功"))
|
||||
|
||||
api_logger.info(f"成功获取节点统计: end_user_id={end_user_id}, total={result['total']}")
|
||||
api_logger.info(f"成功获取记忆类型统计: end_user_id={end_user_id}, 感知记忆={result.get('感知记忆', 0)}")
|
||||
return success(data=result, msg="查询成功")
|
||||
except Exception as e:
|
||||
api_logger.error(f"节点统计查询失败: end_user_id={end_user_id}, error={str(e)}")
|
||||
return fail(BizCode.INTERNAL_ERROR, "节点统计查询失败", str(e))
|
||||
api_logger.error(f"记忆类型查询失败: end_user_id={end_user_id}, error={str(e)}")
|
||||
return fail(BizCode.INTERNAL_ERROR, "记忆类型查询失败", str(e))
|
||||
|
||||
@router.get("/analytics/graph_data", response_model=ApiResponse)
|
||||
async def get_graph_data_api(
|
||||
@@ -293,7 +290,7 @@ async def get_end_user_profile(
|
||||
# 构建响应数据
|
||||
profile_data = EndUserProfileResponse(
|
||||
id=end_user.id,
|
||||
name=end_user.name,
|
||||
other_name=end_user.other_name,
|
||||
position=end_user.position,
|
||||
department=end_user.department,
|
||||
contact=end_user.contact,
|
||||
@@ -364,7 +361,7 @@ async def update_end_user_profile(
|
||||
# 构建响应数据
|
||||
profile_data = EndUserProfileResponse(
|
||||
id=end_user.id,
|
||||
name=end_user.name,
|
||||
other_name=end_user.other_name,
|
||||
position=end_user.position,
|
||||
department=end_user.department,
|
||||
contact=end_user.contact,
|
||||
|
||||
@@ -19,7 +19,6 @@ class EndUser(Base):
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
|
||||
# 用户基本信息字段
|
||||
name = Column(String, nullable=True, comment="姓名")
|
||||
position = Column(String, nullable=True, comment="职位")
|
||||
department = Column(String, nullable=True, comment="部门")
|
||||
contact = Column(String, nullable=True, comment="联系方式")
|
||||
|
||||
@@ -18,7 +18,6 @@ class EndUser(BaseModel):
|
||||
updated_at: datetime.datetime = Field(description="更新时间", default_factory=datetime.datetime.now)
|
||||
|
||||
# 用户基本信息字段
|
||||
name: Optional[str] = Field(description="姓名", default=None)
|
||||
position: Optional[str] = Field(description="职位", default=None)
|
||||
department: Optional[str] = Field(description="部门", default=None)
|
||||
contact: Optional[str] = Field(description="联系方式", default=None)
|
||||
@@ -32,7 +31,7 @@ class EndUserProfileResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: uuid.UUID = Field(description="终端用户ID")
|
||||
name: Optional[str] = Field(description="姓名", default=None)
|
||||
other_name: Optional[str] = Field(description="其他名称", default="")
|
||||
position: Optional[str] = Field(description="职位", default=None)
|
||||
department: Optional[str] = Field(description="部门", default=None)
|
||||
contact: Optional[str] = Field(description="联系方式", default=None)
|
||||
@@ -44,7 +43,7 @@ class EndUserProfileResponse(BaseModel):
|
||||
class EndUserProfileUpdate(BaseModel):
|
||||
"""终端用户基本信息更新请求模型"""
|
||||
end_user_id: str = Field(description="终端用户ID")
|
||||
name: Optional[str] = Field(description="姓名", default=None)
|
||||
other_name: Optional[str] = Field(description="其他名称", default="")
|
||||
position: Optional[str] = Field(description="职位", default=None)
|
||||
department: Optional[str] = Field(description="部门", default=None)
|
||||
contact: Optional[str] = Field(description="联系方式", default=None)
|
||||
|
||||
@@ -65,19 +65,9 @@ class EmotionAnalyticsService:
|
||||
"""获取情绪标签统计
|
||||
|
||||
查询指定用户的情绪类型分布,包括计数、百分比和平均强度。
|
||||
|
||||
Args:
|
||||
end_user_id: 宿主ID(用户组ID)
|
||||
emotion_type: 可选的情绪类型过滤
|
||||
start_date: 可选的开始日期(ISO格式)
|
||||
end_date: 可选的结束日期(ISO格式)
|
||||
limit: 返回结果的最大数量
|
||||
|
||||
Returns:
|
||||
Dict: 包含情绪标签统计的响应数据:
|
||||
- tags: 情绪标签列表
|
||||
- total_count: 总情绪数量
|
||||
- time_range: 时间范围信息
|
||||
确保返回所有6个情绪维度(joy、sadness、anger、fear、surprise、neutral),
|
||||
即使某些维度没有数据也会返回count=0的记录。
|
||||
|
||||
"""
|
||||
try:
|
||||
logger.info(f"获取情绪标签统计: user={end_user_id}, type={emotion_type}, "
|
||||
@@ -92,8 +82,34 @@ class EmotionAnalyticsService:
|
||||
limit=limit
|
||||
)
|
||||
|
||||
# 定义所有6个情绪维度
|
||||
all_emotion_types = ['joy', 'sadness', 'anger', 'fear', 'surprise', 'neutral']
|
||||
|
||||
# 将查询结果转换为字典,方便查找
|
||||
tags_dict = {tag["emotion_type"]: tag for tag in tags}
|
||||
|
||||
# 补全缺失的情绪维度
|
||||
complete_tags = []
|
||||
for emotion in all_emotion_types:
|
||||
if emotion in tags_dict:
|
||||
complete_tags.append(tags_dict[emotion])
|
||||
else:
|
||||
# 如果该情绪类型不存在,添加默认值
|
||||
complete_tags.append({
|
||||
"emotion_type": emotion,
|
||||
"count": 0,
|
||||
"percentage": 0.0,
|
||||
"avg_intensity": 0.0
|
||||
})
|
||||
|
||||
# 计算总数
|
||||
total_count = sum(tag["count"] for tag in tags)
|
||||
total_count = sum(tag["count"] for tag in complete_tags)
|
||||
|
||||
# 如果有数据,重新计算百分比(因为补全了0值项)
|
||||
if total_count > 0:
|
||||
for tag in complete_tags:
|
||||
if tag["count"] > 0:
|
||||
tag["percentage"] = round((tag["count"] / total_count) * 100, 2)
|
||||
|
||||
# 构建时间范围信息
|
||||
time_range = {}
|
||||
@@ -104,12 +120,12 @@ class EmotionAnalyticsService:
|
||||
|
||||
# 格式化响应
|
||||
response = {
|
||||
"tags": tags,
|
||||
"tags": complete_tags,
|
||||
"total_count": total_count,
|
||||
"time_range": time_range if time_range else None
|
||||
}
|
||||
|
||||
logger.info(f"情绪标签统计完成: total_count={total_count}, tags_count={len(tags)}")
|
||||
logger.info(f"情绪标签统计完成: total_count={total_count}, tags_count={len(complete_tags)}")
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -272,7 +272,7 @@ async def get_workspace_total_memory_count(
|
||||
from app.repositories.end_user_repository import EndUserRepository
|
||||
repo = EndUserRepository(db)
|
||||
end_user = repo.get_by_id(uuid.UUID(end_user_id))
|
||||
user_name = end_user.name if end_user else None
|
||||
user_name = end_user.other_name if end_user else None
|
||||
|
||||
return {
|
||||
"total_memory_count": search_result.get("total", 0),
|
||||
|
||||
@@ -534,6 +534,91 @@ async def analytics_node_statistics(
|
||||
return data
|
||||
|
||||
|
||||
async def analytics_memory_types(
|
||||
db: Session,
|
||||
end_user_id: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
统计8种记忆类型的数量
|
||||
|
||||
计算规则:
|
||||
1. 感知记忆 = statement + entity
|
||||
2. 工作记忆 = chunk + entity
|
||||
3. 短期记忆 = chunk
|
||||
4. 长期记忆 = entity
|
||||
5. 显性记忆 = 1/2 * entity
|
||||
6. 隐形记忆 = 1/3 * entity
|
||||
7. 情绪记忆 = statement
|
||||
8. 情景记忆 = memory_summary
|
||||
|
||||
Args:
|
||||
db: 数据库会话
|
||||
end_user_id: 可选的终端用户ID (UUID),用于过滤特定用户的节点
|
||||
|
||||
Returns:
|
||||
{
|
||||
"感知记忆": int,
|
||||
"工作记忆": int,
|
||||
"短期记忆": int,
|
||||
"长期记忆": int,
|
||||
"显性记忆": int,
|
||||
"隐形记忆": int,
|
||||
"情绪记忆": int,
|
||||
"情景记忆": int
|
||||
}
|
||||
"""
|
||||
# 定义需要查询的节点类型
|
||||
node_types = {
|
||||
"Statement": "Statement",
|
||||
"Entity": "ExtractedEntity",
|
||||
"Chunk": "Chunk",
|
||||
"MemorySummary": "MemorySummary"
|
||||
}
|
||||
|
||||
# 存储每种节点类型的计数
|
||||
node_counts = {}
|
||||
|
||||
# 查询每种节点类型的数量
|
||||
for key, node_type in node_types.items():
|
||||
if end_user_id:
|
||||
query = f"""
|
||||
MATCH (n:{node_type})
|
||||
WHERE n.group_id = $group_id
|
||||
RETURN count(n) as count
|
||||
"""
|
||||
result = await _neo4j_connector.execute_query(query, group_id=end_user_id)
|
||||
else:
|
||||
query = f"""
|
||||
MATCH (n:{node_type})
|
||||
RETURN count(n) as count
|
||||
"""
|
||||
result = await _neo4j_connector.execute_query(query)
|
||||
|
||||
# 提取计数结果
|
||||
count = result[0]["count"] if result and len(result) > 0 else 0
|
||||
node_counts[key] = count
|
||||
|
||||
# 获取各节点类型的数量
|
||||
statement_count = node_counts.get("Statement", 0)
|
||||
entity_count = node_counts.get("Entity", 0)
|
||||
chunk_count = node_counts.get("Chunk", 0)
|
||||
memory_summary_count = node_counts.get("MemorySummary", 0)
|
||||
|
||||
# 按规则计算8种记忆类型
|
||||
memory_types = {
|
||||
"感知记忆": statement_count + entity_count,
|
||||
"工作记忆": chunk_count + entity_count,
|
||||
"短期记忆": chunk_count,
|
||||
"长期记忆": entity_count,
|
||||
"显性记忆": entity_count // 2, # 1/2 entity,使用整除
|
||||
"隐形记忆": entity_count // 3, # 1/3 entity,使用整除
|
||||
"情绪记忆": statement_count,
|
||||
"情景记忆": memory_summary_count
|
||||
}
|
||||
|
||||
return memory_types
|
||||
|
||||
|
||||
async def analytics_graph_data(
|
||||
db: Session,
|
||||
end_user_id: str,
|
||||
|
||||
Reference in New Issue
Block a user