feat: 完善企业场景多线路由与执行稳定性

补齐平台模板与场景 DSL、预算控制、执行看板和企业场景脚本,增强 Windows 启动/迁移与前端代理和聊天会话记忆,修复执行创建阶段 500 与异步链路排障体验。

Made-with: Cursor
This commit is contained in:
renjianbo
2026-04-09 21:58:53 +08:00
parent bd3f8be781
commit 0608161c82
58 changed files with 6104 additions and 172 deletions

View File

@@ -38,6 +38,114 @@ class ExecutionLogResponse(BaseModel):
from_attributes = True
def _has_execution_permission(db: Session, execution: Execution, current_user: User) -> bool:
if getattr(current_user, "role", None) == "admin":
return True
if execution.workflow_id:
workflow = db.query(Workflow).filter(Workflow.id == execution.workflow_id).first()
return bool(workflow and workflow.user_id == current_user.id)
if execution.agent_id:
agent = db.query(Agent).filter(Agent.id == execution.agent_id).first()
return bool(agent and (agent.user_id == current_user.id or agent.status in ["published", "running"]))
return False
@router.get("/executions/{execution_id}/chain")
async def get_execution_chain(
execution_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""获取父子执行链路(当前执行 + 递归子执行)。"""
root = db.query(Execution).filter(Execution.id == execution_id).first()
if not root:
raise NotFoundError("执行记录", execution_id)
if not _has_execution_permission(db, root, current_user):
raise NotFoundError("执行记录", execution_id)
all_execs = db.query(Execution).all()
by_parent: Dict[Optional[str], List[Execution]] = {}
for e in all_execs:
by_parent.setdefault(e.parent_execution_id, []).append(e)
def _build_tree(e: Execution) -> Dict[str, Any]:
children = by_parent.get(e.id, [])
children.sort(key=lambda x: x.created_at or datetime.min)
return {
"id": e.id,
"status": e.status,
"workflow_id": e.workflow_id,
"agent_id": e.agent_id,
"depth": e.depth,
"parent_execution_id": e.parent_execution_id,
"execution_time": e.execution_time,
"created_at": e.created_at.isoformat() if e.created_at else None,
"children": [_build_tree(c) for c in children],
}
return _build_tree(root)
@router.get("/executions/{execution_id}/chain/summary")
async def get_execution_chain_summary(
execution_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""获取父子执行链路汇总(节点数、成功失败数、总耗时)。"""
root = db.query(Execution).filter(Execution.id == execution_id).first()
if not root:
raise NotFoundError("执行记录", execution_id)
if not _has_execution_permission(db, root, current_user):
raise NotFoundError("执行记录", execution_id)
all_execs = db.query(Execution).all()
by_parent: Dict[Optional[str], List[Execution]] = {}
by_id: Dict[str, Execution] = {}
for e in all_execs:
by_parent.setdefault(e.parent_execution_id, []).append(e)
by_id[e.id] = e
stack = [root.id]
chain_ids: List[str] = []
while stack:
cur = stack.pop()
chain_ids.append(cur)
for c in by_parent.get(cur, []):
stack.append(c.id)
chain_execs = [by_id[i] for i in chain_ids if i in by_id]
status_count: Dict[str, int] = {
"pending": 0,
"running": 0,
"completed": 0,
"failed": 0,
"awaiting_approval": 0,
}
total_time = 0
for e in chain_execs:
st = (e.status or "").lower()
if st in status_count:
status_count[st] += 1
if isinstance(e.execution_time, int):
total_time += e.execution_time
max_depth = 0
for e in chain_execs:
try:
max_depth = max(max_depth, int(e.depth or 0))
except (TypeError, ValueError):
pass
return {
"root_execution_id": root.id,
"total_executions": len(chain_execs),
"status_count": status_count,
"total_execution_time_ms": total_time,
"max_depth": max_depth,
}
@router.get("/executions/{execution_id}", response_model=List[ExecutionLogResponse])
async def get_execution_logs(
execution_id: str,
@@ -58,17 +166,7 @@ async def get_execution_logs(
raise NotFoundError("执行记录", execution_id)
# 验证权限检查workflow或agent的所有权
has_permission = False
if execution.workflow_id:
workflow = db.query(Workflow).filter(Workflow.id == execution.workflow_id).first()
if workflow and workflow.user_id == current_user.id:
has_permission = True
elif execution.agent_id:
agent = db.query(Agent).filter(Agent.id == execution.agent_id).first()
if agent and (agent.user_id == current_user.id or agent.status in ["published", "running"]):
has_permission = True
if not has_permission:
if not _has_execution_permission(db, execution, current_user):
raise NotFoundError("执行记录", execution_id)
# 构建查询
@@ -104,17 +202,7 @@ async def get_execution_log_summary(
raise NotFoundError("执行记录", execution_id)
# 验证权限检查workflow或agent的所有权
has_permission = False
if execution.workflow_id:
workflow = db.query(Workflow).filter(Workflow.id == execution.workflow_id).first()
if workflow and workflow.user_id == current_user.id:
has_permission = True
elif execution.agent_id:
agent = db.query(Agent).filter(Agent.id == execution.agent_id).first()
if agent and (agent.user_id == current_user.id or agent.status in ["published", "running"]):
has_permission = True
if not has_permission:
if not _has_execution_permission(db, execution, current_user):
raise NotFoundError("执行记录", execution_id)
# 统计各级别日志数量
@@ -184,17 +272,7 @@ async def get_execution_performance(
raise NotFoundError("执行记录", execution_id)
# 验证权限检查workflow或agent的所有权
has_permission = False
if execution.workflow_id:
workflow = db.query(Workflow).filter(Workflow.id == execution.workflow_id).first()
if workflow and workflow.user_id == current_user.id:
has_permission = True
elif execution.agent_id:
agent = db.query(Agent).filter(Agent.id == execution.agent_id).first()
if agent and (agent.user_id == current_user.id or agent.status in ["published", "running"]):
has_permission = True
if not has_permission:
if not _has_execution_permission(db, execution, current_user):
raise NotFoundError("执行记录", execution_id)
from sqlalchemy import func