知识图谱的权限逻辑和知识库的权限逻辑保持一致
This commit is contained in:
parent
07b24c1f31
commit
9c056ab391
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue