feat: restore source parity and harden agent runtime

This commit is contained in:
2026-06-22 11:17:26 +08:00
parent e33f08277b
commit 0793eb82d6
596 changed files with 168879 additions and 290 deletions
@@ -0,0 +1,178 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
并行分支节点处理器
处理并行分支的执行和汇聚
"""
import copy
import logging
from typing import Dict, TYPE_CHECKING
from sqlalchemy.orm.attributes import flag_modified
from online_dev.workflow.engine.handlers.base_handler import BaseNodeHandler
if TYPE_CHECKING:
from online_dev.workflow.engine.base import ExecutionContext
logger = logging.getLogger(__name__)
class ParallelHandler(BaseNodeHandler):
"""
并行分支节点处理器
同时执行所有分支,所有分支完成后汇聚继续
"""
async def execute(self, context: 'ExecutionContext', node: Dict) -> None:
"""
执行并行节点:同时启动所有分支
"""
node_id = self.get_node_id(node)
node_name = self.get_node_name(node)
branches = node.get('branches', [])
logger.info(f"处理并行分支 - 节点: {node_id}, 分支数: {len(branches)}")
if not branches:
logger.warning(f"并行节点 {node_id} 没有分支,自动跳过")
await self.advance_to_next(context, node)
return
# 更新实例当前节点
await self.update_instance_node(context, node)
# 初始化并行分支状态(深拷贝确保 SQLAlchemy 检测到变更)
parallel_status = copy.deepcopy(context.instance.parallel_branch_status or {})
parallel_status[node_id] = {
branch.get('id', ''): 'pending' for branch in branches
}
context.instance.parallel_branch_status = parallel_status
flag_modified(context.instance, 'parallel_branch_status')
context.db.add(context.instance)
await context.db.flush()
# 记录进入并行分支
await self.create_log(
context, node, 'parallel_start',
comment=f'进入并行分支,共 {len(branches)} 个分支',
extra_data={
'branch_count': len(branches),
'branch_ids': [b.get('id') for b in branches],
},
)
# 同时执行所有分支
for branch in branches:
branch_children = branch.get('children')
if branch_children:
await self._execute_branch(context, node, branch, branch_children)
else:
# 空分支,标记为完成
await self.mark_branch_complete(context, node, branch)
# 检查是否所有分支都已完成
await self.check_completion(context, node)
async def _execute_branch(
self,
context: 'ExecutionContext',
parallel_node: Dict,
branch: Dict,
first_node: Dict,
) -> None:
"""
执行并行分支中的第一个节点。
统一通过引擎 handler 分发,支持所有节点类型(approval, handle, copy, notify,
condition, delay, service, subflow, data_update, parallel 等)。
非阻塞节点执行后会自动调用 advance_to_next 推进;阻塞节点(approval/handle
在任务完成时由引擎推进。当分支走到末尾时,引擎的 _advance_to_next 会通过
find_parallel_branch_for_node 找到所属并行分支并 mark_branch_complete。
"""
node_type = first_node.get('type')
handler = self.engine._get_handler(node_type)
if handler:
await handler.execute(context, first_node)
else:
logger.warning(f"并行分支内未知节点类型: {node_type},标记分支完成")
await self.mark_branch_complete(context, parallel_node, branch)
async def mark_branch_complete(
self,
context: 'ExecutionContext',
parallel_node: Dict,
branch: Dict,
) -> None:
"""
标记并行分支完成
"""
node_id = self.get_node_id(parallel_node)
branch_id = branch.get('id', '')
branch_name = branch.get('name', '')
# 更新分支状态(深拷贝确保 SQLAlchemy 检测到 JSON 字段变更)
await context.db.refresh(context.instance)
parallel_status = copy.deepcopy(context.instance.parallel_branch_status or {})
if node_id in parallel_status:
parallel_status[node_id][branch_id] = 'completed'
context.instance.parallel_branch_status = parallel_status
flag_modified(context.instance, 'parallel_branch_status')
context.db.add(context.instance)
await context.db.flush()
logger.info(f"并行分支完成 - 节点: {node_id}, 分支: {branch_id}")
# 记录分支完成
await self.create_log(
context, parallel_node, 'parallel_branch_complete',
comment=f'并行分支 {branch_name} 完成',
extra_data={'branch_id': branch_id},
)
# 检查是否所有分支都已完成
await self.check_completion(context, parallel_node)
async def check_completion(self, context: 'ExecutionContext', parallel_node: Dict) -> None:
"""
检查并行分支是否全部完成
"""
node_id = self.get_node_id(parallel_node)
# 刷新实例获取最新状态
await context.db.refresh(context.instance)
parallel_status = copy.deepcopy(context.instance.parallel_branch_status or {})
branch_statuses = parallel_status.get(node_id, {})
if not branch_statuses:
logger.warning(f"并行节点 {node_id} 没有分支状态记录")
return
# 检查是否所有分支都已完成
all_completed = all(status == 'completed' for status in branch_statuses.values())
pending_branches = [bid for bid, status in branch_statuses.items() if status != 'completed']
logger.info(f"并行节点 {node_id} 状态检查: 全部完成={all_completed}, 待完成={pending_branches}")
if all_completed:
logger.info(f"并行节点 {node_id} 所有分支完成,继续推进")
# 清理已完成的并行节点状态(深拷贝确保变更检测)
parallel_status = copy.deepcopy(parallel_status)
del parallel_status[node_id]
context.instance.parallel_branch_status = parallel_status
flag_modified(context.instance, 'parallel_branch_status')
context.db.add(context.instance)
await context.db.flush()
# 记录日志
await self.create_log(
context, parallel_node, 'parallel_complete',
comment='所有并行分支完成',
)
# 推进到下一节点
await self.advance_to_next(context, parallel_node)