知识图谱的权限逻辑和知识库的权限逻辑保持一致

This commit is contained in:
silk 2026-06-02 21:51:15 +08:00
parent 07b24c1f31
commit 9c056ab391
5 changed files with 46 additions and 13 deletions

View File

@ -632,6 +632,29 @@ async def chat_completion(
) )
def _wrap_mcp_tool_safe(tool):
"""
包装 MCP 工具确保第三方服务异常网络不可达超时等被捕获并以
错误字符串返回而非向上抛出
这样 LangChain Agent 在工具调用失败时仍能写入对应的 ToolMessage
保持 checkpoint 中消息历史的完整性避免下次对话因 tool_calls 缺少
配对回复而触发 LLM 400 BadRequest 错误
"""
original_arun = tool._arun
async def safe_arun(*args, **kwargs):
try:
return await original_arun(*args, **kwargs)
except Exception as exc:
error_msg = f"工具 [{tool.name}] 调用失败: {exc}"
logger.warning(f"MCP 工具异常已捕获,返回错误字符串以维持对话完整性: {error_msg}")
return error_msg
tool._arun = safe_arun
return tool
async def _create_agent_for_request( async def _create_agent_for_request(
request: ChatRequest, request: ChatRequest,
current_user: User, current_user: User,
@ -699,6 +722,7 @@ async def _create_agent_for_request(
# 普通聊天模式 # 普通聊天模式
mcp_client = await get_mcp_client() mcp_client = await get_mcp_client()
mcp_tools = await mcp_client.get_tools() mcp_tools = await mcp_client.get_tools()
# mcp_tools = [_wrap_mcp_tool_safe(t) for t in mcp_tools]
logger.info(f"成功加载 {len(mcp_tools)} 个 MCP 工具") logger.info(f"成功加载 {len(mcp_tools)} 个 MCP 工具")
# 查询用户设置(深度思考是否与模型侧一致取决于 user_list.is_reasoner # 查询用户设置(深度思考是否与模型侧一致取决于 user_list.is_reasoner

View File

@ -277,7 +277,7 @@ async def _fetch_graph_or_404(conn: asyncpg.Connection, graph_pk: int, user: Use
creator_id=raw.get("creator_id"), creator_id=raw.get("creator_id"),
visibility=raw.get("visibility") or "private", visibility=raw.get("visibility") or "private",
) )
if not can_view_graph(user, gr): if not await can_view_graph(conn, user, gr):
raise HTTPException(status_code=404, detail="图谱不存在或无权访问") raise HTTPException(status_code=404, detail="图谱不存在或无权访问")
return raw return raw

View File

@ -159,17 +159,19 @@ async def can_upload_to_kb(conn: asyncpg.Connection, user: User, kb: KnowledgeBa
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
# 知识图谱级权限(保持不变 # 知识图谱级权限(与知识库规则完全一致
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
def can_view_graph(user: User, g: GraphRecord) -> bool: async def can_view_graph(conn: asyncpg.Connection, user: User, g: GraphRecord) -> bool:
"""判断用户是否可查看该知识图谱规则与知识库一致)。""" """判断用户是否可查看该知识图谱。leader 权限覆盖本部门及所有子孙部门(与知识库一致)。"""
if user.role == "admin": if user.role == "admin":
return True return True
if g.creator_id is not None and user.id == g.creator_id: if g.creator_id is not None and user.id == g.creator_id:
return True return True
if user.role == "leader" and user.department_id is not None and g.department_id == user.department_id: if user.role == "leader" and user.department_id is not None and g.department_id is not None:
return True managed = await get_managed_dept_ids(conn, user)
if g.department_id in managed:
return True
vis = g.visibility or "private" vis = g.visibility or "private"
if vis == "private": if vis == "private":
return False return False

View File

@ -601,7 +601,7 @@ async def check_knowledge_graph_has_rag(knowledge_graph_id: int, user: User) ->
creator_id=raw.get("creator_id"), creator_id=raw.get("creator_id"),
visibility=raw.get("visibility") or "private", visibility=raw.get("visibility") or "private",
) )
if not can_view_graph(user, gr): if not await can_view_graph(conn, user, gr):
return False return False
return ( return (
raw.get("build_status") == "completed" raw.get("build_status") == "completed"
@ -633,7 +633,7 @@ async def get_knowledge_graph_tool_flags(user: User, graph_id: int) -> Dict[str,
creator_id=raw.get("creator_id"), creator_id=raw.get("creator_id"),
visibility=raw.get("visibility") or "private", visibility=raw.get("visibility") or "private",
) )
if not can_view_graph(user, gr): if not await can_view_graph(conn, user, gr):
return out return out
if raw.get("build_status") != "completed": if raw.get("build_status") != "completed":
return out return out

View File

@ -8,7 +8,7 @@ from typing import Any, Dict, List, Optional, Tuple
import asyncpg import asyncpg
from core.graph_metadata import graph_table_sql from core.graph_metadata import graph_table_sql
from core.permissions import can_manage_graph, can_view_graph from core.permissions import can_manage_graph, can_view_graph, get_managed_dept_ids
from models.graph_metadata import GraphRecord from models.graph_metadata import GraphRecord
from models.user import User from models.user import User
from logger.logging import get_logger from logger.logging import get_logger
@ -95,13 +95,18 @@ class KnowledgeGraphService:
dept_id = user.department_id dept_id = user.department_id
uid = user.id uid = user.id
# leader 需要获取本部门及所有子孙部门 ID与知识库列表保持一致
managed_dept_ids: List[int] = []
if role == "leader" and dept_id is not None:
managed_dept_ids = await get_managed_dept_ids(conn, user)
where_sql = """ where_sql = """
g.enterprise_id = $1 g.enterprise_id = $1
AND ( AND (
$2::text = 'admin' $2::text = 'admin'
OR g.creator_id = $3 OR g.creator_id = $3
OR ($2::text = 'leader' AND g.department_id IS NOT NULL AND g.department_id = $4) OR ($2::text = 'leader' AND g.department_id IS NOT NULL AND g.department_id = ANY($4::int[]))
OR (g.visibility = 'department' AND g.department_id IS NOT NULL AND g.department_id = $4) OR (g.visibility = 'department' AND g.department_id IS NOT NULL AND g.department_id = $5)
OR (g.visibility = 'enterprise') OR (g.visibility = 'enterprise')
) )
""" """
@ -114,6 +119,7 @@ class KnowledgeGraphService:
enterprise_id, enterprise_id,
role, role,
uid, uid,
managed_dept_ids,
dept_id, dept_id,
) )
@ -131,11 +137,12 @@ class KnowledgeGraphService:
LEFT JOIN department d ON d.id = g.department_id LEFT JOIN department d ON d.id = g.department_id
WHERE {where_sql} WHERE {where_sql}
ORDER BY g.created_at DESC ORDER BY g.created_at DESC
LIMIT $5 OFFSET $6 LIMIT $6 OFFSET $7
""", """,
enterprise_id, enterprise_id,
role, role,
uid, uid,
managed_dept_ids,
dept_id, dept_id,
page_size, page_size,
offset, offset,
@ -182,6 +189,6 @@ class KnowledgeGraphService:
gr = KnowledgeGraphService._row_to_graph_record(raw) gr = KnowledgeGraphService._row_to_graph_record(raw)
except Exception: except Exception:
return None return None
if not can_view_graph(user, gr): if not await can_view_graph(conn, user, gr):
return None return None
return await KnowledgeGraphService.enrich_graph_for_response(conn, raw, user) return await KnowledgeGraphService.enrich_graph_for_response(conn, raw, user)