Skip to content

feat(orchestrator): 子工作流节点(同步调用已保存工作流) - #122

Merged
chumingjun merged 1 commit into
mainfrom
feat/subworkflow
Sep 2, 2026
Merged

feat(orchestrator): 子工作流节点(同步调用已保存工作流)#122
chumingjun merged 1 commit into
mainfrom
feat/subworkflow

Conversation

@chumingjun

Copy link
Copy Markdown
Owner

背景

工作流规模变大后出现「一段流程被多个工作流复用」的需求。本 PR 新增 subworkflow 节点:按稳定 workflowId 同步调用库内已保存工作流,父节点等待子 run 终态后取其输出作为节点输出。首期不支持异步等待、重试、续跑与重放。

方案

引擎(engine.js)

  • run 级联元数据:parentRunId/parentNodeId/rootRunId/depth/callChainrun-start/run-end 事件携带。
  • 取消传播:新增 cancelAbort + onCancel 钩子,父运行取消递归取消子运行;重试退避改为可中断等待(waitWithAbort)。
  • _executeNode 修复:releaseSlot 防重试重复扣减并发槽位;queued 事件后同步触发 cancel 不再重复扣 remaining;空图/全覆盖续跑种子主动收尾。
  • registerKind(subworkflow):execute 委托宿主注入的 runChildWorkflowretryable: falselint/templateLintFields 接入节点校验与模板静态检查。

编排器(index.js + lib/subworkflow.js)

  • 输入映射inputMap.triggerInput/runInputs$ref 纯引用保留 JSON 类型、模板字符串输出文本,$upstream 默认取上游输出。
  • 防护:循环调用检测(callChain)、嵌套深度上限 3、单根运行子 run 预算 16。
  • 校验复用validateSubworkflowInputs 内部走 feat(web): 工作流列表实时运行状态与运行控制 #121 已合并的 workflow-inputs.js 统一校验(required/类型/enum/未知字段)。
  • 结果选择selectWorkflowResult 优先输出节点、回退最后成功业务节点,结构化输出与产物一并透传。
  • 删除守卫:被 subworkflow 节点引用的工作流不可删——助手 workflow_deleteDELETE /workflows/detail 均拦截(409 subworkflow-referenced,列出引用方);自引用不拦。
  • 运行记录/runsworkflow_runs/runs/detail 附 parent 元数据;detail 附 children[](SQLite listChildRuns 按开始时间正序 + live 内存合并);含子工作流节点的运行禁续跑/重放(409,避免重复执行子运行)。
  • 子 run 以 source='subworkflow' 启动、suppressNotifications 不重复发通知。

前端(web)

  • registry 注册 subworkflow 节点(图标/色/preset/summary/badges)。
  • NodePanel:目标工作流选择(下拉过滤自身 + 手填 ID 兜底)、输入映射编辑(triggerInput 模板 + runInputs JSON,非法 JSON 保留编辑态由后端校验)。
  • ResultPanel:子工作流运行区——children 列表(状态 pill)、点击拉取子 run 详情(带 AbortController 防竞态)。
  • App 首载拉 /workflows 供选择器使用。

验证

  • orchestrator 18 套单测全过(新增 subworkflow.test.mjs;engine 新增 6 例:空图收尾/取消扣减/重试退避可取消/subworkflow 等待与取消映射/不重复重试/lint 目标解析;sqlite-store listChildRuns;workflow-tools 删除守卫集成)。
  • web 全套 + 全量 npm test 通过;build-web.sh 双 base 构建通过;git diff --check 干净。
  • 基于 main(9f4903f,含 feat(web): 工作流列表实时运行状态与运行控制 #121),复用 workflow-inputs.js 不重复造校验。

后续(不在本 PR)

  • 真实 dsh + 浏览器点验(建子工作流图、父流程调用、取消传播、children 详情)。
  • resume/replay 对子工作流的支持(需要子 run 结果快照语义,单独设计)。

新增 subworkflow 节点类型:按 workflowId 同步调用库内已保存工作流,
父节点等待子 run 终态后取其输出作为节点输出。首期不支持异步等待、
重试(retryable:false)、续跑与重放。

引擎(engine.js):
- run 级联元数据 parentRunId/parentNodeId/rootRunId/depth/callChain,
  run-start/run-end 事件携带;NODE_TIMEOUT_MS 统一 5 分钟
- 取消传播:cancelAbort + onCancel 钩子(父取消递归取消子 run);
  重试退避改为可中断等待;_executeNode 支持 releaseSlot 与
  queued 后取消防重复扣减;空图主动收尾
- registerKind(subworkflow):execute 委托宿主注入的 runChildWorkflow,
  lint/templateLintFields 接入节点校验与模板静态检查

编排器(index.js):
- lib/subworkflow.js:输入映射($ref 纯引用/模板字符串)、循环/
  深度(3)/预算(16 子 run)防护、selectWorkflowResult 结果选择、
  validateSubworkflowInputs(复用 workflow-inputs.js 统一校验)
- runChildWorkflow:lint → 防护 → 输入映射 → startRun(source=
  'subworkflow',suppressNotifications)→ 同步等待;双向取消传播
- 删除守卫:被 subworkflow 节点引用的工作流不可删(助手工具与
  HTTP /workflows/detail DELETE 均拦截,409 subworkflow-referenced)
- /runs、workflow_runs、/runs/detail 附 parent 元数据与 children
  (listChildRuns 按开始时间正序);含子工作流节点的运行禁续跑/重放

前端(web):
- registry 注册 subworkflow 节点(图标/预设/徽标)
- NodePanel:目标工作流选择(列表选择或手填 ID)+ 输入映射编辑
  (triggerInput 模板 + runInputs JSON)
- ResultPanel:子工作流运行区(children 列表、点开子 run 详情)
- App 拉取 /workflows 供 NodePanel 选择器使用

验证:orchestrator 18 套(含 subworkflow.test.mjs 新套、engine 新增
6 例、sqlite-store/assistant/workflow-tools 集成扩展)+ web 全套 +
全量 npm test 通过;build-web.sh 双 base 构建通过。
@chumingjun
chumingjun merged commit 20580d2 into main Sep 2, 2026
1 check passed
@chumingjun
chumingjun deleted the feat/subworkflow branch September 2, 2026 17:25
chumingjun added a commit that referenced this pull request Sep 3, 2026
背景:渠道/模型/思考级别已有设置面板默认值层(#117/#125),但 agent 节点
的两个超时旋钮(节点总超时 500s / 模型单请求超时 300s)只能逐节点配置。
另发现 #122 意外把 engine NODE_TIMEOUT_MS 从 500s 回退成 300s(#114 语义
是 500s),UI placeholder 与引擎实际值不符。

方案(解析链与模型层同构:节点 data > WF1 默认值 > 内置默认):
- agent-defaults:新增 nodeTimeoutSec/modelTimeoutSec(正整数秒,0=不设置,
  上限 86400);resolveAgentTimeouts 统一解析,导出 500/300 内置默认
- 引擎:engine.js 加 resolveNodeTimeout 注入位(仅 agent 节点吃 WF1 默认值,
  其余节点维持内置 300s);runAgentNode 单请求超时、单节点测试入口同步走链
- API:agent-defaults GET/PUT 回包 defaults/effective 带超时字段;
  llm-config 的 wf1Defaults 自动同源下发;旧配置文件读回自动补 0
- 设置面板:AgentDefaultsCard 加「节点超时(秒)/单次超时(秒)」两个输入框,
  前端预校验非法值
- NodePanel:agent 面板超时 placeholder 如实展示实际生效秒数(WF1 默认值
  或内置);容错区占位按节点类型区分(agent 500 / 其他 300),上限放宽到
  86400

验证:orchestrator 17+ 套、web 15 套、全仓 npm test 绿;build-web.sh 双构建
+ canvasui 防漂移通过;实机 dsh 验证 PUT 900/180 落盘、llm-config 下发、
非法值 400、旧文件兼容读回。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant